with tag scraping example code python html-parsing beautifulsoup headless-browser

tag - scrape with selenium python



¿Cómo extraer un objeto JSON que se definió en una página HTML javascript block usando Python? (3)

Algo como esto puede funcionar:

import re HTML = """ <html> <head> ... <script type= "text/javascript"> window.blog.data = {"activity": {"type":"read"} }; ... </script> </head> <body> ... </body> </html> """ JSON = re.compile(''window.blog.data = ({.*?});'', re.DOTALL) matches = JSON.search(HTML) print matches.group(1)

Estoy descargando páginas HTML que tienen datos definidos en ellas de la siguiente manera:

... <script type= "text/javascript"> window.blog.data = {"activity":{"type":"read"}}; </script> ...

Me gustaría extraer el objeto JSON definido en ''window.blog.data''. ¿Hay una manera más simple que analizarla manualmente? (Estoy buscando en Beautiful Soap, pero no puedo encontrar un método que devuelva el objeto exacto sin analizar)

Gracias

Editar: ¿Sería posible y más correcto hacer esto con un navegador sin cabeza de python (por ejemplo, Ghost.py)?


Tuve un problema similar y terminé usando selenio con phantomjs. Es un poco hacky y no pude entender el método correcto de esperar hasta el método, pero la espera implícita parece funcionar bien hasta ahora para mí.

from selenium import webdriver import json import re url = "http..." driver = webdriver.PhantomJS(service_args=[''--load-images=no'']) driver.set_window_size(1120, 550) driver.get(url) driver.implicitly_wait(1) script_text = re.search(r''window/.blog/.data/s*=.*<//script>'', driver.page_source).group(0) # split text based on first equal sign and remove trailing script tag and semicolon json_text = script_text.split(''='',1)[1].rstrip(''</script>'').strip().rstrip('';'').strip() # only care about first piece of json json_text = json_text.split("};")[0] + "}" data = json.loads(json_text) driver.quit()

`` `


BeautifulSoup es un analizador html; también necesita un analizador de JavaScript aquí. Por cierto, algunos literales de objetos javascript no son válidos json (aunque en su ejemplo el literal también es un objeto json válido).

En casos simples, usted podría:

  1. Extrae el texto de <script> usando un analizador html
  2. asume que window.blog... es una sola línea o que no hay '';'' dentro del objeto y extrae el literal del objeto javascript usando manipulaciones simples de cadenas o una expresión regular
  3. asumir que la cadena es un JSON válido y analizarlo utilizando el módulo json

Ejemplo:

#!/usr/bin/env python html = """<!doctype html> <title>extract javascript object as json</title> <script> // .. window.blog.data = {"activity":{"type":"read"}}; // .. </script> <p>some other html here """ import json import re from bs4 import BeautifulSoup # $ pip install beautifulsoup4 soup = BeautifulSoup(html) script = soup.find(''script'', text=re.compile(''window/.blog/.data'')) json_text = re.search(r''^/s*window/.blog/.data/s*=/s*({.*?})/s*;/s*$'', script.string, flags=re.DOTALL | re.MULTILINE).group(1) data = json.loads(json_text) assert data[''activity''][''type''] == ''read''

Si las suposiciones son incorrectas, el código falla.

Para relajar la segunda suposición, se podría usar un analizador de JavaScript en lugar de una expresión regular, por ejemplo, slimit ( sugerido por @approximatenumber ):

from slimit import ast # $ pip install slimit from slimit.parser import Parser as JavascriptParser from slimit.visitors import nodevisitor soup = BeautifulSoup(html, ''html.parser'') tree = JavascriptParser().parse(soup.script.string) obj = next(node.right for node in nodevisitor.visit(tree) if (isinstance(node, ast.Assign) and node.left.to_ecma() == ''window.blog.data'')) # HACK: easy way to parse the javascript object literal data = json.loads(obj.to_ecma()) # NOTE: json format may be slightly different assert data[''activity''][''type''] == ''read''

No es necesario tratar el objeto literal ( obj ) como un objeto json. Para obtener la información necesaria, obj se puede visitar recursivamente como otros nodos AS. Permitiría admitir código javascript arbitrario (que puede ser analizado por slimit ).