1. 项目缘起为什么是东方财富网作为一名在数据分析和量化策略领域摸爬滚打了十多年的从业者我深知一手、及时、结构化的金融数据有多重要。无论是做简单的市场情绪分析还是构建复杂的多因子模型数据都是地基。市面上虽然有Wind、Choice这样的专业金融终端但对于个人开发者、学生或者小型团队来说成本是个不小的门槛。这时候公开的财经网站就成了我们获取数据的“富矿”。东方财富网作为国内流量最大的财经门户之一几乎涵盖了A股市场所有我们关心的信息实时行情、公司公告、财务报表、龙虎榜、资金流向、股吧讨论……数据维度非常丰富。更重要的是它的数据相对规整页面结构稳定对于希望通过技术手段获取数据的我们来说是一个绝佳的练习场和实战数据源。我这次整理笔记就是想系统地梳理一下从东方财富网抓取各类数据的思路、方法以及那些官方文档里不会写的“坑”希望能给同样需要金融数据的朋友们一条清晰的路径。2. 环境准备与核心工具选型工欲善其事必先利其器。爬虫项目的环境搭建看似简单但工具选型直接决定了后续开发的效率和代码的健壮性。下面是我基于多年实战总结出的一套稳定组合。2.1 Python生态为什么是Requests BeautifulSoup Pandas对于东方财富网这类主要以静态HTML页面呈现数据的网站我的首选工具链是Requests BeautifulSoup Pandas。这个组合轻量、灵活学习曲线平缓足以应对90%以上的数据抓取需求。Requests库负责网络请求是爬虫的“手”。它的API设计非常人性化会话保持Session、请求头Headers模拟、代理设置等功能一应俱全。相比于更底层的urllib用Requests写出的代码可读性要高得多。BeautifulSoup库负责解析HTML是爬虫的“眼睛”。东方财富网的页面虽然结构复杂但标签相对规范。BeautifulSoup支持多种解析器如lxml速度更快能让我们用类似find()、select()这样直观的方法像使用CSS选择器一样精准定位到需要的数据标签。Pandas库负责数据清洗与存储是爬虫的“大脑”。爬下来的数据往往是文本、列表或字典格式用Pandas的DataFrame可以非常方便地进行格式化、去重、缺失值处理并一键导出为CSV或Excel文件无缝对接后续的数据分析流程。为什么不直接用Scrapy对于定向、目标明确的单站爬取如只爬东方财富Scrapy框架稍显“重”了。它的优势在于强大的异步处理、项目管理和中间件扩展适合构建大型、分布式的爬虫系统。而我们当前的需求用上述“三板斧”组合开发速度更快代码也更直观易懂。2.2 关键配置让你的请求更像“真人”直接发送请求很容易被网站识别为爬虫并拒绝访问。因此模拟浏览器行为是关键。这主要靠精心设置HTTP请求头Headers。import requests from bs4 import BeautifulSoup # 定义一个常用的请求头尽可能模拟Chrome浏览器 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1 } # 使用Session对象可以自动管理Cookies保持登录状态如果需要 session requests.Session() session.headers.update(headers) # 示例访问东方财富网首页 url https://www.eastmoney.com/ try: response session.get(url, timeout10) response.raise_for_status() # 检查请求是否成功 response.encoding response.apparent_encoding # 自动识别编码 print(页面请求成功) except requests.exceptions.RequestException as e: print(f请求失败: {e})实操心得一User-Agent轮换与超时设置不要永远使用同一个User-Agent。可以准备一个列表随机选取降低被屏蔽的风险。同时务必为requests.get()设置timeout参数如10秒避免因网络问题导致程序长时间挂起。对于大规模爬取还应该加入随机延时如time.sleep(random.uniform(1, 3))体现友好爬虫的伦理。3. 实战解析攻克东方财富网典型数据页面东方财富网的数据分布在不同的子域名和页面路径下。我们需要针对不同页面的结构定制化的编写解析逻辑。下面以几个最常用的数据场景为例拆解其中的技术细节。3.1 抓取个股实时行情与日K线数据个股详情页如https://quote.eastmoney.com/sz000001.html包含了丰富的实时数据。但要注意很多实时数据是通过JavaScript动态加载的直接解析HTML可能拿不到。策略寻找数据接口API现代网站普遍采用前后端分离架构数据通常通过Ajax请求特定的JSON接口获取。我们可以通过浏览器的“开发者工具”F12切换到“网络”Network选项卡刷新页面观察XHR或Fetch请求找到返回数据的真实接口。例如东方财富的个股五档行情、分笔交易等数据往往有独立的接口。找到接口后直接用Requests模拟该接口的请求可能需要携带特定的参数或Cookies就能拿到结构清晰的JSON数据比解析HTML方便得多。# 示例假设通过分析找到了获取某股票实时数据的API此处为示例URL非真实接口 stock_code sz000001 api_url fhttps://push2.eastmoney.com/api/qt/stock/get?invt2fltt2fieldsf43,f57,f58,f169,f170secid{stock_code} try: resp session.get(api_url, headersheaders) data_json resp.json() # 解析JSON数据 stock_data data_json[data] current_price stock_data[f43] # 当前价 change stock_data[f170] # 涨跌额 print(f股票{stock_code}: 当前价 {current_price}, 涨跌额 {change}) except Exception as e: print(f获取接口数据失败: {e})对于历史日K线数据东方财富有标准的数据中心页面其数据也是通过接口加载通常参数包含股票代码、开始日期、结束日期、K线周期日K101等。分析并模拟这个接口请求就能批量获取历史数据。3.2 解析财务报表年报/季报公司的财务数据是基本面分析的核心。东方财富的财务数据页面如https://data.eastmoney.com/bbsj/000001.html表格众多结构规整非常适合用BeautifulSoup的select方法结合Pandas处理。步骤定位表格使用浏览器检查元素找到包含利润表、资产负债表等数据的table标签观察其id或class属性。使用Pandas直接读取Pandas的read_html()函数能直接将HTML中的表格解析为DataFrame列表。这是处理这类结构化数据的“神器”。import pandas as pd # 假设我们已经获取了财务报表页面的HTML内容 financial_html financial_soup BeautifulSoup(financial_html, lxml) # 方法一通过CSS选择器找到所有表格 tables financial_soup.select(table) # 通常第一个或第二个大表是核心的利润表或资产负债表需要具体分析 for i, table in enumerate(tables[:3]): # 先看前三个表格 df_list pd.read_html(str(table)) if df_list: print(f表格 {i} 解析成功形状{df_list[0].shape}) # 这里可以进一步判断哪个df是我们需要的 # 方法二如果知道表格的特定class或id直接定位 # 例如发现利润表有一个id为dt_1 try: income_statement_df pd.read_html(str(financial_soup.find(table, iddt_1)))[0] print(利润表数据) print(income_statement_df.head()) except: print(未找到指定的利润表)实操心得二表格数据的清洗pd.read_html()解析出来的DataFrame往往带有合并的表头、多余的空行或索引列。后续清洗工作必不可少df.dropna(howall)删除全为空的行。df.dropna(axis1, howall)删除全为空的列。使用df.iloc或df.columns重新设置正确的列名。注意货币单位如“亿元”和百分比符号可能需要将其转换为纯数字。3.3 抓取公司公告列表与详情公告数据是事件驱动策略的重要输入。公告列表页如https://data.eastmoney.com/notices/stock/000001.html通常是分页的并且有筛选条件。策略处理分页与详情页链接分析分页规律观察点击“下一页”时URL或请求参数的变化。通常是pageNum或page参数递增。构造一个循环即可遍历所有页面。提取公告链接在列表页解析出每条公告的标题和详情页链接a标签的href属性。串行访问详情页遍历链接列表逐个请求详情页提取公告正文、发布时间等。这里必须加入延时避免对服务器造成过大压力。import time base_url https://data.eastmoney.com/notices/stock/000001.html notice_list [] for page in range(1, 6): # 假设爬取前5页 # 构造带页码的URL具体参数需根据实际网站分析 list_url f{base_url}?pageNum{page} list_resp session.get(list_url) list_soup BeautifulSoup(list_resp.text, lxml) # 假设公告链接在 class 为 ‘notice-item’ 的div里的a标签中 items list_soup.select(.notice-item a) for item in items: notice_title item.text.strip() notice_link item[href] # 可能需要补全为绝对URL if notice_link.startswith(/): notice_link https://data.eastmoney.com notice_link # 访问详情页 time.sleep(1) # 重要每次请求详情页前暂停1秒 detail_resp session.get(notice_link) detail_soup BeautifulSoup(detail_resp.text, lxml) # 解析详情页正文假设在 id 为 ‘content’ 的div里 content_div detail_soup.find(div, idcontent) notice_content content_div.get_text(stripTrue) if content_div else N/A notice_list.append({ title: notice_title, link: notice_link, content: notice_content[:200] # 只存储前200字符示例 }) print(f第{page}页公告列表抓取完成) time.sleep(2) # 每爬完一页列表再暂停2秒 # 转换为DataFrame df_notices pd.DataFrame(notice_list)4. 数据清洗、存储与反爬应对策略爬取只是第一步让数据变得可用、可管理并确保爬虫能长期稳定运行才是更考验功夫的地方。4.1 数据清洗从杂乱到规整爬取的原始数据往往包含大量噪音。清洗工作通常在Pandas DataFrame中进行处理缺失值用df.isnull().sum()检查缺失情况。对于数值列可能用均值、中位数填充对于文本列可能填充为“未知”或直接删除。格式统一日期字符串可能有“2023-01-01”、“2023/01/01”等多种格式使用pd.to_datetime()统一转换。数字字符串可能包含逗号如“1234.56”或百分号需要先使用str.replace()清理再用pd.to_numeric()转换。去重使用df.drop_duplicates()根据关键字段如公告ID、股票代码日期去除重复数据。异常值处理对于股价、交易量等数据可以通过分位数如df[‘price’].quantile(0.99)或标准差方法识别并处理极端异常值。4.2 数据存储选择适合的介质根据数据量和用途选择存储方式CSV/Excel文件适合小规模、一次性分析的数据。使用df.to_csv(‘stock_data.csv’ indexFalse)保存。优点是简单直观无需额外环境。缺点是不便于增量更新和复杂查询。SQLite数据库轻量级单文件数据库适合个人项目。使用Python内置的sqlite3库可以将清洗后的DataFrame通过df.to_sql(‘table_name’ conn)写入。它支持SQL查询管理比多个CSV文件方便。MySQL/PostgreSQL数据库如果数据量很大或者需要多用户、多程序并发访问就需要专业的数据库服务器。这是生产级项目的标配。import sqlite3 # 存储到SQLite conn sqlite3.connect(eastmoney_data.db) df_cleaned.to_sql(daily_quotes, conn, if_existsreplace, indexFalse) # 替换模式 # if_existsappend 可用于增量添加数据 conn.close()4.3 反爬虫策略与应对之道东方财富网作为大型网站肯定有反爬虫机制。除了之前提到的设置Headers和添加延时还可能遇到以下问题及应对策略遇到的现象可能的原因应对策略返回状态码403/404IP地址被识别为爬虫并封禁1.使用代理IP池购买或搭建代理IP服务在请求时随机切换。这是应对IP封锁最有效的方法。2.降低请求频率大幅增加time.sleep()的间隔模拟真人浏览。返回的HTML中包含“请验证”、“访问过于频繁”等提示触发了基于行为如请求速度、会话的风控1.模拟完整会话使用requests.Session()并在首次访问时获取必要的Cookies。2.随机化操作不仅延时随机化访问顺序也可以在一定范围内随机。数据通过JavaScript加密或混淆网站为了增加爬取难度对关键数据进行了前端加密1.分析JS代码在开发者工具的“源代码”中搜索关键数据字段找到解密函数用Python的execjs库执行。2.使用Selenium直接控制浏览器渲染页面等数据加载完成后再从DOM中提取。这是“终极”但最慢的方法。实操心得三关于Selenium的使用只有当RequestsBeautifulSoup完全无法获取数据即数据由JS动态生成且没有公开接口时才考虑Selenium。它通过WebDriver控制真实浏览器如Chrome能执行所有JS但代价是速度极慢、资源占用高。使用时务必配合WebDriverWait和expected_conditions来等待元素加载避免因页面加载慢而报错。对于大规模爬取Selenium通常不是首选。5. 项目架构与代码组织建议当要爬取的数据类型增多如行情、财务、公告、研报代码很容易变得混乱。一个好的项目结构能极大提升可维护性。eastmoney_crawler/ ├── config.py # 配置文件存放请求头、代理列表、数据库连接字符串等 ├── utils.py # 工具函数如网络请求封装、日志设置、延时函数 ├── parsers/ # 解析器模块 │ ├── quote_parser.py # 行情数据解析 │ ├── financial_parser.py # 财务报表解析 │ └── notice_parser.py # 公告数据解析 ├── storages/ # 存储模块 │ ├── csv_storage.py │ └── db_storage.py ├── spiders/ # 爬虫主逻辑 │ └── stock_spider.py # 统筹调度各解析器和存储器 ├── data/ # 存放爬取的原始或清洗后的数据文件 └── main.py # 程序主入口核心思想是“高内聚、低耦合”每个解析器只负责解析一种页面的逻辑存储模块负责所有与IO相关的操作主爬虫负责组装流程获取URL - 调用解析器 - 调用存储器。这样当东方财富网的页面结构发生变化时你只需要修改对应的解析器而不用动其他部分的代码。6. 法律、伦理与数据使用边界这是所有数据爬取者必须严肃对待的一课。爬取公开数据本身可能不违法但行为方式和使用目的可能触碰红线。遵守Robots协议访问https://www.eastmoney.com/robots.txt查看网站允许或禁止爬虫访问的路径。尊重这些规则是基本的网络礼仪。控制访问频率这是最重要的伦理准则。你的爬虫不应该对目标网站的正常运营造成任何可感知的影响如服务器负载显著升高。这既是技术上的自我保护避免被封也是道德上的要求。明确数据用途爬取的数据应用于个人学习、研究或非商业用途。绝对禁止将数据用于非法活动、商业倒卖或任何可能损害东方财富网及其用户利益的行为。关注用户协议仔细阅读网站的用户协议其中可能对数据抓取和使用有明确条款。违反协议可能导致法律风险。保护个人隐私如果爬取过程中意外接触到非公开的个人信息这在财经网站不常见但在股吧等UGC区域需留意应立即停止并删除相关数据。我的个人原则是只取所需慢速渐进心存敬畏。技术是中立的但使用技术的人需要为自己的行为负责。通过这个项目我们锻炼的是技术能力但时刻不能忘记法律和伦理的框架。7. 从数据到价值简单的分析示例爬取数据不是终点让数据产生洞察才是目的。这里举一个最简单的例子计算一批股票最近一段时间的平均日收益率和波动率。假设我们已经爬取并清洗好了多只股票的历史日K线数据并存储在一个Pandas DataFramedf_history中包含code股票代码、date日期、close收盘价等字段。import numpy as np # 计算每日收益率 df_history[‘return’] df_history.groupby(‘code’)[‘close’].pct_change() # 按股票代码分组计算平均收益率和收益率的标准差波动率 summary df_history.groupby(‘code’)[‘return’].agg([‘mean’ ‘std’]) summary.columns [‘avg_daily_return’ ‘daily_volatility’] # 年化处理假设一年有242个交易日 summary[‘annualized_return’] summary[‘avg_daily_return’] * 242 summary[‘annualized_volatility’] summary[‘daily_volatility’] * np.sqrt(242) print(summary.sort_values(by‘annualized_return’ ascendingFalse).head(10))这个简单的分析可以帮你快速筛选出在一段时间内表现强势且波动特征各异的股票作为进一步深度研究的基础。数据的价值正是在于通过这样的加工和分析从杂乱的信息中提炼出有意义的信号。整个爬取东方财富网数据的项目从环境搭建到数据应用是一个完整的闭环。它考验的不仅是Python编程和HTML解析能力更是对网络协议、数据清洗、系统架构甚至法律伦理的综合理解。希望这份详细的笔记能为你打开金融数据获取的大门并在实践中少走一些我曾经走过的弯路。记住稳健的代码和审慎的态度是让项目长期运行下去的关键。