我正在尝试使用以下代码从yahoo finance中读取历史CSV数据:
import datetime
import time
from bs4 import BeautifulSoup
per1 = str(int(time.mktime((datetime.datetime.today() - td(days=365)).timetuple())))
per2 = str(int(time.mktime((datetime.datetime.today()).timetuple())))
url = 'https://query1.finance.yahoo.com/v7/finance/download/MSFT?period1=' + per1 + '&period2=' + per2 + '&interval=1d&events=history&crumb=OQg/YFV3fvh'
当你去雅虎财经,输入代码并将鼠标悬停在“下载数据”按钮上时,可以看到url
变量。
我收到身份验证错误,我认为这是由于缺少Cookie而导致我尝试以下操作:
import requests
ses = requests.Session()
url1 = 'https://finance.yahoo.com/quote/MSFT/history?p=MSFT'
ses.get(url1)
soup = BeautifulSoup(ses.get(url).content)
print soup.prettify()
这次我的Cookie错误不正确。
有人可以建议如何解决这个问题吗?
答案 0 :(得分:2)
查询字符串的crumb
参数会随着每个浏览器会话而不断变化。因此,当您从浏览器复制其值,关闭它然后在另一个浏览器实例中使用它时,它将在那时过期。
因此,当您在requests
会话中使用它时,它不会识别cookie值并产生错误,这一点不足为奇。
第1步
在任何浏览器中学习网络标签都会有所帮助。在这种特殊情况下,当您单击主页面中的滚动条时,可能会生成此crumb
部分。因此,您必须先获取该网址。
tickers = ('000001.SS', 'NKE', 'MA', 'SBUX')
url = 'https://finance.yahoo.com/quote/{0}?p={0}'.format(tickers[0])
r = s.get(url, headers = req_headers)
此网址只需提取一次。因此,您使用哪个股票代码并不重要。
第2步
服务器返回的响应包含下载CSV文件时传递给查询字符串中crumb
参数的值。
但是,它包含在上一个请求返回的页面的script
标记中。这意味着您无法单独使用BeautifulSoup来提取crumb
值。
我最初尝试re
从script
标记的文本中提取出来。但由于某种原因,我无法做到。所以我转移到json
进行解析。
soup = BeautifulSoup(r.content, 'lxml')
script_tag = soup.find(text=re.compile('crumb'))
response_dict = json.loads(script_tag[script_tag.find('{"context":'):script_tag.find('}}}};') + 4])
crumb = response_dict['context']['dispatcher']['stores']['CrumbStore']['crumb']
请注意,BeautifulSoup需要提取script
元素的内容,以便稍后传递给json
以将其解析为Python dict
对象。
我必须使用pprint
将生成的dict
打印到文件中,以确切了解存储碎片值的位置。
第3步
获取CSV文件的最终URL如下所示:
for ticker in tickers:
csv_url = 'https://query1.finance.yahoo.com/v7/finance/download/{0}?period1=1506656676&period2=1509248676&interval=1d&events=history&crumb={1}'.format(ticker, crumb)
r = s.get(csv_url, headers = req_headers)
<强>结果强>
这是下载文件的前几行:
Date,Open,High,Low,Close,Adj Close,Volume
2017-09-29,3340.311035,3357.014893,3340.311035,3348.943115,3348.943115,144900
2017-10-09,3403.246094,3410.169922,3366.965088,3374.377930,3374.377930,191700
2017-10-10,3373.344971,3384.025879,3358.794922,3382.988037,3382.988037,179400
注意:强>
我在两个请求中都使用了适当的标头。因此,如果您跳过该部分并且没有获得所需的结果,您可能也必须包含它们。
req_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.8',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
}