使用BeautifulSoup爬取含JavaScript的网页

3

大家好!我再次向你们求助。我可以使用标签来爬取简单的网站,但最近遇到了一个相当复杂的网站,其中包含JavaScript。因此,我想以表格(CSV)格式获取页面底部的所有估计值,如“用户”,“收入估计”和“每股收益估计”。

我希望自己能够解决它,但失败了。

这是我的代码:

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'))

输出格式很奇怪,我不知道如何以适当的形式提取数据。任何帮助都将不胜感激!

1
使用Selenium来爬取带有JavaScript的网页。 - Wonka
2个回答

3

看起来你试图提取的数据在数据模型中,这意味着它是JSON格式的。如果你使用以下方法进行简单的解析:

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}

DataModel.parse是一个JavaScript方法,以括号和冒号结尾。该函数的参数是您想要的JSON对象。通过将其加载到json.loads中,您可以像访问字典一样访问它。
然后,您可以将数据重新映射为CSV所需的格式。

2
这是我使用上述一些技巧解决问题的方法:

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"]])

你好,我想问一下这个答案还适用吗?我正在尝试使用类似结构的代码,但是当执行到 data_string = soup.split(insert my split condition here)[1] 时出现错误,因为 soup 变量返回的是类似字节的对象,而 Python 希望通过类似字节的对象进行拆分,而不是字符串。 - experiment unit 1998X
我使用了str()函数将soup字节对象转换为字符串,以便于解析。现在一切都正常工作了,但这可能不是正确的方法? - experiment unit 1998X

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接