来自雅虎的python lxml etree applet信息

时间:2016-08-17 22:29:30

标签: python python-3.x web-scraping lxml

雅虎财经更新了他们的网站。我有一个用于提取分析师建议的lxml / etree脚本。然而,现在,分析师的建议在那里,但仅作为图形。您可以在this page上看到示例。右栏中名为“建议趋势”的图表显示了分析报告的数量,显示强买入,买入,持有,表现不佳和卖出。

我的猜测是雅虎会在接下来的一段时间内对页面进行一些调整,但它让我想知道这些数据是否可以以任何合理的方式提取?

  1. 我的意思是,有没有办法让图形与之配合使用?
  2. 即使一个人成功了,是否有合理的方法从图形中提取数据?
  3. 我过去常常得到这样的来源:

    url = 'https://finance.yahoo.com/quote/'+code+'/analyst?p='+code
    tree = etree.HTML(urllib.request.urlopen(url).read())
    

    然后在html树中找到数据。但显然现在不可能。

2 个答案:

答案 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
          }]
        }
    }]
  }
}

如何查找数据

我所做的大致是:

  1. 在目标小部件中查找一些唯一标记(例如图表值或趋势字符串)
  2. 页面的开源(使用HTML和JS的某些格式化程序,例如this
  3. 在那里查找令牌(在第3页是以/* -- Data -- */开头的部分)
  4. 搜索“.js”以获取脚本标记(或程序包含,例如require.js)并在那里查找令牌
  5. 在Firebug或Chromium Developer Tools中打开网络选项卡并检查XHR请求
  6. 然后使用Postman(如果您更喜欢终端,则使用curl)去除额外参数并查看端点如何反应