尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

东方财富财报爬虫:Selenium与Requests技术路线对比解析

发布时间:2026/9/26 8:34:25

资讯中心
01
ARTICLE

东方财富财报爬虫:Selenium与Requests技术路线对比解析

东方财富财报爬虫:Selenium与Requests技术路线对比解析
简介基于Selenium与Requests的东方财富网财报爬取项目为爬虫开发者和金融数据研究者提供一站式上市公司财务数据采集方案可解决从东财批量抓取财报并输出CSV的问题。项目内含两个Python脚本分别演示Selenium与Requests两种技术路线支持按年度、财季、报表类型采集并导出资产负债表、利润表、现金流量表等数据。资源共19个文件压缩后约37.96MB含脚本、10个CSV数据表、说明文档与备份文件便于对照学习。其中Requests方案效率更高适合生产环境。目前已有47人学习/下载适合需要快速实现财报抓取的开发者。1. 拆解东方财富财报爬虫Selenium与Requests两条路线怎么选网络爬虫方向的项目资料不少但真正把同一目标用两种技术路线实现、还带着完整CSV产出物的不多。这份资源包里的核心是两个脚本eastmoney_crawler.py走 Selenium 自动化测试框架控制浏览器像人一样翻页eastmoney_crawler2.py走 Requests 库直连接口把 HTTP 请求发给服务器后直接拿 JSON 数据。两者都能采集东方财富平台上的上市公司财务报表覆盖资产负债表、利润表、现金流量表、业绩报表等八类数据时间范围指向 2018 至 2023 年。适合想拿现成代码改参数就能跑的从业者也适合想对比两种爬虫思路差异的学习者。第一次跑完两个方案你大概率会留下一个印象同一个 JSON 接口能比浏览器自动化快出两个数量级。2. Selenium方案财务报表页面动态加载的模拟浏览器采集2.1 东方财富财务页面的结构动态数据与分页东方财富的财务报表页面不是那种服务端直接拼好 HTML 的传统网站。打开资产负债表页面表格里的数字、表头、单位、报告期几乎全部由前端 JavaScript 脚本在页面加载后向后端发起请求再把返回的数据渲染到 DOM 节点上。换句话说用 Requests 直接去get那个 HTML 地址拿到的只是一个空壳框架里面没有一行财务数据。这种页面形态正是 Selenium 的典型适用场景。Selenium 通过 WebDriver 驱动真实的浏览器内核让 Chrome 或 Firefox 去加载页面JavaScript 照常执行等数据渲染完成后再读取表格内容。eastmoney_crawler.py的核心思路就是定义一个等待条件反复轮询目标表格节点是否出现了预期行数然后定位table下的tr和td逐行提取文本。在这个过程中要注意分页。东方财富的报表列表默认每页展示 50 条记录翻页控件是一个带>from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def init_driver(chromedriver_path: str, headless: bool True): options webdriver.ChromeOptions() if headless: options.add_argument(--headlessnew) options.add_argument(--no-sandbox) options.add_argument(--disable-dev-shm-usage) service Service(chromedriver_path) driver webdriver.Chrome(serviceservice, optionsoptions) driver.set_page_load_timeout(30) return driver def fetch_table_rows(driver, table_selector: str, wait_seconds: int): wait WebDriverWait(driver, wait_seconds) table wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, table_selector)) ) rows table.find_elements(By.TAG_NAME, tr) data [] for row in rows: cells row.find_elements(By.TAG_NAME, td) if cells: data.append([cell.text.strip() for cell in cells]) return data这段代码里有几个关键点。headlessnew是无头模式适合 Windows 服务器上没装显示器的情况但代价是部分反爬脚本对无头浏览器识别更敏感遇到验证码时建议改回有头模式观察页面状况。WebDriverWait配合presence_of_element_located是等表格骨架出现如果表格里某些单元格是延迟填充的还需要把条件换成text_to_be_present_in_element否则拿到的仍然是空文本。set_page_load_timeout(30)是页面整体加载超时控制网络波动时可以放宽到 45 秒但超过这个值还加载不完更大概率是目标站点对自动化请求做了限制调大超时意义不大。2.3 速率瓶颈每一次翻页都是完整页面渲染用方案A跑过一遍完整流程后你会有很直观的感受慢。每打开一页浏览器要重新执行几十个 JS 文件渲染几百个 DOM 节点再触发网络图片和广告资源的加载。东方财富财报页的筛选条件多每个报告期切换、每页翻页都是一次整页刷新一页 50 条数据跑十几秒是常态。如果任务范围只是几个特定年度的某一张报表比如只采集 2021 年第一季度的资产负债表前 3 页方案A完全够用。它的优势是开发门槛低、调试直观出问题时能看到浏览器里真实发生了什么事。但要按年度区间 2018 到 2023、四个财季、四种报表全量去采集页面数量会迅速膨胀单页十几秒乘上几百页消耗的时间很难让人接受。这就是eastmoney_crawler2.py被选为主要实施方案的根本原因用 Requests 绕过浏览器渲染直接请求财务报表数据背后的 JSON 接口。3. Requests方案从抓包定位到直接请求JSON接口3.1 抓包定位真实接口请求头与参数格式方案B的第一步不是写代码而是打开浏览器开发者工具切到 Network 面板在东方财富财务报表页面里翻一页、切换一个报告期观察页面发出的是什么请求。你会发现真正返回财务数据的是一个以datacenter.eastmoney.com域名开头的接口路径里带FilterXsjs或类似名称的参数响应体是一段 JSON 文本里面包含data、success、message等字段。这个接口的请求参数远比页面 URL 复杂常见的分组参数大致如下参数取值示例作用filter(reportdate2023-12-31)过滤指定报告期typeRPT_LICO_FN_CPD等报表类型代码pageNumber1当前页码pageSize50每页条数sortColumnsSECURITY_CODE排序列sortTypes1排序方向sourceHSF10数据来源标识sortColumns是一个容易被忽略的参数。东方财富接口默认排序字段在不同报表里有差异如果不对排序方式做强约束翻页时会出现数据顺序漂移同一公司在页面1和页面2的边界位置可能被重复读取或遗漏。所以方案B在构造请求时强制固定SECURITY_CODE升序确保每次爬取结果都稳定。3.2 eastmoney_crawler2.py 的请求构造与JSON解析eastmoney_crawler2.py是典型的 Requests 加循环分页结构。核心逻辑可以拆成下面这段代码它展示的是请求头、参数拼接和 JSON 解析的配合import requests import json import pandas as pd SESSION_URL https://datacenter.eastmoney.com/securities/api/data/v1/get def fetch_financial_report(api_url, params, headers, max_retry3): for attempt in range(max_retry): resp requests.get(api_url, paramsparams, headersheaders, timeout10) if resp.status_code 200: payload resp.json() if payload.get(success): return payload.get(result, {}).get(data, []) # 非200或successfalse时进入重试 if attempt max_retry - 1: time.sleep(2 * (attempt 1)) return [] def collect_all_pages(api_url, params, headers, start_page, end_page, page_size50): all_records [] for page in range(start_page, end_page 1): params[pageNumber] page params[pageSize] page_size records fetch_financial_report(api_url, params, headers) if not records: break all_records.extend(records) print(fpage {page} done, records: {len(records)}) return all_records # 使用示例 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://data.eastmoney.com/, Accept: application/json, text/plain, */*, } params { type: RPT_LICO_FN_CPD, filter: (reportdate2023-12-31), sortColumns: SECURITY_CODE, sortTypes: 1, pageNumber: 1, pageSize: 50, } result collect_all_pages(SESSION_URL, params, headers, start_page1, end_page5) df pd.DataFrame(result) df.to_csv(balance_sheet.csv, indexFalse, encodingutf-8-sig)这段代码的逻辑是fetch_financial_report负责单次请求收到 200 但successfalse时同样当作失败触发指数退避重试collect_all_pages负责页面迭代某页拿不到数据就立即终止循环避免无效请求一路打到底。有三处参数值得在换报表时额外确认。第一pageSize调成 50 能减少请求次数但接口单页最大值有上限强行设置超过接口阈值的值会被静默忽略返回数据的条数可能少于预期。第二headers里缺少User-Agent时部分反爬配置会返回 403加上常见浏览器的 UA 是底线操作。第三timeout参数必须显式设置Requests 默认不设超时一旦服务端不响应线程就会一直挂起整个脚本会被拖死。热词里反复出现的exceeded retry limit、last status: 429 too many requests就是在并发或高频请求下触发的限流封禁重试逻辑一旦设计不当429 会演变成持续被拒的恶性循环。3.3 效率对比的数学账两个数量级差在哪Selenium 方案的耗时由页面加载时间主导打开一个 50 行的报表页平均需要 8 到 15 秒Requests 方案拿到相同 50 行数据通常只需要 300 到 500 毫秒。差出的两个数量级主要来自三部分Requests 省掉了 HTML、CSS、JS 资源的下载省掉了 JS 引擎解析执行的时间省掉了浏览器渲染引擎构建 DOM 和布局的时间。方案B只保留最核心的事务——HTTP 请求、JSON 解析、CSV 落盘。两份代码最终产出的 CSV 结构是兼容的都包含证券代码、证券简称、报告期、报表项目名、数值等标准列。这也是这个资源包设计合理的地方方案A作为验证打法确认页面结构和字段名称方案B作为批量实施工具拿到验证结果后直接替换方案A执行全量采集。4. 命令行实操年度、财季、报表与页码的正确组合4.1 参数规则会计年度2018-2023与财季标识eastmoney_crawler2.py启动时用的是命令行参数设计上参考了常规爬虫项目的标准做法不接受交互式输入避免在服务器上无人值守时卡在input()等待。一次典型的调用长这样python eastmoney_crawler2.py \ --start-year 2018 \ --end-year 2023 \ --quarter 1 \ --report-type balance \ --start-page 1 \ --end-page 10 \ --output-dir D:/estmoney/参数命名见名知义但有几个细节容易忽略。--quarter接受的是 1 到 4 的数字脚本内部会把它转换为对应的报告期日期1 对应3-312 对应6-303 对应9-304 对应12-31。不同财季对应不同报表规则比如第四季度报告期往往附带全年累计数据和单季度数据两套口径脚本默认只取合并口径如果你想要母公司口径需要修改filter里关于合并类型的代码段。--report-type接收的是关键字不是报表中文名脚本启动时会先打印关键字到报表类型代码的映射表看到映射再继续防止输错。--start-page和--end-page是闭区间两端都会采集比如1 10会抓 10 页、每页 50 条合计最多 500 条记录。4.2 报表字段映射资产负债表、利润表、现金流量表的差别这个资源包里预置了 8 个 CS文件包括资产负债表、利润表、现金流量表、业绩报表、业绩预告表、业绩快报表、利润表全部和预约披露时间表。这正好对应东方财富后台的几套报表类型代码每套代码的字段列表不完全一致。报表文件接口类型代码示例特有字段资产负债表.csvRPT_LICO_FN_CPD下相关类型货币资金、应收账款、存货、总资产、负债合计利润表.csvRPT_LICO_FN_CPD利润表类型营业总收入、营业总成本、净利润、基本每股收益现金流量表.csvRPT_LICO_FN_CPD现金流类型经营活动现金流量净额、投资活动现金流量净额业绩报表.csv业绩快报相关类型净利润同比、营收同比、每股收益同比业绩预告表.csv业绩预告相关类型预告类型、变动幅度、预告摘要业绩快报表.csv业绩快报相关类型快报摘要、快报日期、实际数值预约披露时间表.csv披露日期相关类型预计披露日期、实际披露日期、报告期字段映射到 CSV 时脚本会把 JSON 里的SECURITY_CODE转为证券代码列REPORT_DATE转为报告期列数值字段统一转成float。这里有一个经典问题东方财富接口返回的数值型字段偶尔会出现空字符串直接把空字符串转float会抛异常。脚本里的处理是把空值先替换成None数值缺省统一补 0如果你不希望用 0 掩盖缺失需要自己改一下空值策略。就我接触过的同类爬虫来说补 0 是最省事的做法但对后续做财务分析的人不友好建议落地时先留空让下游处理时不至于把缺失项当真实 0 值参与计算。4.3 输出路径D:/estmoney/与文件覆盖逻辑摘要描述里明确了输出目录D:/estmoney/。脚本不会自动创建不存在的目录首次运行前需要手动建立否则会在写入 CSV 时报错。这是一个小的设计取舍自动os.makedirs(exist_okTrue)只需要三行代码但资源包保持了这个动作由使用者完成的风格这么做的好处是强迫使用者确认输出盘符存在且路径正确避免跑完几百页数据后发现目录配错白跑一趟。文件命名逻辑是报表类型_报告期_起始页_终止页.csv。同一次运行重复执行会覆盖同名文件。如果你要累积多个财季的数据要么改文件名的前缀要么手动把旧文件移到另一个目录。实际使用时更推荐的做法是每次运行前用 Python 生成一个带时间戳的子目录例如D:/estmoney/20240215_1530/不同批次的采集结果彼此隔离后续合并时再统一拼接。这种习惯能极大降低多季度连续采集时的文件管理负担。4.4 业务低峰时段执行大规模采集摘要和 README 里都提到大型采集建议在业务低峰时段进行。这个提示背后有实际依据东方财富的数据网关在高并发时段对非同源请求的限流更明显同一 IP 短时间内连续请求特定数据接口触发限流的概率会明显上升。热词里频繁出现的429 too many requests正是这种限流的典型响应码。我一般的处理方式是单次采集页数超过 100 页的任务拆成多个时间段执行Requests 请求之间加 0.2 到 0.5 秒的随机延时抓取中途如果连续收到两个 429立即 sleep 30 秒再继续。这样一来效率虽然比纯并发模式低一点但任务的整体成功率反而更高。要算一笔账一天内完成和三天内完成对财务数据采集这种对时效性不敏感的任务来说差别不大但请求被全量拉黑导致的返工代价要高得多。5. 避坑与常见问题采集过程中的八个翻车点5.1 连续429限流导致重试死循环现象脚本运行到第 40 页左右终端连续输出429 too many requests随后exceeded retry limit异常退出已采集的数据全部丢失。原因单 IP 在短时间窗口内对东方财富数据接口发起了高频请求网关按请求频率算法触发限流。默认的三次重试策略在持续 429 时没有意义三次重试用完后直接抛出异常结束进程已写入内存的 39 页数据没有落盘。解决重试逻辑需要区分限流和真正的网络错误。当响应码是 429 时不能按普通失败对待应该把等待时间拉长到 30 秒以上同时检查是否还有必要继续当前会话。更稳妥的做法是每完成一页立即把当前 DataFrame 追加写入磁盘写一个append模式的 CSV 落盘函数。这样即使中途中断最多丢一页的数据而不是前功尽弃重跑整个区间。5.2 Selenium打开的页面无表格数据现象eastmoney_crawler.py运行时浏览器正常启动目标页面也能打开但presence_of_element_located一直超时页面上没有任何财务表格内容。原因这个场景和三年前的踩坑情况比较类似。东方财富的反爬机制会对带自动化特征的用户代理做拦截同时无头模式下缺少关键请求头时页面加载后会进入一个验证或异常分支不渲染业务数据表格。另一个常见原因是 ChromeDriver 日志目录或用户数据目录权限不对在 Windows 服务器上尤其容易出现。解决先改成有头模式确认页面在真实浏览器窗口里是否正常展示数据。如果真实浏览器正常而 Selenium 异常说明是自动化特征被识别需要给 ChromeDriver 增加排除自动化控制的参数并设置完整的 UA。如果页面在两种模式下都无数据大概率是请求头里的Referer丢失需要手动指定为数据频道的首页地址。5.3 导出的CSV出现乱码或Excel无法打开现象CSV 文件用记事本看正常用 Excel 双击打开时中文全部变成乱码并且提示文件格式与扩展名不匹配。原因脚本区域中写入 CSV 时默认用了 UTF-8 编码。Excel 在中文 Windows 环境下默认用 ANSI也就是 GBK解析文本文件没有 BOM 头的 UTF-8 文件会被误按 GBK 解码中文内容自然乱码。解决这是整个资源包里最容易预判也最值得预判的问题。写入时把编码参数从utf-8改成utf-8-sig也就是带 BOM 的 UTF-8Excel 识别后会自动切成正确的解码方式。我每次写 DataFrame 到 CSV 都强制带上encodingutf-8-sig已经不需要思考。5.4 数值字段出现None或空字符串现象采集成功返回的 JSON 里某些公司的净利润、营收字段是空串转换为 DataFrame 后形成None下游统计出错。原因部分上市公司在报告期内未披露完整数据或者公司处于停牌、暂停上市状态数据源本身就没有返回有效数值。这不是爬虫 bug而是数据源侧的缺失。解决明确空值策略。如果目标是做数量分析补 0 最省事如果目标是做基本面筛选建议不要补 0保留空值并在后续清洗时用dropna(subset[净利润])过滤。同时在采集完成后输出一份缺失字段统计表列出哪些公司、哪些报表列存在空值方便判断是否需要二次补充。5.5 页码超过数据边界导致越界现象设置--end-page 50但目标接口实际只有 32 页数据。脚本从第 33 页开始请求返回空数组但循环没有正确退出继续向后发了 18 个无效请求白白浪费请求配额。原因脚本对空数据页的退出条件判断不严格。部分接口在页码超出范围时返回的不是空列表而是包含少量无效占位记录的数组导致if not records: break判断失效。解决增加两层判断。第一层当返回记录数为 0 时直接退出。第二层当返回记录数少于page_size时也退出因为最后一页通常不满页之后必然是空页。这个改动只需三行代码但对接口配额的保护效果很明显也避免了持续请求触发限流。6. 进阶技巧用数据校验脚本守好CSV的最后一公里报表采集完成不等于任务结束CSV 里的数据质量需要独立验证。我习惯在采集脚本之外单独写一个校验脚本跑完之后立刻对落盘的 CSV 做三件事核对行数是否符合页码范围、检查关键字段的缺失率是否异常、对比核心指标的跨表一致性。import pandas as pd import sys def validate_csv(path, expected_rowsNone, key_colsNone): df pd.read_csv(path, encodingutf-8-sig, dtype{SECURITY_CODE: str}) issues [] if expected_rows and len(df) ! expected_rows: issues.append(frow count mismatch: expected {expected_rows}, got {len(df)}) for col in key_cols or []: if col not in df.columns: issues.append(fmissing column: {col}) continue missing df[col].isna().sum() total len(df) ratio missing / total if total else 0 if ratio 0.05: issues.append(f{col}: missing ratio {ratio:.2%} exceeds 5%) if issues: for msg in issues: print([FAIL], msg) sys.exit(1) print([OK] all checks passed:, path) validate_csv( D:/estmoney/资产负债表_2018-2023Q1_1_10.csv, expected_rows500, key_cols[SECURITY_CODE, REPORT_DATE, TOTAL_ASSETS, TOTAL_LIABILITIES] )这里的关键思路是把校验做成前置门禁行数对不上说明采集过程可能发生了页面漏抓缺失率超过 5% 说明接口参数或空值策略需要调整表格跨表一致性则靠不同报表之间的勾稽关系去验证例如资产负债表的资产合计应当等于负债合计加所有者权益合计。把这些检查固化成脚本之后每次采集完跑一遍问题在进入分析流程之前就被拦住了。从那以后我每次跑完一批财务数据采集都强制走一遍这个校验流程哪怕只是重跑一个几十页的小任务也不跳过。这套组合拳——Requests 直连、分页退出条件收紧、UTF-8 with BOM 落盘、数据校验前置——基本可以覆盖东方财富爬虫项目的大部分翻车场景。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。