使用Requests解析Yahoo财经页面时日期不一致的技术问询
我碰到过类似的问题,Yahoo财经这里的日期差异是因为JSON里的endDate存储的是UTC标准时间,而页面上显示的是公司财报对应的本地时区日期——GTX的财报结束日当地时间是2019-06-29,但转成UTC就变成了次日的0点,所以JSON里的日期是2019-06-30。而AAPL的财报结束日刚好在UTC和本地时区是同一天,所以没有差异,这也是为什么直接加减天数不可行的原因。
不想用Selenium的话,有两个靠谱的方案:
方案1:解析页面HTML中的显示日期
既然页面上已经渲染出了正确的日期,我们可以直接从requests获取的HTML源码里提取它。Yahoo的财报页面里,季度日期会出现在表格的第一行(对应你取的第一个总营收数据),可以用正则定位这个元素:
修改你的脚本如下:
import re import json import requests url = 'https://finance.yahoo.com/quote/GTX/financials?p=GTX' res = requests.get(url) # 解析总营收(保留原逻辑) data = re.findall(r'root.App.main[^{]+(.*);', res.text)[0] jsoncontent = json.loads(data) container = jsoncontent['context']['dispatcher']['stores']['QuoteSummaryStore']['incomeStatementHistoryQuarterly']['incomeStatementHistory'] total_revenue = container[0]['totalRevenue']['raw'] # 解析页面显示的日期 date_pattern = r'<div class="D\(tbr\) fi-row Bgc\(\$hoverBgColor\):h" data-test="fin-row">.*?<span class="Ta\(c\) Py\(6px\) Bxz\(bb\) BdB Bdc\(\$seperatorColor\) Miw\(120px\) Miw\(100px\)--pnclg D\(tbc\)">(\d{4}-\d{2}-\d{2})</span>' page_dates = re.findall(date_pattern, res.text, re.DOTALL) if page_dates: concerning_date = page_dates[0] else: # 解析失败时回退到原日期 concerning_date = container[0]['endDate']['fmt'] print(total_revenue, concerning_date)
这个方案的优点是直接拿到页面显示的结果,不需要处理时区转换,兼容性更好——不管股票对应的时区是什么,都能准确获取页面展示的日期。
方案2:将UTC时间戳转换为公司本地时区日期
如果你想完全依赖JSON数据处理,可以把endDate的时间戳(raw字段)转换为公司所在时区的日期。Yahoo的JSON数据里已经包含了公司的时区信息,我们可以利用这一点:
import re import json import requests from datetime import datetime import pytz url = 'https://finance.yahoo.com/quote/GTX/financials?p=GTX' res = requests.get(url) data = re.findall(r'root.App.main[^{]+(.*);', res.text)[0] jsoncontent = json.loads(data) # 获取总营收 container = jsoncontent['context']['dispatcher']['stores']['QuoteSummaryStore']['incomeStatementHistoryQuarterly']['incomeStatementHistory'] total_revenue = container[0]['totalRevenue']['raw'] # 获取公司时区 timezone_str = jsoncontent['context']['dispatcher']['stores']['QuoteSummaryStore']['summaryProfile']['timeZone']['timeZoneId'] company_tz = pytz.timezone(timezone_str) # 转换UTC时间戳为本地时区日期 utc_timestamp = container[0]['endDate']['raw'] utc_dt = datetime.utcfromtimestamp(utc_timestamp).replace(tzinfo=pytz.utc) local_dt = utc_dt.astimezone(company_tz) concerning_date = local_dt.strftime('%Y-%m-%d') print(total_revenue, concerning_date)
这个方案需要先安装pytz库(执行pip install pytz),优点是完全依赖结构化的JSON数据,稳定性更高(只要Yahoo不修改时区字段的存储位置)。
需要注意的是,GTX的时区是美国东部时区(America/New_York),UTC时间2019-06-30 00:00转换为该时区后是2019-06-29 20:00,取日期部分就是页面显示的2019-06-29,完美匹配预期结果。
内容的提问来源于stack exchange,提问作者MITHU

