Как очистить страницы с динамически сгенерированными URL-адресами с помощью Python?

Я пытаюсь очистить http://www.dailyfinance.com/quote/NYSE/international-business-machines/IBM/financial-ratios, но традиционная технология построения строковых строк не работает, потому что "полное название компании" -is-insert-in-path ". И точное "полное название компании" не известно заранее. Известен только символ компании "IBM".

По сути, способ, которым я царапаю, - это перебрать массив символа компании и построить строку url перед отправкой ее на urllib2.urlopen(url). Но в этом случае это невозможно.

Например, строка CSCO

http://www.dailyfinance.com/quote/NASDAQ/cisco-systems-inc/CSCO/financial-ratios

и другой пример строки url - AAPL:

http://www.dailyfinance.com/quote/NASDAQ/apple/AAPL/financial-ratios

Поэтому, чтобы получить URL-адрес, мне пришлось искать символ в поле ввода на главной странице:

http://www.dailyfinance.com/

Я заметил, что когда я печатаю "CSCO" и просматриваю вход для поиска по адресу (http://www.dailyfinance.com/quote/NASDAQ/apple/AAPL/financial-ratios) на вкладке сети веб-разработчиков Firefox, я заметил, что запрос на отправку

http://j.foolcdn.com/tmf/predictivesearch?callback=_predictiveSearch_csco&term=csco&domain=dailyfinance.com

и что референт действительно дает путь, который я хочу захватить

Host: j.foolcdn.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://www.dailyfinance.com/quote/NASDAQ/cisco-systems-inc/CSCO/financial-ratios?source=itxwebtxt0000007
Connection: keep-alive

Извините за длинное объяснение. Итак, вопрос в том, как извлечь URL-адрес в Referer? Если это невозможно, как мне подойти к этой проблеме? Есть ли другой способ?

Я действительно ценю твою помощь.

Ответ 1

Мне нравится этот вопрос. И из-за этого я дам очень тщательный ответ. Для этого я буду использовать мою любимую библиотеку Requests вместе с BeautifulSoup4. Портирование на Механизацию, если вы действительно хотите использовать это, зависит от вас. Тем не менее, запросы помогут вам сэкономить массу головных болей.


Во-первых, вы, вероятно, ищете POST-запрос. Однако запросы POST часто не нужны, если функция поиска сразу же отправляет вас на страницу, которую вы ищете. Так что посмотрим, не так ли?

Когда я приземляюсь по базовому URL-адресу, http://www.dailyfinance.com/, я могу выполнить простую проверку через Firebug или инструмент проверки Chrome, который, когда я помещаю CSCO или AAPL в строку поиска и включаю "прыжок", есть 301 Moved Permanently код статуса 301 Moved Permanently статуса. Что это значит?

enter image description here

Проще говоря, меня куда-то перевели. URL-адрес этого запроса GET следующий:

http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input=CSCO

Теперь мы проверяем, работает ли он с AAPL, используя простую манипуляцию URL.

import requests as rq

apl_tick = "AAPL"
url = "http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input="
r = rq.get(url + apl_tick)
print r.url

Вышеприведенное дает следующий результат:

http://www.dailyfinance.com/quote/nasdaq/apple/aapl
[Finished in 2.3s]

Посмотрите, как изменился URL-адрес ответа? Давайте сделаем URL-манипуляцию на один шаг дальше, ища страницу /financial-ratios, добавив ниже приведенный выше код:

new_url = r.url + "/financial-ratios"
p = rq.get(new_url)
print p.url

При запуске это дает следующий результат:

http://www.dailyfinance.com/quote/nasdaq/apple/aapl
http://www.dailyfinance.com/quote/nasdaq/apple/aapl/financial-ratios
[Finished in 6.0s]

Теперь мы на правильном пути. Теперь я попытаюсь проанализировать данные с помощью BeautifulSoup. Мой полный код выглядит следующим образом:

from bs4 import BeautifulSoup as bsoup
import requests as rq

apl_tick = "AAPL"
url = "http://www.dailyfinance.com/quote/jump?exchange-input=&ticker-input="
r = rq.get(url + apl_tick)
new_url = r.url + "/financial-ratios"
p = rq.get(new_url)

soup = bsoup(p.content)
div = soup.find("div", id="clear").table
rows = table.find_all("tr")
for row in rows:
    print row

Затем я пытаюсь запустить этот код, только чтобы встретить ошибку со следующей трассировкой:

  File "C:\Users\nanashi\Desktop\test.py", line 13, in <module>
    div = soup.find("div", id="clear").table
AttributeError: 'NoneType' object has no attribute 'table'

Следует отметить, что 'NoneType' object... Это означает, что наш целевой div не существует! Эгады, но почему я вижу следующее?!

enter image description here

Может быть только одно объяснение: таблица загружается динамически! Крысы. Посмотрим, можем ли мы найти другой источник для таблицы. Я изучаю эту страницу и вижу, что внизу есть полосы прокрутки. Это может означать, что таблица была загружена внутри фрейма или была загружена прямо из другого источника целиком и помещена в div на странице.

Я обновляю страницу и снова смотрю запросы GET. Бинго, я нашел кое-что, что кажется немногообещающим:

enter image description here

URL-адрес стороннего источника, и посмотрите, он легко манипулируется с помощью символа тикера! Попробуйте загрузить его на новую вкладку. Вот что мы получаем:

enter image description here

ВАУ! Теперь у нас есть очень точный источник наших данных. Последнее препятствие, хотя это будет работать, когда мы попытаемся вытащить данные CSCO с помощью этой строки (помните, что мы пошли CSCO → AAPL и теперь снова возвращаемся к CSCO, так что вы не путаете). Позвольте очистить струну и отбросить роль www.dailyfinance.com здесь полностью. Наш новый URL-адрес выглядит следующим образом:

http://www.motleyfool.idmanagedsolutions.com/stocks/financial_ratios.idms?SYMBOL_US=AAPL

Попробуй использовать это в нашем последнем скребке!

from bs4 import BeautifulSoup as bsoup
import requests as rq

csco_tick = "CSCO"
url = "http://www.motleyfool.idmanagedsolutions.com/stocks/financial_ratios.idms?SYMBOL_US="
new_url = url + csco_tick

r = rq.get(new_url)
soup = bsoup(r.content)

table = soup.find("div", id="clear").table
rows = table.find_all("tr")
for row in rows:
    print row.get_text()

И наши исходные результаты для данных финансовых коэффициентов CSCO:

Company
Industry


Valuation Ratios


P/E Ratio (TTM)
15.40
14.80


P/E High - Last 5 Yrs 
24.00
28.90


P/E Low - Last 5 Yrs
8.40
12.10


Beta
1.37
1.50


Price to Sales (TTM)
2.51
2.59


Price to Book (MRQ)
2.14
2.17


Price to Tangible Book (MRQ)
4.25
3.83


Price to Cash Flow (TTM)
11.40
11.60


Price to Free Cash Flow (TTM)
28.20
60.20


Dividends


Dividend Yield (%)
3.30
2.50


Dividend Yield - 5 Yr Avg (%)
N.A.
1.20


Dividend 5 Yr Growth Rate (%)
N.A.
144.07


Payout Ratio (TTM)
45.00
32.00


Sales (MRQ) vs Qtr 1 Yr Ago (%)
-7.80
-3.70


Sales (TTM) vs TTM 1 Yr Ago (%)
5.50
5.60


Growth Rates (%)


Sales - 5 Yr Growth Rate (%)
5.51
5.12


EPS (MRQ) vs Qtr 1 Yr Ago (%)
-54.50
-51.90


EPS (TTM) vs TTM 1 Yr Ago (%)
-54.50
-51.90


EPS - 5 Yr Growth Rate (%)
8.91
9.04


Capital Spending - 5 Yr Growth Rate (%)
20.30
20.94


Financial Strength


Quick Ratio (MRQ)
2.40
2.70


Current Ratio (MRQ)
2.60
2.90


LT Debt to Equity (MRQ)
0.22
0.20


Total Debt to Equity (MRQ)
0.31
0.25


Interest Coverage (TTM)
18.90
19.10


Profitability Ratios (%)


Gross Margin (TTM)
63.20
62.50


Gross Margin - 5 Yr Avg
66.30
64.00


EBITD Margin (TTM)
26.20
25.00


EBITD - 5 Yr Avg
28.82
0.00


Pre-Tax Margin (TTM)
21.10
20.00


Pre-Tax Margin - 5 Yr Avg
21.60
18.80


Management Effectiveness (%)


Net Profit Margin (TTM)
17.10
17.65


Net Profit Margin - 5 Yr Avg
17.90
15.40


Return on Assets (TTM)
8.30
8.90


Return on Assets - 5 Yr Avg
8.90
8.00


Return on Investment (TTM)
11.90
12.30


Return on Investment - 5 Yr Avg
12.50
10.90


Efficiency


Revenue/Employee (TTM)
637,890.00
556,027.00


Net Income/Employee (TTM)
108,902.00
98,118.00


Receivable Turnover (TTM)
5.70
5.80


Inventory Turnover (TTM)
11.30
9.70


Asset Turnover (TTM)
0.50
0.50

[Finished in 2.0s]

Очистка данных зависит от вас.


Один хороший урок, который можно извлечь из этой царапины, - это не все данные, содержащиеся только на одной странице. Приятно видеть, что это происходит с другого статического сайта. Если бы это было вызвано вызовами JavaScript или AJAX или тому подобное, у нас, вероятно, были бы некоторые трудности с нашим подходом.

Надеюсь, вы узнали кое-что из этого. Сообщите нам, если это поможет и удачи.

Ответ 2

Не отвечает на ваш конкретный вопрос, но решает вашу проблему.

http://www.dailyfinance.com/quotes/{Company Symbol}/{Stock Exchange}

Примеры:

http://www.dailyfinance.com/quotes/AAPL/NAS

http://www.dailyfinance.com/quotes/IBM/NYSE

http://www.dailyfinance.com/quotes/CSCO/NAS

Чтобы перейти на страницу финансовых коэффициентов, вы можете использовать что-то вроде этого:

import urllib2

def financial_ratio_url(symbol, stock_exchange):
    starturl  = 'http://www.dailyfinance.com/quotes/'
    starturl += '/'.join([symbol, stock_exchange])
    req = urllib2.Request(starturl)
    res = urllib2.urlopen(starturl)
    return '/'.join([res.geturl(),'financial-ratios'])

Пример:

financial_ratio_url('AAPL', 'NAS')
'http://www.dailyfinance.com/quote/nasdaq/apple/aapl/financial-ratios'