如何用Python抓取动态生成URL的页面?

14 投票
2 回答
7860 浏览
提问于 2025-04-18 04:17

我正在尝试从这个网站 http://www.dailyfinance.com/quote/NYSE/international-business-machines/IBM/financial-ratios 获取数据,但传统的构建网址的方法不管用,因为网址中包含了“完整公司名称”,而我并不知道这个完整的公司名称是什么。我只知道公司的符号是“IBM”。

简单来说,我获取数据的方式是通过一个公司符号的数组来循环,然后在发送请求之前构建网址字符串。但是在这种情况下,这种方法行不通。

比如,CSCO的字符串是:

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

另一个例子是AAPL的网址字符串:

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

所以为了获取网址,我必须在主页的输入框中搜索这个符号:

http://www.dailyfinance.com/

我注意到,当我在Firefox的开发者工具网络标签中输入“CSCO”并检查搜索输入时,发现发送的请求是:

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

抱歉解释得这么长。那么问题是,我该如何从Referer中提取网址?如果这不可能,我应该如何解决这个问题?还有其他方法吗?

非常感谢你的帮助。

2 个回答

2

这段内容虽然没有直接回答你的具体问题,但可以帮助你解决问题。

你可以使用这个链接格式:http://www.dailyfinance.com/quotes/{公司代码}/{股票交易所}

下面是一些例子:

http://www.dailyfinance.com/quotes/AAPL/NAS(苹果公司的股票)

http://www.dailyfinance.com/quotes/IBM/NYSE(IBM公司的股票)

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

我很喜欢这个问题。为了回答它,我会给出一个非常详细的解答。我会使用我最喜欢的 Requests 库和 BeautifulSoup4。如果你真的想用 Mechanize,那就看你自己了。不过,使用 Requests 会让你省去很多麻烦。


首先,你可能在寻找一个 POST 请求。然而,如果搜索功能能直接带你到你想要的页面,通常就不需要 POST 请求了。那么我们来看看吧?

当我打开基本网址 http://www.dailyfinance.com/ 时,我可以通过 Firebug 或 Chrome 的检查工具简单检查一下。当我在搜索框中输入 CSCO 或 AAPL 并启用“跳转”时,会出现一个 301 Moved Permanently 的状态码。这是什么意思呢?

在这里输入图片描述

简单来说,我被转移到了其他地方。这个 GET 请求的 URL 是:

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

现在,我们通过简单的 URL 操作来测试一下 AAPL 是否有效。

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 变了吗?我们再进一步,通过在上面的代码后面加上 /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 不存在!天哪,为什么我还看到以下内容呢?!

在这里输入图片描述

只能有一个解释:这个表格是动态加载的!真糟糕。让我们看看能否找到这个表格的其他来源。我仔细观察页面,发现底部有滚动条。这可能意味着这个表格是在一个框架内加载的,或者是直接从其他来源加载并放入了页面的一个 div 中。

我刷新页面,再次查看 GET 请求。太好了,我发现了一些看起来有希望的东西:

在这里输入图片描述

这是一个第三方来源的 URL,看看,它可以很容易地通过股票代码进行操作!让我们试着在新标签页中加载它。结果如下:

在这里输入图片描述

哇!我们现在得到了数据的确切来源。不过最后一个障碍是,当我们尝试使用这个字符串提取 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 调用等方式生成的,我们可能会在处理上遇到一些困难。

希望你从中学到了一些东西。如果这对你有帮助,请告诉我们,祝你好运。

撰写回答