无法使用请求解析网页的确切结果

Posted

技术标签:

【中文标题】无法使用请求解析网页的确切结果【英文标题】:Unable to parse an exact result from a webpage using requests 【发布时间】:2020-02-06 13:57:33 【问题描述】:

我在 python 中创建了一个脚本来解析网页中的两个字段 - total revenue,它与 date 有关。我追求的字段是 javascript 加密的。它们在 json 数组中的页面源中可用。以下脚本可以相应地解析这两个字段。

但是,问题在于该页面中可见的日期与页面源中可用的日期不同。

Webpage link

该网页中的日期类似于 this

页面源中的日期类似于 this

显然有一天的变化。

访问该webpage后,当您点击此标签Quarterly时,您可以看到结果:

我试过了:

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

【问题讨论】:

如果日期最多相差一天,我的第一个猜测是差异来自时区转换。 我尝试使用不同的位置激活 *** 以查看结果,但变化仍然存在 @Janne Karila。 我刚试了你的代码,打印语句输出和页面上的一样(2019-6-30)。 是的,你是@Jack Fleeting,但其他代码肯定会有所不同。事实上,你可能会在其他时候得到不同的结果。在这一点上,Janne Karila 似乎在他的第一个猜测中是正确的。我想可能有什么办法可以解决这个问题。 能否再提供一些日期正确和日期错误的股票代码? 【参考方案1】:

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


原始答案


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 相当于:07/28/2019 12:00am (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 日期和列日期有时不同的原始问题。但这是我目前关于正在发生的日期差异的假设。

    root.App.main 中提取的 json 日期(fmt 和 raw)采用协调世界时 (UTC)。这一点很清楚,因为 raw 中的 UNIX 时间戳。

    表格列中显示的日期似乎位于东部标准时间 (EST) 时区。 EST 目前是 UTC-4。这意味着 2019-07-28 22:00 (10pm) EST 将是 2019-07-29 02:00 (2am) UTC。根据 traceroute,托管 finance.yahoo.com 的服务器看起来在美国 结果。这些值也在 json 文件中:

    'exchangeTimezoneName': 'America/New_York' 'exchangeTimezoneShortName':'EDT'

    还有一些日期差异可能与网站使用的底层 React 代码相关联。这个问题更难诊断,因为代码不可见。

目前,我认为最好的解决方案是使用 UNIX 时间戳作为您的真实时间参考。此引用可用于替换表格列的日期。

在 JSON 文件和列之间肯定会发生某种类型的转换。

NVIDIA JSON 文件:'endDate': 'raw': 1561766400, 'fmt': '2019-06-29'

NVIDIA 相关总收入栏:2019 年 6 月 30 日

但总收入列日期应为 2019 年 6 月 28 日(美国东部时间),因为 1561766400 的 UNIX 时间戳是 2019 年 6 月 29 日凌晨 12:00(UTC)。

DELL 的差异大于基本 UNIX 时间戳和 EDT 时间戳转换。

戴尔 JSON 文件:"raw":1564704000,"fmt":"2019-08-02"

DELL 相关总收入列:2019 年 7 月 31 日

如果我们将 UNIX 时间戳转换为 EDT 时间戳,结果将是 2019 年 8 月 1 日,但在 DELL 示例中并非如此,即 2019 年 7 月 31 日。雅虎代码库中的某些东西一定会导致这种差异。

我开始相信 React 可能是造成这些日期差异的罪魁祸首,但如果不做更多研究,我无法确定。

如果 React 是根本原因,那么最好的选择是使用 JSON 数据中的日期元素。


2019 年 10 月 17 日更新答案


这个问题非常有趣,因为这些列日期似乎与公司的正式财政季度末相关联,而不是日期对话问题。

这里有几个例子

苹果公司 (AAPL) Atlassian Corporation Plc (TEAM) Arrowhead Pharmaceuticals, Inc. (ARWR):

他们的专栏日期是:

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

这些日期与这些财政季度相匹配。

第 1 季度(第一季度):1 月 1 日 - 3 月 31 日。 第 2 季度(第 2 季度):4 月 1 日 - 6 月 30 日。 第 3 季度(第 3 季度):7 月 1 日 - 9 月 30 日。 第 4 季度(第 4 季度):10 月 1 日 - 12 月 31 日

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

戴尔(在纳斯达克发布) 财政季度末:2019 年 7 月

雅虎财经 专栏日期:7/31/2019

JSON 日期:2019-08-02

来自公司网站:

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

我们的财政年度是在最近的 1 月 31 日星期五结束的 52 或 53 周期间。我们的 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 年 1 月 31 日 我们的 2013 财年于 2013 年 2 月 1 日结束

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

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

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

这些列日期似乎与公司的财政季度结束日期相关联。因此,我建议您使用 JSON 日期作为主要参考元素或相应的列日期。

附:雅虎出现了某种类型的日期巫术,因为他们似乎根据假期、周末和月末移动这些列季度日期。

【讨论】:

【参考方案2】:

如 cmets 中所述,您需要将日期转换为适当的时区 (EST),这可以通过 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)

【讨论】:

这似乎是我期望的结果。我不能像06-29-2019 而不是2019-06-29 19:00:00-05:00 那样格式化日期吗?谢谢。 @MITHU 我在更新中添加了这个。首先需要使用.date()将datetime对象转换为日期,然后需要使用.strftime将其转换为所需的格式。 如果我尝试使用这个代码 NVDA 会导致这个 link,我发现差异更大。 @MITHU 奇怪。这意味着 TZ 可能不是问题,因为NVIDIA 也在 EST 中。我会研究解决方案,但我现在不知道为什么会发生这种情况。【参考方案3】:

与其获取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

【讨论】:

以上是关于无法使用请求解析网页的确切结果的主要内容,如果未能解决你的问题,请参考以下文章

无法从网页中抓取所有公司名称

无法显示网页,错误代码:500 这是怎么回事?应该怎么办?

无法获得 Google fit 应用上显示的确切步骤

无法继续进行 switch case C 编程

为啥进网站的时候出这个?它说无法显示网页。可能是网络问题或404错误

无法使用请求从网页中获取特定项目