使用Python模拟API请求抓取Yahoo Finance历史财报数据

使用Python模拟API请求抓取Yahoo Finance历史财报数据

本教程旨在解决使用python抓取yahoo finance动态加载财报数据的问题。由于yahoo finance的财报页面内容通过javascript动态加载,传统的beautifulsoup直接解析html的方法往往无法获取完整数据。我们将深入探讨如何通过模拟浏览器api请求,直接从yahoo finance的后端接口获取结构化的历史财报数据,包括构建请求头、url参数、json载荷以及处理日期筛选等关键步骤。

1. 动态内容抓取的挑战与API模拟

在尝试从Yahoo Finance等现代网站抓取数据时,一个常见的问题是页面内容并非全部包含在初始HTML响应中。特别是像财报日历这样的数据,通常会通过JavaScript在页面加载后异步地从后端API获取并渲染。这意味着,如果直接使用requests库获取页面HTML并结合BeautifulSoup进行解析,很可能只能得到一个空的或不完整的数据集。

原始尝试中,使用requests和BeautifulSoup去解析https://finance.yahoo.com/calendar/earnings?day={yesterday}页面,发现获取到的并非预期中的“昨日”财报数据,而是当前日期(如周日)的少量信息,这正是动态内容加载的典型表现。

解决这类问题的核心方法是模拟浏览器行为,直接调用网站后端用于获取数据的API接口。通过浏览器开发者工具(Network标签页),我们可以观察到页面加载过程中发出的XHR(XMLHttpRequest)请求,这些请求通常会返回JSON格式的结构化数据,这比解析HTML要高效和稳定得多。

2. 识别并模拟Yahoo Finance的财报API请求

通过分析Yahoo Finance财报页面的网络请求,我们可以发现其财报数据是通过向https://query2.finance.yahoo.com/v1/finance/visualization这个URL发送POST请求来获取的。这个请求的特点是:

立即学习“Python免费学习笔记(深入)”;

请求方法: POST请求URL: https://query2.finance.yahoo.com/v1/finance/visualization请求头 (Headers): 包含User-Agent等,模拟浏览器行为。URL参数 (Params): 包含crumb、lang、region等。请求体 (JSON Payload): 这是一个复杂的JSON对象,定义了需要查询的实体类型、包含的字段、日期范围、排序方式等。Cookie: 可能需要特定的Cookie(如A3)来维持会话或通过认证。

3. 构建API请求参数

为了成功模拟这个API请求,我们需要精确地构建上述各项参数。

3.1 请求头 (Headers)

User-Agent是必不可少的,它让服务器认为我们是一个合法的浏览器请求。

headers = {    "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",}

3.2 URL参数 (Params)

crumb是一个安全令牌,通常用于防止CSRF攻击,它的值可能会动态变化。lang和region定义了语言和地区。

params = {    "crumb": "EwuCwsPbKM2", # 注意:此crumb可能随时间变化,需要动态获取或更新    "lang": "en-US",    "region": "US",    "corsDomain": "finance.yahoo.com",}

3.3 请求体 (JSON Payload – query)

这是最关键的部分,它定义了我们想要获取的数据。

entityIdType: 指定查询类型,这里是”earnings”。includeFields: 一个列表,包含我们希望在结果中看到的字段,例如股票代码、公司名称、EPS估值、实际EPS等。offset 和 size: 用于分页,offset是偏移量,size是每页返回的条目数。query: 定义了筛选条件。其中operands是一个列表,每个元素是一个条件。例如,{“operands”: [“startdatetime”, “2023-12-15”], “operator”: “gte”}表示开始日期大于等于2023-12-15。{“operands”: [“startdatetime”, “2023-12-16”], “operator”: “lt”}表示开始日期小于2023-12-16。这两个条件结合起来,就精确筛选出2023-12-15这一天的财报数据。”operator”: “and”表示多个条件之间是“与”的关系。sortField 和 sortType: 定义了结果的排序字段和排序方式。

from datetime import date, timedelta# 假设我们要获取2023年12月15日的财报数据target_date = date(2023, 12, 15)# 如果要获取“昨天”的财报,且确保是工作日,需要更复杂的日期逻辑# 例如:# today = date.today()# target_date = today - timedelta(days=1)# while target_date.weekday() > 4: # 0-Mon, 1-Tue, ..., 4-Fri, 5-Sat, 6-Sun#     target_date -= timedelta(days=1)query_payload = {    "entityIdType": "earnings",    "includeFields": [        "ticker",        "companyshortname",        "eventname",        "startdatetime",        "startdatetimetype",        "epsestimate",        "epsactual",        "epssurprisepct",        "timeZoneShortName",        "gmtOffsetMilliSeconds",    ],    "offset": 0,    "query": {        "operands": [            {"operands": ["startdatetime", f"{target_date}T00:00:00.000Z"], "operator": "gte"},            {"operands": ["startdatetime", f"{target_date + timedelta(days=1)}T00:00:00.000Z"], "operator": "lt"},            {"operands": ["region", "us"], "operator": "eq"},        ],        "operator": "and",    },    "size": 100, # 可以根据需要调整,获取更多数据    "sortField": "companyshortname",    "sortType": "ASC",}

3.4 Cookie

某些情况下,Yahoo Finance会检查特定的Cookie来验证会话。A3 Cookie就是其中之一。它的值也是动态的,可能会过期。

# 注意:此cookie可能随时间变化,需要动态获取或更新cookie_value = "d=AQABBK8KXmQCEA8-VE0dBLqG5QEpQ7OglmEFEgABCAHCeWWpZfNtb2UB9qMAAAcIqgpeZJj7vK8&S=AQAAAqhyTAOrxcxONc4ktfzCOkg"

4. 完整示例代码

结合上述所有参数,我们可以构建一个完整的Python脚本来抓取指定日期的Yahoo Finance财报数据。

import requestsfrom datetime import date, timedeltadef get_yahoo_earnings(target_date: date):    """    从Yahoo Finance API获取指定日期的财报数据。    Args:        target_date (date): 目标日期,例如 date(2023, 12, 15)。    Returns:        list: 包含财报数据的列表,每个元素是一个字典。    """    headers = {        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",    }    # URL参数,crumb可能需要定期更新    params = {        "crumb": "EwuCwsPbKM2", # 示例值,实际使用时可能需要更新        "lang": "en-US",        "region": "US",        "corsDomain": "finance.yahoo.com",    }    # 构建JSON请求体,用于指定查询条件    query_payload = {        "entityIdType": "earnings",        "includeFields": [            "ticker",            "companyshortname",            "eventname",            "startdatetime",            "startdatetimetype",            "epsestimate",            "epsactual",            "epssurprisepct",            "timeZoneShortName",            "gmtOffsetMilliSeconds",        ],        "offset": 0,        "query": {            "operands": [                {"operands": ["startdatetime", f"{target_date}T00:00:00.000Z"], "operator": "gte"},                {"operands": ["startdatetime", f"{target_date + timedelta(days=1)}T00:00:00.000Z"], "operator": "lt"},                {"operands": ["region", "us"], "operator": "eq"},            ],            "operator": "and",        },        "size": 100, # 每页获取的最大条目数        "sortField": "companyshortname",        "sortType": "ASC",    }    # Cookie,A3值也可能需要定期更新    cookie_value = "d=AQABBK8KXmQCEA8-VE0dBLqG5QEpQ7OglmEFEgABCAHCeWWpZfNtb2UB9qMAAAcIqgpeZJj7vK8&S=AQAAAqhyTAOrxcxONc4ktfzCOkg" # 示例值,实际使用时可能需要更新    url = "https://query2.finance.yahoo.com/v1/finance/visualization"    try:        with requests.Session() as s:            s.headers.update(headers)            s.cookies["A3"] = cookie_value # 设置会话cookie            response = s.post(url, params=params, json=query_payload)            response.raise_for_status() # 检查HTTP请求是否成功            data = response.json()            # 解析结果            earnings_data = []            if data and "finance" in data and "result" in data["finance"] and                data["finance"]["result"] and data["finance"]["result"][0]["documents"]:                for doc in data["finance"]["result"][0]["documents"]:                    if "rows" in doc:                        for r in doc["rows"]:                            # r[0]是ticker, r[1]是companyshortname, r[2]是eventname, r[3]是startdatetime                            earnings_data.append({                                "ticker": r[0],                                "company_name": r[1],                                "event_name": r[2],                                "start_datetime": r[3],                                # 其他字段可以根据includeFields的顺序和实际返回的r[]索引添加                            })            return earnings_data    except requests.exceptions.RequestException as e:        print(f"请求失败: {e}")        return []    except KeyError as e:        print(f"解析JSON数据失败,可能数据结构发生变化: {e}")        return []if __name__ == "__main__":    # 示例:获取2023年12月15日的财报数据    # 请注意,crumb和cookie_value可能已经失效,需要从浏览器开发者工具中获取最新的值。    target_date_example = date(2023, 12, 15)    print(f"正在获取 {target_date_example} 的财报数据...")    earnings = get_yahoo_earnings(target_date_example)    if earnings:        print(f"成功获取到 {len(earnings)} 条财报数据:")        for item in earnings:            print(f"公司: {item['company_name']:<40} 事件: {item['event_name'] or '': 4: # 5是周六,6是周日        last_weekday -= timedelta(days=1)    print(f"正在获取上一个工作日 ({last_weekday}) 的财报数据...")    last_weekday_earnings = get_yahoo_earnings(last_weekday)    if last_weekday_earnings:        print(f"成功获取到 {len(last_weekday_earnings)} 条财报数据:")        for item in last_weekday_earnings[:5]: # 仅打印前5条            print(f"公司: {item['company_name']:<40} 事件: {item['event_name'] or '':<40} 时间: {item['start_datetime']}")    else:        print("未能获取到上一个工作日的财报数据。")

5. 数据解析与处理

API返回的数据是JSON格式,结构清晰。在get_yahoo_earnings函数中,我们通过response.json()获取到Python字典,然后可以根据其结构进行解析。

核心数据位于data[“finance”][“result”][0][“documents”][0][“rows”]。每个row是一个列表,其中的元素对应于includeFields中定义的字段顺序。例如,r[0]是ticker,r[1]是companyshortname。为了提高可读性和健壮性,可以将这些列表项映射到字典中,如示例代码所示。

6. 注意事项

动态参数 (crumb 和 cookie): crumb和A3 Cookie的值是动态生成的,并且可能具有时效性。示例代码中的值可能很快失效。在实际应用中,你可能需要:定期手动更新: 每次运行脚本前,从浏览器开发者工具中获取最新的crumb和A3 Cookie值。自动化获取(高级): 使用Selenium或其他自动化工具模拟浏览器访问Yahoo Finance页面,然后从页面源码或网络请求中提取这些动态值。这超出了本教程的范围,但对于生产环境的爬虫是必要的。频率限制与IP封禁: 频繁或过快的请求可能会导致你的IP地址被Yahoo Finance暂时或永久封禁。建议在请求之间添加适当的延迟(例如,使用time.sleep()),并考虑使用代理IP池。API结构变化: Yahoo Finance的API结构可能会在未来发生变化。如果脚本突然失效,请检查其网站的网络请求,看是否有新的API端点、参数或数据结构调整。错误处理: 示例代码中包含了基本的try-except块来处理网络请求失败和JSON解析错误。在实际项目中,应增加更详细的错误日志记录和重试机制。日期处理: 确保日期格式(YYYY-MM-DDTHH:mm:ss.sssZ)符合API要求。使用datetime模块进行日期计算可以避免许多常见错误。对于获取“上一个工作日”的需求,需要额外的逻辑来跳过周末。

7. 总结

通过模拟API请求,我们成功绕过了Yahoo Finance财报页面动态加载内容的限制,直接获取到了结构化的历史财报数据。这种方法比传统的HTML解析更加稳定和高效,尤其适用于处理JavaScript动态渲染的网站。尽管存在crumb和cookie等动态参数的挑战,但理解其工作原理并采取相应的更新策略,是构建健壮网络爬虫的关键。掌握这种API模拟技术,将极大地扩展你在数据抓取方面的能力。

以上就是使用Python模拟API请求抓取Yahoo Finance历史财报数据的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1379011.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 20:17:25
下一篇 2025年12月14日 20:17:43

相关推荐

  • Wagtail自定义设置的集成与故障排除指南

    本教程详细介绍了如何在wagtail cms中集成自定义设置,并将其注册到后台管理界面。文章将逐步指导您定义设置模型、使用`wagtail.contrib.settings`和`wagtail.contrib.modeladmin`进行注册,并特别指出一个常见陷阱:自定义`construct_set…

    2025年12月14日
    000
  • 解决树莓派4B上OpenCV cv2导入错误的教程

    本文旨在解决树莓派4b上导入`cv2`库时遇到的`importerror: undefined symbol: __atomic_store_8`问题。我们将提供两种解决方案:一种是使用`ld_preload`进行快速临时修复,另一种是涉及通过特定`cmake`标志重新编译opencv的永久性方法。…

    2025年12月14日
    000
  • Python猜谜游戏:优化条件逻辑以实现准确的用户反馈

    本教程深入探讨python猜谜游戏中常见的逻辑陷阱,即如何避免在用户输入正确答案时,程序仍错误地显示“答案错误”的提示。我们将分析原始代码中条件判断的误区,并提供一个经过优化的解决方案。通过精确调整条件语句的执行顺序和结构,确保只有在猜错时才给出错误反馈,从而提升程序的交互准确性和用户体验。 原始代…

    2025年12月14日
    000
  • Python爬虫怎么写_Python网络爬虫编写步骤与实战案例

    答案:编写Python爬虫需先分析网页结构,用requests发送请求获取HTML,再用BeautifulSoup解析提取数据,最后清洗并存储为CSV或数据库;以豆瓣电影Top250为例,通过设置headers、分页爬取、解析class标签获取电影名、评分等信息,保存为CSV文件,并注意遵守robo…

    2025年12月14日
    000
  • Python官网项目模板的获取使用_Python官网快速启动项目指南

    首先使用Python官网推荐的标准项目模板快速搭建结构,接着可通过pipx安装Cookiecutter、用Poetry初始化项目或克隆GitHub高质量样板库来高效启动开发,确保项目具备良好组织与可维护性。 如果您希望快速启动一个Python项目,但不清楚如何组织文件结构或配置基础设置,可以直接使用…

    2025年12月14日
    000
  • 优化大规模细胞突变模拟:使用Numba提升Python/NumPy性能

    本文探讨了在python中模拟大规模细胞突变时遇到的性能瓶颈,特别是在处理数亿个细胞的数组操作和随机数生成方面。针对numpy在处理此类任务时的效率问题,文章提出并详细阐述了如何利用numba进行即时编译和优化,包括高效的整数型随机数生成、减少内存访问以及启用并行计算。通过这些优化,模拟速度可显著提…

    2025年12月14日
    000
  • 持久化ChromaDB向量嵌入:避免重复计算的教程

    本教程详细介绍了如何使用chromadb的`persist_directory`功能来高效地保存和加载向量嵌入数据库,从而避免重复计算。通过指定一个持久化目录,用户可以轻松地将生成的嵌入结果存储到本地文件系统,并在后续操作中直接加载,极大地节省了时间和计算资源。文章提供了清晰的代码示例和关键注意事项…

    2025年12月14日
    000
  • 在Xcelium中为Specman设置环境变量的策略与注意事项

    在Xcelium仿真环境中为Specman设置环境变量以集成外部工具(如Python)是一个常见挑战。本文将深入探讨环境变量的作用域、设置方法及其在复杂仿真流程中的继承机制,提供通过Shell脚本、Xcelium启动参数以及Specman ‘e’ 代码进行设置的详细指导,并强…

    2025年12月14日
    000
  • Python特殊方法文档中的object.前缀解读:并非指代object基类

    python文档中对特殊方法(如`__len__`、`__getitem__`)使用`object.`前缀,并非指这些方法是`object`基类的属性,也不是要求将它们添加到`object`类。这是一种文档约定,旨在表明这些是用户定义的任意类可以实现的方法,以模拟内置类型行为,从而融入python的…

    2025年12月14日
    000
  • 解决Kaggle环境中DuckDuckGo API调用HTTP错误指南

    在使用kaggle jupyter notebook进行机器学习课程(如fast.ai)时,调用`duckduckgo_search`库进行图片搜索可能会遇到`httperror`。本文将深入分析此问题的原因,并提供一个简单而有效的解决方案:通过更新kaggle notebook的环境配置,确保使用…

    2025年12月14日
    000
  • Python中实现+=操作符的动态类型处理策略

    本文探讨在Python中创建变量,使其能够灵活地通过`+=`操作符处理字符串和整数等不同初始数据类型的方法。文章将介绍两种核心模式:`StringBuilder`模式,用于将所有操作统一为字符串拼接;以及`UniversalIdentity`模式,通过自定义运算符重载,使变量能够动态适配第一个操作数…

    2025年12月14日
    000
  • Python环境管理深度解析:理解pipx与虚拟环境的正确应用

    本文深入探讨python包管理工具pipx与传统虚拟环境(如venv)之间的关键差异和正确应用场景。我们将解释为何pipx安装的库无法直接导入到python脚本中,因为其设计宗旨是为命令行应用程序提供隔离环境。教程将指导用户如何利用虚拟环境正确安装和管理项目所需的python库,确保模块可导入性,并…

    2025年12月14日
    000
  • Django Simple JWT 刷新令牌轮换与页面刷新策略

    在使用Django Simple JWT并启用刷新令牌轮换(`ROTATE_REFRESH_TOKENS`)时,快速页面刷新可能导致令牌在接收新令牌前被黑名单。本文将深入探讨此问题,并提供一种更健壮的解决方案:通过利用现有访问令牌处理页面加载,并在访问令牌过期时采用同步刷新机制,从而避免不必要的刷新…

    2025年12月14日
    000
  • Python中(回车符)的行为解析与行内更新技巧

    本文深入探讨了Python中回车符`r`的工作原理,解释了为何在使用`r`进行行内更新时可能出现残余字符,如”Time’s up!ning: 1″。文章通过具体代码示例,详细分析了该现象产生的原因,并提供了两种解决方案:一是放弃行内更新,采用默认换行符`n`;二是…

    2025年12月14日
    000
  • 使用Python和Selenium抓取动态网页数据教程

    本教程旨在指导读者如何使用python结合selenium和beautifulsoup库,有效抓取包含切换按钮等动态交互元素的网页数据。文章将详细阐述传统静态网页抓取方法在处理此类场景时的局限性,并提供一套完整的解决方案,通过模拟用户浏览器行为来获取动态加载的内容,最终实现对目标数据的精确提取。 在…

    2025年12月14日
    000
  • Python3数据类型有哪些_Python3常见数据类型全面解析

    Python3基本数据类型包括数字、字符串、列表、元组、字典、集合和布尔类型。1、数字类型含int、float、complex,分别表示整数、浮点数和复数;2、字符串是不可变的字符序列,用单、双或三引号定义,支持索引与切片;3、列表为有序可变序列,用方括号定义,可进行增删改查操作;4、元组为有序不可…

    2025年12月14日
    000
  • Python 3.x 环境中安装 enum 包报错及正确使用内置枚举模块

    在python 3.x环境中尝试安装外部`enum`包时,常会遇到`attributeerror: module ‘enum’ has no attribute ‘__version__’`错误。这通常是因为python 3.4及更高版本已内置`enu…

    2025年12月14日
    000
  • CCXT fetch_ohlcv数据获取:时区处理与最新K线完整性指南

    使用ccxt的`fetch_ohlcv`方法获取最新ohlcv数据时,用户常遇到数据缺失,尤其是在请求特定时间范围时。这通常是由于未正确处理时区造成的。ccxt默认处理utc时间戳,而用户可能传入了本地化时间。本文将深入探讨这一常见问题,提供正确的时区处理策略和代码示例,确保您能准确无误地获取到最新…

    2025年12月14日
    000
  • 在Windows上正确执行nbdev导出与本地包安装教程

    本教程旨在解决在Windows环境下使用nbdev时,如何正确结合`nbdev_export`命令与本地包安装。文章将详细解释`pip install .`(或`pip install -e .`)的用法,以确保nbdev导出的模块能够被项目正确识别和导入,并提供跨平台命令执行的注意事项及最佳实践。…

    2025年12月14日
    000
  • 利用Pandas与NumPy高效构建坐标DataFrame

    本文旨在指导读者如何基于现有DataFrame和索引列表,高效地构建一个新的坐标DataFrame。我们将探讨两种主要方法:基于循环和字典的迭代方法,以及利用NumPy高级索引和向量化操作的更优方法,旨在提高数据处理的效率和代码简洁性,为后续数据可视化(如路线绘制)奠定基础。 在数据分析和处理中,我…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信