Raspando una página web que tiene JavaScript con BeautifulSoup

chicos! Te estoy aplicando una vez más. Estoy de acuerdo con raspar sitios web simples con tags, pero recientemente he encontrado un sitio web bastante complejo que tiene JavaScript. Como resultado, me gustaría obtener todas las estimaciones en la parte inferior de la página en un formato de tabla (csv). Como ‘Usuario’, ‘Estimación de ingresos’, ‘Estimación de EPS’.

Esperaba imaginármelo por mi cuenta, pero fracasé un poco.

Aquí está mi código:

from urllib import urlopen from bs4 import BeautifulSoup html = urlopen("https://www.estimize.com/jpm/fq3-2016?sort=rank&direction=asc&estimates_per_page=142&show_confirm=false") soup = BeautifulSoup(html.read(), "html.parser") print(soup.findAll('script')[11].string.encode('utf8')) 

La salida tiene un formato extraño y no sé cómo extraer los datos de forma adecuada. ¡Apreciaré cualquier ayuda!

Parece que los datos que intentas extraer están en un modelo de datos, lo que significa que están en JSON. Si realiza una pequeña cantidad de análisis con lo siguiente:

 import json import re data_string = soup.findAll('script')[11].string.encode('utf8') data_string = data_string.split("DataModel.parse(")[1] data_string = data_string.split(");")[0] // parse out erroneous html while re.search('\<[^\>]*\>', datastring): data_string = ''.join(datastring.split(re.search('\<[^\>]*\>', datastring).group(0))) // parse out other function parameters, leaving you with the json data_you_want = json.loads(data_string.split(re.search('\}[^",\}\]]+,', data_string).group(0))[0]+'}') print(data_you_want["estimate"]) >>> {'shares': {'shares_hash': {'twitter': None, 'stocktwits': None, 'linkedin': None}}, 'lastRevised': None, 'id': None, 'revenue_points': None, 'sector': 'financials', 'persisted': False, 'points': None, 'instrumentSlug': 'jpm', 'wallstreetRevenue': 23972, 'revenue': 23972, 'createdAt': None, 'username': None, 'isBlind': False, 'releaseSlug': 'fq3-2016', 'statement': '', 'errorRanges': {'revenue': {'low': 21247.3532016398, 'high': 26820.423240734}, 'eps': {'low': 1.02460526459765, 'high': 1.81359679579922}}, 'eps_points': None, 'rank': None, 'instrumentId': 981, 'eps': 1.4, 'season': '2016-fall', 'releaseId': 52773} 

El DataModel.parse es un método javascript que significa que termina con un paréntesis y dos puntos. el parámetro para la función es el objeto JSON que desea. Al cargarlo en json.loads, puedes acceder a él como si fuera un diccionario.

A partir de ahí, reasigna los datos al formulario en el que deseas que esté el csv.

Así es como resolví el problema, usando algunos consejos anteriores:

 from bs4 import BeautifulSoup from urllib import urlopen import json import csv f = csv.writer(open("estimize.csv", "a")) f.writerow(["User Name", "Revenue Estimate", "EPS Estimate"]) html = "https://www.estimize.com/jpm/fq3-2016?sort=rank&direction=asc&estimates_per_page=142&show_confirm=false" html = urlopen(html) soup = BeautifulSoup(html.read(), "html.parser").encode('utf8') data_string = soup.split("\"allEstimateRows\":")[1] data_string = data_string.split(",\"tableSortDirection")[0] data = json.loads(data_string) for item in data: f.writerow([item["userName"], item["revenue"], item["eps"]])