雅虎财经更新了他们的网站。我有一个用于提取分析师建议的lxml / etree脚本。然而,现在,分析师的建议在那里,但仅作为图形。您可以在this page上看到示例。右栏中名为“建议趋势”的图表显示了分析报告的数量,显示强买入,买入,持有,表现不佳和卖出。
我的猜测是雅虎会在接下来的一段时间内对页面进行一些调整,但它让我想知道这些数据是否可以以任何合理的方式提取?
我过去常常得到这样的来源:
url = 'https://finance.yahoo.com/quote/'+code+'/analyst?p='+code
tree = etree.HTML(urllib.request.urlopen(url).read())
然后在html树中找到数据。但显然现在不可能。
答案 0 :(得分:4)
页面非常动态并且涉及在浏览器中执行的大量JavaScript。要遵循@ Padraic关于切换到selenium
的建议,这里有一个完整的示例工作代码,最后会生成一个月到趋势的字典。每个条形的值计算为条形高度的比例:
from pprint import pprint
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://finance.yahoo.com/quote/CSX/analysts?p=CSX")
# wait for the chart to be visible
wait = WebDriverWait(driver, 10)
trends = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "section[data-reactid$=trends]")))
chart = trends.find_element_by_css_selector("svg.ratings-chart")
# get labels
month_names = [month.text for month in chart.find_elements_by_css_selector("g.x-axis g.tick")]
trend_names = [trend.text for trend in trends.find_elements_by_css_selector("table tr > td:nth-of-type(2)")]
# construct month-to-trend dictionary
data = {}
months = chart.find_elements_by_css_selector("g[transform]:not([class])")
for month_name, month_data in zip(month_names, months):
total = month_data.find_element_by_css_selector("text.total").text
data[month_name] = {'total': total}
bars = month_data.find_elements_by_css_selector("g.bar rect")
# let's calculate the values of bars as proportions of a bar height
heights = {trend_name: int(bar.get_attribute("height")) for trend_name, bar in zip(trend_names[::-1], bars)}
total_height = sum(heights.values())
for trend_name, bar in zip(trend_names, bars):
data[month_name][trend_name] = heights[trend_name] * 100 / total_height
driver.close()
pprint(data)
打印:
{u'Aug': {u'Buy': 19,
u'Hold': 45,
u'Sell': 3,
u'Strong Buy': 22,
u'Underperform': 8,
'total': u'26'},
u'Jul': {u'Buy': 18,
u'Hold': 44,
u'Sell': 3,
u'Strong Buy': 25,
u'Underperform': 7,
'total': u'27'},
u'Jun': {u'Buy': 21,
u'Hold': 38,
u'Sell': 3,
u'Strong Buy': 28,
u'Underperform': 7,
'total': u'28'},
u'May': {u'Buy': 21,
u'Hold': 38,
u'Sell': 3,
u'Strong Buy': 28,
u'Underperform': 7,
'total': u'28'}}
total
值是您在每个栏顶部看到的标签。
希望这对你来说至少是一个好的开始。如果您希望我详细说明代码的任何部分或需要任何其他信息,请与我们联系。
答案 1 :(得分:2)
正如评论所说他们已经转移到ReactJS,所以lxml
不再那么重要,因为HTML页面中没有数据。现在,您需要环顾四周,找到他们从中提取数据的端点。在推荐趋势的情况下,它就在那里。
#!/usr/bin/env python3
import json
from pprint import pprint
from urllib.request import urlopen
from urllib.parse import urlencode
def parse():
host = 'https://query2.finance.yahoo.com'
path = '/v10/finance/quoteSummary/CSX'
params = {
'formatted' : 'true',
'lang' : 'en-US',
'region' : 'US',
'modules' : 'recommendationTrend'
}
response = urlopen('{}{}?{}'.format(host, path, urlencode(params)))
data = json.loads(response.read().decode())
pprint(data)
if __name__ == '__main__':
parse()
输出看起来像这样。
{
'quoteSummary': {
'error': None,
'result': [{
'recommendationTrend': {
'maxAge': 86400,
'trend': [{
'buy': 0,
'hold': 0,
'period': '0w',
'sell': 0,
'strongBuy': 0,
'strongSell': 0
},
{
'buy': 0,
'hold': 0,
'period': '-1w',
'sell': 0,
'strongBuy': 0,
'strongSell': 0
},
{
'buy': 5,
'hold': 12,
'period': '0m',
'sell': 2,
'strongBuy': 6,
'strongSell': 1
},
{
'buy': 5,
'hold': 12,
'period': '-1m',
'sell': 2,
'strongBuy': 7,
'strongSell': 1
},
{
'buy': 6,
'hold': 11,
'period': '-2m',
'sell': 2,
'strongBuy': 8,
'strongSell': 1
},
{
'buy': 6,
'hold': 11,
'period': '-3m',
'sell': 2,
'strongBuy': 8,
'strongSell': 1
}]
}
}]
}
}
我所做的大致是: