2016-08-17 18 views
8

Yahoo finance zaktualizował swoją stronę. Miałem skrypt lxml/etree, który służy do wyodrębniania zaleceń analityka. Teraz jednak istnieją zalecenia analityków, ale tylko jako grafika. Możesz zobaczyć przykład na this page. Wykres o nazwie Trendy rekomendacji w kolumnie po prawej stronie pokazuje liczbę raportów analityków wykazujących silne kupno, kupno, wstrzymanie, niższe wyniki i sprzedaż.python lxml etree aplet informacje z Yahoo

Domyślam się, że yahoo dokona kilku poprawek na stronie przez najbliższy krótki czas, ale zastanawiałem się, czy dane te można wydobyć w jakikolwiek rozsądny sposób?

  1. Chodzi mi o to, czy istnieje sposób, aby uzyskać grafikę do pracy z tym?
  2. Nawet gdyby się udało, czy istnieje rozsądny sposób wyodrębnienia danych z grafiki?

kiedyś dostać źródło takiego:

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

a następnie znaleźć dane w drzewie html. Ale oczywiście to niemożliwe.

+0

Z pamięci zaczęli używać reagowania, więc wiele treści jest teraz dynamicznie tworzonych, w jaki sposób otrzymujesz źródło? –

+0

Umieściłem kod w pytaniu. Wygląda na to, że treść jest tworzona dynamicznie. Sądzę, że zastanawiam się, czy takie treści i tak można wyciągnąć. –

+0

tak, właśnie obejrzałem, jest całkowicie dynamicznie stworzony, w tym kolorowanki itp. Można dość łatwo uzyskać wartości przy użyciu selenu choć –

Odpowiedz

2

W komentarzach napisano, że przeniosły się do ReactJS, więc lxml nie jest już do rzeczy, ponieważ nie ma danych na stronie HTML. Teraz musisz rozejrzeć się i znaleźć punkt końcowy, z którego czerpią dane. W przypadku Trendów rekomendacji jest tam.

#!/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() 

Wynik wygląda następująco.

{ 
    '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 
      }] 
     } 
    }] 
    } 
} 

Jak szukać danych

co zrobiłem było z grubsza:

  1. Znajdź jakiś unikalny token w widgecie docelowej (słownie wartość wykres lub Trend string)
  2. Otwórz źródło strony (użyj jakiegoś formatyzatora dla HTML i JS, np. this)
  3. Poszukaj tokena tam (w e strona trzecia to sekcja zaczynająca się od /* -- Data -- */)
  4. Wyszukaj ".js", aby uzyskać znaczniki skryptu (lub programowe włączania, np. wymagać.js) i poszukać tokena tam
  5. Otwórz zakładkę sieć w Firebug lub chrom Developer Tools i skontrolować XHR żąda
  6. Następnie użyj Postman (lub curl jeśli wolisz terminalu) rozebrać dodatkowych parametrów i zobaczyć jak wygląda końcowy reaguje
+0

Zielona technologia w porównaniu do posiadania w pełni funkcjonalnej przeglądarki i zdalnego sterowania do niej ;-) – saaj

+0

Tak, bałem się, że ktoś wysyła bezpośrednie podejście. Zdecydowanie zasługuje na nagrodę! – alecxe

+0

To naprawdę działa dobrze. Zastanawiałem się, jak udało ci się ustalić właściwe rzeczy do wprowadzenia w parach? Ja tylko niejasno rozumiem, co robi, więc pomyślałem, że dobrze byłoby zrozumieć, w jaki sposób dostosowałbym kod do innych informacji (np. Cena docelowa lub cokolwiek innego). –

4

Strona jest bardzo dynamiczna i zawiera dużo javascript wykonanych w przeglądarce. Aby postępować zgodnie z poradą @ Padraic dotyczącą przejścia na selenium, tutaj znajduje się kompletny przykładowy kod roboczy, który generuje na końcu słownik miesiąca do trendu. Wartości każdego baru są obliczane jako proporcje wysokości kreskowych:

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) 

Drukuje:

{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'}} 

Wartości total są etykiety, które można zobaczyć na górze każdego baru.

Mam nadzieję, że to byłby dla ciebie dobry początek. Daj mi znać, jeśli chcesz, żebym omówił jakąkolwiek część kodu lub zażądał dodatkowych informacji.

Powiązane problemy