使用requests无法从网页中解析出精确结果

7
我已经用Python创建了一个脚本来解析网页中的两个字段 - 总收入和它相关的日期。 我需要的字段是JavaScript加密的。 它们在json数组中的页面源代码中可用。 以下脚本可以相应地解析这两个字段。
然而,问题是页面上可见的日期与页面源代码中可用的日期不同。 网页链接 该网页中的日期如this 页面源代码中的日期如this 显然有一天的差异。
访问那个网页后,当您点击此选项卡季度报告时,您可以在那里看到结果。
我已经尝试过:
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']
concerning_date = container[0]['endDate']['fmt']
print(total_revenue,concerning_date)

我得到的结果(收入以百万为单位):

802000000 2019-06-30

我想要得到的结果:

802000000 2019-06-29

当我使用这个股票代码 AAPL 时,我得到了确切的日期,所以减去或加上一天不是一个选项。如何从该网站获取确切的日期?顺便说一下,我知道如何使用 selenium 获取它们,所以我只想坚持使用 requests

3
如果日期相差不超过一天,我的第一个猜测是时间区转换导致的差异。 - Janne Karila
1
我尝试使用不同的位置激活VPN以查看结果,但变化仍然存在@Janne Karila。 - MITHU
我刚刚尝试了你的代码,print语句输出与页面上的相同(2019-6-30)。 - Jack Fleeting
是的,@Jack Fleeting,你说得对,但其他标记肯定会有所不同。实际上,您可能在其他时间得到不同的结果。目前看来,Janne Karila在他的第一个猜测中是正确的。我想可能有任何方法来解决这个问题。 - MITHU
请问您能否提供更多正确日期的股票代码以及错误日期的股票代码? - Life is complex
我并没有股票代码列表。生活是复杂的。我的做法是在***这个网站***右侧区域中选择任意一个可用的股票代码,然后尝试使用脚本检查结果。 - MITHU
3个回答

3

如评论所述,您需要将日期转换为适当的时区(东部标准时间),这可以使用datetime和dateutil来完成。

下面是一个可行的示例:

import re
import json
import requests
from datetime import datetime, timezone
from dateutil import tz

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

EST = tz.gettz('EST')
raw_date = datetime.fromtimestamp(container[0]['endDate']['raw'], tz=EST)
concerning_date = raw_date.date().strftime('%d-%m-%Y')
print(total_revenue, concerning_date)

这似乎是我期望得到的结果。我能否不将日期格式化为2019-06-29 19:00:00-05:00,而改为06-29-2019?谢谢。 - MITHU
1
@MITHU 我在更新中添加了这个。首先,你需要使用.date()将datetime对象转换为日期,然后使用.strftime将其转换为所需格式。 - Lord Elrond
2
如果我尝试使用这个股票代码 NVDA,将会跳转到这个***链接***,我发现差异甚至更大。 - MITHU
@MITHU 奇怪。这意味着TZ可能不是问题,因为NVIDIA也在EST时区。我会研究一个解决方案,但现在我对为什么会发生这种情况毫无头绪。 - Lord Elrond

2

本答案更新的部分概述了日期差异的根本原因。


原始答案


您的JSON中某些原始值是UNIX时间戳。

参考您的代码并进行修改:

concerning_date_fmt = container[0]['endDate']['fmt']
concerning_date_raw = container[0]['endDate']['raw']
print(f'{concerning_date} -- {concerning_date_raw}')
# output 
2019-07-28 -- 1564272000 

'endDate': {'fmt': '2019-07-28', 'raw': 1564272000}

1564272000是自1970年1月1日以来经过的秒数。这个日期是Unix纪元的开始,时间是协调世界时(UTC)。1564272000相当于:2019年7月28日上午12:00(UTC)。

您可以使用内置的Python函数将这些时间戳转换为标准的datetime格式。

from datetime import datetime
unix_timestamp = int('1548547200')

converted_timestamp = datetime.utcfromtimestamp(unix_timestamp).strftime('%Y-%m-%dT%H:%M:%SZ')
print (converted_timestamp)
# output Coordinated Universal Time (or UTC)
2019-07-28T00:00:00Z

reformatted_timestamp = datetime.strptime(converted_timestamp, '%Y-%m-%dT%H:%M:%SZ').strftime('%d-%m-%Y')
print (reformatted_timestamp)
# output
28-07-2019

这仍然没有解决与JSON日期和列日期有时不同的原始问题。但是,这是我目前关于发生日期差异的假设。
1. 从root.App.main中提取的json日期(fmt和raw)均为协调世界时(UTC)。这很明显,因为raw中有UNIX时间戳。 2. 在表格列中显示的日期似乎是东部标准时间(EST)时区的日期。EST目前是UTC-4。这意味着2019年7月28日22:00(晚上10点)EST将是2019年7月29日02:00(凌晨2点)UTC。根据traceroute结果,托管finance.yahoo.com的服务器位于美国。这些值也在JSON文件中: - 'exchangeTimezoneName': 'America/New_York' - 'exchangeTimezoneShortName': 'EDT' 3. 还有可能一些日期差异与该网站使用的基础React代码有关。这个问题更难诊断,因为代码不可见。 目前,我认为最好的解决方案是使用UNIX时间戳作为您的参考时间。可以使用此参考替换表格列的日期。 在JSON文件和列之间肯定进行了某种类型的转换。
NVIDIA JSON FILE:'endDate':{'raw':1561766400,'fmt':'2019-06-29'} NVIDIA Associated Total Revenue列:6/30/2019 但是Total Revenue列的日期应为6/28/2019(EDT),因为1561766400的UNIX时间戳是06/29/2019 12:00am(UTC)。
DELL的差异大于基本的UNIX时间戳和EDT时间戳转换。 DELL JSON FILE:{"raw":1564704000,"fmt":"2019-08-02"} DELL Associated Total Revenue列:7/31/2019 如果我们将UNIX时间戳转换为EDT时间戳,则结果将为8/1/2019,但这在DELL示例中不是这种情况,该日期为7/31/2019。雅虎代码库中的某些内容必须导致此差异。 我开始相信React可能是这些日期差异的罪魁祸首,但如果没有进行更多的研究,我无法确定。 如果React是根本原因,那么最好的选择是使用JSON数据中的日期元素。 更新的答案 2019年10月17日

这个问题非常有趣,因为这些列日期似乎与公司的官方财政季度结束日期有关,而不是日期转换问题。

以下是几个例子:

  • Apple Inc. (AAPL)
  • Atlassian Corporation Plc (TEAM)
  • Arrowhead Pharmaceuticals, Inc. (ARWR):

他们的列日期是:

  • 2019年6月30日
  • 2019年3月31日
  • 2018年12月31日
  • 2018年9月30日

这些日期对应以下财政季度:

  • 第1季度(Q1):1月1日至3月31日。
  • 第2季度(Q2):4月1日至6月30日。
  • 第3季度(Q3):7月1日至9月30日。
  • 第4季度(Q4):10月1日至12月31日

这些财政季度结束日期可能会有很大的差异,如DELL的例子所示。

DELL(发布在纳斯达克) 财政季度结束日期:2019年7月

Yahoo Finance 列日期:2019年7月31日

JSON日期:2019-08-02

从公司网站上得知:

Dell Technologies的财政年度何时结束?

  • 我们的财政年度为52或53周,以最接近1月31日的星期五结束。我们的2020财政年度将于2020年1月31日结束。有关以前的财政年度,请参见以下列表:我们的2019财政年度于2019年2月1日结束,我们的2018财政年度于2018年2月2日结束,我们的2017财政年度于2017年2月3日结束,我们的2016财政年度于2016年1月29日结束,我们的2015财政年度于2015年1月30日结束,我们的2014财政年度于2014年1月31日结束,我们的2013财政年度于2013年2月1日结束。

dell's fiscal quarters

注意:05-03-19和08-02-19日期。

这些来自DELL的JSON季度数据:

  • {'raw': 1564704000, 'fmt': '2019-08-02'}
  • {'raw': 1556841600, 'fmt': '2019-05-03'}

看起来这些列的日期与公司财务季度结束日期有关。因此,我建议您将JSON日期作为主要参考元素或相应的列日期。

P.S. 雅虎似乎正在进行某种日期魔法,因为它们似乎会根据假日、周末和月底移动这些列季度日期。


1

与其获取concerning_datefmt,最好获取时间戳。

concerning_date = container[0]['endDate']['raw']

在上面的示例中,您将获得结果1561852800,您可以将其转换为特定时区的日期。(提示:使用datetimepytz)。根据时区,此时间戳将产生以下结果:
Date in Los Angeles*: 29/06/2019, 17:00:00
Date in Berlin* :30/06/2019, 02:00:00
Date in Beijing*: 30/06/2019, 07:00:00
Date in New York* :29/06/2019, 19:00:00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接