尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python爬虫实战:从东方财富网高效获取金融数据的完整方案

Python爬虫实战:从东方财富网高效获取金融数据的完整方案 1. 项目缘起与核心价值最近在做一个金融数据分析的小项目需要用到A股上市公司的基本面数据、行情数据以及一些市场情绪指标。市面上的数据接口要么收费不菲要么数据维度不全对于个人开发者或者小团队来说成本压力不小。这时候像东方财富网这样的财经门户就成了一个重要的数据源。它页面结构相对稳定数据公开且全面从实时行情、历史K线到财务报告、股东持股几乎涵盖了个人研究所需的大部分信息。因此动手写一个针对东方财富网的爬虫把数据规整地抓取下来就成了一个非常实际的需求。这篇笔记就是记录我在这个过程中的思路、技术选型、踩过的坑以及最终沉淀下来的可复用方案。无论你是想学习Python爬虫还是和我一样有实际的金融数据需求希望这篇超过5000字的详细拆解能给你带来直接的帮助。爬虫听起来神秘但核心逻辑就是模拟浏览器行为从网页的HTML代码中提取我们需要的信息。东方财富网虽然没有提供官方的API但其网页为了前端展示必然包含了这些数据我们的任务就是找到它们并“挖”出来。这个过程会涉及到请求库的选择、反爬策略的应对、数据解析的精度以及数据存储的考量。接下来我会按照一个完整的爬虫项目流程从环境准备、页面分析、代码实现到数据存储和优化一步步拆解说明。2. 环境准备与工具选型工欲善其事必先利其器。在开始写代码之前搭建一个顺手且高效的环境至关重要。这里的工具选型直接决定了后续开发的效率和爬虫的稳定性。2.1 核心Python库Python是爬虫领域的首选语言生态丰富库函数强大。对于这个项目我们需要以下几个核心库Requests: 这是处理HTTP请求的基石库。它比Python内置的urllib更简洁易用功能也更强大。我们将用它来向东方财富网的服务器发送请求获取网页的HTML源代码。BeautifulSoup4: 获取到HTML源码后我们需要从中解析出特定的数据。BeautifulSoup简称bs4是一个优秀的HTML/XML解析库它能够帮助我们以非常直观的方式通过标签名、CSS类名等属性来定位和提取数据。对于东方财富网这种结构清晰的页面bs4通常是首选。Pandas: 我们的目标是获取数据并进行分析Pandas是数据处理和分析的不二之选。它可以将爬取到的结构化数据比如列表、字典轻松转换为DataFrame方便进行清洗、计算和保存为CSV或Excel文件。lxml: 这是BeautifulSoup可以选用的一个解析器它的解析速度比Python标准库中的html.parser要快很多。在处理大量页面时这个速度优势会非常明显。安装这些库非常简单只需在命令行中执行pip install requests beautifulsoup4 pandas lxml2.2 辅助与备选工具除了核心库还有一些工具在特定场景下会发挥巨大作用Selenium: 如果目标数据是通过JavaScript动态加载的即打开网页源代码看不到数据但浏览器里能看见那么就需要用到Selenium。它可以模拟真实浏览器操作等待JS执行完毕后再获取完整的页面内容。东方财富网的部分页面如某些实时资金流可能涉及动态加载但经过我实测其核心数据页面如个股行情页、财务报告页大多是静态或半静态的初期可以不用Selenium以提升效率。但把它作为技术储备是必要的。Fiddler/Charles: 这些是网络抓包工具。当页面数据来自额外的Ajax请求XHR时直接请求网页HTML是拿不到数据的。这时需要用抓包工具分析浏览器和服务器之间的所有通信找到真正返回数据的那个请求接口然后用Requests去模拟这个接口请求。这是应对动态网站更高效的方法。Redis: 当爬虫规模扩大需要分布式调度、请求去重或作为缓存时Redis这样的内存数据库就派上用场了。对于初学者或小规模爬取可以暂不考虑。注意工具的选择遵循“按需引入”原则。一开始尽量用最简单的方案Requests BeautifulSoup解决问题遇到瓶颈时再寻求更强大的工具如Selenium或分析Ajax接口。这能保持代码的简洁和爬取的高效。3. 目标分析与页面结构拆解在写第一行代码之前我们必须先弄清楚要去哪里、拿什么、怎么拿。盲目地开始写爬虫只会事倍功半。3.1 明确数据目标东方财富网数据庞杂我们需要明确具体目标。例如我本次爬取的目标主要包括个股基本信息股票代码、名称、所属行业、地区、上市日期等。这些通常在个股概览页。历史行情数据日K线数据包括日期、开盘价、最高价、最低价、收盘价、成交量、成交额。这是技术分析的基础。财务数据利润表、资产负债表、现金流量表的关键指标如营业收入、净利润、资产负债率等。通常按季度或年度报告呈现。公司公告列表公告标题、发布日期、链接等。我们需要为每一类数据找到其对应的URL网页地址和页面结构。3.2 分析页面与寻找数据规律以获取“贵州茅台600519”的历史日K线数据为例。找到目标页面在东方财富网搜索“600519”进入其个股页面。通常历史K线数据有一个独立的页面或通过Ajax加载。查看网页源代码在浏览器中以Chrome为例在K线图页面右键点击选择“检查”或按F12打开开发者工具。切换到“Network”网络选项卡然后刷新页面。在所有的网络请求中寻找包含“kline”或“data”字样的请求特别是类型为XHR或JS的请求。这很可能就是真正提供K线数据的接口。分析请求参数点击找到的疑似数据请求查看其“Headers”标头。重点看Request URL请求地址和Query String Parameters查询参数。你会发现请求的URL可能像http://push2his.eastmoney.com/api/qt/stock/kline/get?这样后面跟着一长串参数如secid股票代码格式如1.6005191代表沪市0代表深市、kltK线周期101为日线、fqt复权类型1为前复权等。查看响应数据在“Response”响应选项卡中可以看到服务器返回的数据。幸运的是东方财富网的很多数据接口返回的是JSON格式这是一种结构化的数据格式比从HTML里解析要方便和精确得多JSON数据可以直接被Python的json库解析成字典或列表。实操心得对于东方财富网优先寻找并分析其Ajax接口返回JSON格式这比解析HTML要稳定和高效得多。HTML结构可能会因前端改版而变化而数据接口相对稳定。如何找到接口关键在于耐心观察“网络”请求并尝试理解每个参数的意义。你可以先手动在浏览器中操作一遍用开发者工具记录下整个过程。4. 核心爬取流程与代码实现掌握了目标地址和数据接口后我们就可以开始编写爬虫的核心逻辑了。这里我将以获取个股日K线数据通过Ajax接口和从HTML页面解析公司公告列表为例展示两种最常见的数据抓取方式。4.1 方式一请求JSON接口获取K线数据这是最推荐的方式因为数据干净、结构化程度高。import requests import pandas as pd import json from datetime import datetime def fetch_kline_data(stock_code, start_date20230101, end_date20231231, klt101): 获取个股历史K线数据日线 :param stock_code: 股票代码如 600519 :param start_date: 开始日期格式 20230101 :param end_date: 结束日期格式 20231231 :param klt: K线周期101日线102周线103月线 :return: 包含K线数据的Pandas DataFrame # 构造请求URL和参数 # 东方财富网的secid规则沪市为1.代码深市为0.代码 market 1 if stock_code.startswith((6, 9)) else 0 # 简单判断6/9开头通常为沪市 secid f{market}.{stock_code} url http://push2his.eastmoney.com/api/qt/stock/kline/get params { secid: secid, ut: fa5fd1943c7b386f172d6893dbfba10b, # 固定参数可不变 fields1: f1,f2,f3,f4,f5,f6, fields2: f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61, klt: str(klt), fqt: 1, # 1为前复权 beg: start_date, end: end_date, smplmt: 10000, # 貌似无效实际返回数据量由beg和end决定 lmt: 100000, # 单次返回最大数据条数 _: str(int(datetime.now().timestamp() * 1000)) # 时间戳防缓存 } 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 } try: response requests.get(url, paramsparams, headersheaders, timeout10) response.raise_for_status() # 检查请求是否成功 data_json response.json() # 解析JSON数据 kline_data data_json.get(data, {}).get(klines, []) if not kline_data: print(f未获取到股票 {stock_code} 的数据) return pd.DataFrame() # 数据转换 data_list [] for item in kline_data: # 每条数据是一个用逗号分隔的字符串 values item.split(,) # 字段顺序日期,开盘,收盘,最高,最低,成交量,成交额,振幅,涨跌幅,涨跌额,换手率 data_list.append({ date: values[0], open: float(values[1]), close: float(values[2]), high: float(values[3]), low: float(values[4]), volume: float(values[5]), # 成交量手 amount: float(values[6]), # 成交额元 amplitude: float(values[7].rstrip(%)), # 振幅去掉百分号 pct_chg: float(values[8].rstrip(%)), # 涨跌幅 change: float(values[9]), # 涨跌额 turnover: float(values[10].rstrip(%)) if len(values) 10 else None # 换手率 }) df pd.DataFrame(data_list) # 将日期列转换为datetime类型方便后续处理 df[date] pd.to_datetime(df[date]) # 按日期排序 df.sort_values(date, inplaceTrue) df.reset_index(dropTrue, inplaceTrue) print(f成功获取股票 {stock_code} 从 {df[date].min().date()} 到 {df[date].max().date()} 共 {len(df)} 条日线数据) return df except requests.exceptions.RequestException as e: print(f网络请求出错: {e}) return pd.DataFrame() except json.JSONDecodeError as e: print(fJSON解析出错: {e}) return pd.DataFrame() except KeyError as e: print(f数据格式异常未找到预期字段: {e}) return pd.DataFrame() # 使用示例 if __name__ __main__: df_kline fetch_kline_data(600519, start_date20240101, end_date20240601) if not df_kline.empty: print(df_kline.head()) # 保存到CSV文件 df_kline.to_csv(600519_kline.csv, indexFalse, encodingutf-8-sig)代码解析与注意事项请求头User-Agent模拟浏览器访问是绕过基础反爬的第一步。这里的User-Agent是一个常见的Chrome浏览器标识。参数构造secid、beg、end等参数需要根据你的需求正确构造。_参数是一个时间戳用于防止缓存通常可以直接生成。错误处理使用try...except包裹网络请求和数据处理过程至关重要。网络不稳定、接口变更、数据格式错误都可能导致程序崩溃良好的错误处理能让爬虫更健壮。数据解析接口返回的klines字段是一个列表列表中的每个元素是一个用逗号分隔的字符串。我们需要按约定的字段顺序进行拆分和类型转换。数据存储这里演示了保存为CSV。对于大量数据或频繁更新可以考虑存入数据库如SQLite、MySQL。4.2 方式二解析HTML页面获取公告列表并非所有数据都有友好的JSON接口。例如公司公告列表通常以HTML表格形式呈现这时就需要用BeautifulSoup来解析。import requests from bs4 import BeautifulSoup import pandas as pd import time def fetch_announcement_list(stock_code, page1): 获取个股公告列表第一页 :param stock_code: 股票代码 :param page: 页码 :return: 公告列表的DataFrame url fhttp://guba.eastmoney.com/list,{stock_code},2,f_{page}.html headers { User-Agent: Mozilla/5.0 ... # 同上需填写完整的User-Agent } try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding utf-8 # 设置编码防止乱码 soup BeautifulSoup(resp.text, lxml) # 寻找公告表格。需要实际查看网页结构这里是一个示例选择器。 # 使用浏览器的“检查”功能找到公告列表所在容器的标签和类名。 announcement_table soup.find(div, idmainlist) # 这只是示例实际ID可能不同 if not announcement_table: print(未找到公告列表容器) return pd.DataFrame() announcements [] # 假设每条公告在一个类名为‘articleh’的div里 for item in announcement_table.find_all(div, class_articleh): try: # 同样需要根据实际HTML结构来定位元素 title_elem item.find(span, class_l3) date_elem item.find(span, class_l5) if title_elem and date_elem: title title_elem.text.strip() link title_elem.a[href] if title_elem.a else # 构造完整链接 full_link fhttp://guba.eastmoney.com{link} if link.startswith(/) else link date date_elem.text.strip() announcements.append({ title: title, date: date, link: full_link }) except AttributeError as e: # 某条公告解析出错跳过 continue df pd.DataFrame(announcements) print(f从第{page}页解析到{len(df)}条公告) return df except Exception as e: print(f获取公告列表出错: {e}) return pd.DataFrame() # 使用示例 if __name__ __main__: # 注意东方财富的股吧页面和公告页面结构可能不同此代码为示例逻辑需根据实际页面调整选择器。 df_ann fetch_announcement_list(600519) if not df_ann.empty: print(df_ann.head()) # 可以进一步遍历df_ann中的link去抓取每篇公告的详细内容重要提示HTML解析的代码高度依赖于网页的具体结构。东方财富网的页面结构可能会调整因此find方法中使用的标签名和类名如‘div’, id‘mainlist’必须通过实时分析目标页面来确定。上述代码中的选择器仅为示例无法直接运行。你需要使用浏览器的开发者工具右键点击公告标题或日期选择“检查”来查看其在外层HTML中的准确路径和属性。5. 反爬策略应对与伦理规范爬虫与反爬虫是永恒的博弈。东方财富网作为大型网站肯定有相应的反爬机制。我们的原则是友好、低调、遵守规则。5.1 常见反爬措施与应对请求头Headers检查这是最基本的。务必设置合理的User-Agent、Referer来源页等。可以准备一个列表轮流使用。user_agents [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..., Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 ..., # ... 更多浏览器UA ] import random headers[User-Agent] random.choice(user_agents)请求频率限制过于频繁的请求会导致IP被暂时或永久封禁。必须设置延迟。import time time.sleep(random.uniform(1, 3)) # 在每次请求后随机休眠1-3秒对于大批量爬取延迟是必须的。这既是对目标网站服务器的尊重也是保证自己IP安全的必要措施。IP封禁如果单个IP请求过于频繁即使有延迟也可能被封。解决方案包括使用代理IP池购买或获取一些高质量的HTTP/HTTPS代理IP在请求时随机切换。这是应对IP封禁最有效的方法但需要成本。降低请求速度这是成本最低的方法将休眠时间加长比如time.sleep(5)。验证码当行为被判定为异常时可能会弹出验证码。对于个人小规模爬取尽量通过控制频率来避免触发验证码。如果遇到可以考虑手动处理在代码中暂停提示用户手动输入。使用打码平台商业项目考虑。数据加密/混淆部分数据可能被加密或动态生成。这时需要仔细分析前端JavaScript代码找到解密逻辑并用Python重现难度较高或者直接使用Selenium这类浏览器自动化工具。5.2 爬虫伦理与法律风险务必牢记以下几点查看robots.txt访问https://www.eastmoney.com/robots.txt查看网站允许或禁止爬虫访问的路径。虽然这不是法律文件但遵守它是行业惯例和尊重的表现。限制爬取速度这是最重要的道德准则。不要对网站服务器造成压力。明确数据用途爬取的数据应用于个人学习、研究或非商业用途。严禁用于商业倒卖、恶意竞争或其他非法活动。尊重版权网站上的文章、评论等内容通常有版权批量爬取并发布可能侵权。避免爬取个人隐私信息这是法律红线。个人建议对于东方财富网的数据用于个人量化策略研究或学术分析是常见的合理使用场景。保持低调、控制频率、注明数据来源是长期稳定获取数据的前提。6. 数据存储、调度与工程化思考当爬虫脚本可以稳定运行后我们需要考虑如何系统地管理爬取任务和数据。6.1 数据存储方案选择CSV/Excel文件适用于数据量小、一次性爬取的场景。使用Pandas的to_csv或to_excel方法即可。优点是简单直观无需搭建数据库环境。SQLite数据库Python内置支持无需安装额外服务。将数据存入SQLite表便于查询和管理。适合中等数据量、单机运行的爬虫。import sqlite3 conn sqlite3.connect(eastmoney_data.db) df_kline.to_sql(stock_kline, conn, if_existsappend, indexFalse) # 追加模式 conn.close()MySQL/PostgreSQL数据库适用于数据量大、需要多机访问或长期持久化的项目。需要单独安装数据库服务。MongoDB数据库如果爬取的数据结构不固定如不同页面的字段差异很大文档型数据库MongoDB会更灵活。6.2 任务调度与增量更新我们不可能每次都全量爬取所有历史数据。增量爬取对于行情数据可以记录已爬取的最新日期下次只爬取该日期之后的新数据。这需要你的存储方案支持按条件查询最新记录。定时任务使用系统的定时任务工具如Linux的cron、Windows的“任务计划程序”或Python库如schedule、APScheduler来定期执行爬虫脚本。例如每天收盘后自动爬取当天的行情数据。任务队列对于需要爬取成百上千只股票的场景可以将股票代码列表放入一个队列如Redis的List由爬虫程序逐个消费实现简单的分布式或并发控制。6.3 日志记录与监控一个健壮的爬虫必须有日志。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(spider.log), logging.StreamHandler()]) logger logging.getLogger(__name__) try: # ... 爬取代码 logger.info(f成功爬取股票{stock_code}的数据共{len(df)}条。) except Exception as e: logger.error(f爬取股票{stock_code}时发生错误: {e}, exc_infoTrue)日志能帮助你在爬虫无声无息失败时快速定位问题所在。7. 实战避坑指南与问题排查这部分是我在爬取过程中真实遇到的“坑”和解决方案比任何教程都宝贵。7.1 常见问题速查表问题现象可能原因排查步骤与解决方案返回状态码403IP或请求头被识别为爬虫1. 检查并完善User-Agent,Referer等请求头。2. 添加随机延迟。3. 考虑使用代理IP。返回状态码404请求的URL或参数错误1. 用浏览器访问相同URL确认地址有效。2. 检查参数构造逻辑特别是secid市场.代码的格式。返回数据为空或None接口已变更或参数不正确1. 用浏览器开发者工具重新抓包核对最新的请求参数。2. 打印出完整的请求URL在浏览器中手动测试。解析HTML时找不到标签网页结构已更新1. 使用print(soup.prettify())或保存HTML到文件查看实际结构。2. 重新使用浏览器“检查”功能更新代码中的选择器。数据乱码编码问题1. 设置response.encoding utf-8或gbk等。2. 查看响应头中的Content-Type。被封IP所有请求超时IP被目标网站封禁1. 暂停爬虫等待一段时间可能几小时到一天。2. 更换网络环境如切换手机热点。3.必须使用代理IP池。JSONDecodeError返回内容不是JSON1. 打印response.text的前500字符看是否是HTML错误页面如验证码页面。2. 检查请求是否被重定向。7.2 独家心得与技巧先手动后自动在写爬虫代码前一定要在浏览器中手动完成一次完整的数据获取流程并用开发者工具记录下所有关键请求。这能帮你理解数据流向减少盲目编码。参数化与模块化将股票代码、起止日期等作为函数参数。将网络请求、数据解析、错误处理、数据存储写成独立的函数或类。这样代码清晰也便于维护和复用。比如可以创建一个EastMoneySpider类。使用Session对于需要维持会话如登录后爬取的场景使用requests.Session()。它会自动管理cookies比单次请求更高效。session requests.Session() session.headers.update({User-Agent: ...}) resp session.get(url)处理分页列表数据通常分页显示。分析分页URL的规律如f_2.html代表第二页用循环遍历所有页面。务必在每页之间添加延迟。数据验证爬下来的数据可能包含‘-’、‘--’表示空值或格式错误的字符串。在转换数据类型如float()前一定要做好清洗和异常捕获。尊重网站设定边界明确你的数据需求范围不要无差别地全站爬取。设定合理的每日/每小时爬取上限。在代码中主动遵守robots.txt的规则。爬取东方财富网数据是一个典型的“数据分析前置”工程。它不仅仅是一段脚本更是一套包含目标分析、工具使用、编码实现、策略应对和系统管理的完整解决方案。从最简单的单页面抓取开始逐步应对反爬、完善调度、规范存储这个过程本身就是对工程能力的很好锻炼。希望这份详细的笔记能为你扫清障碍成功获取到你所需的数据。记住耐心和细致是爬虫工程师最重要的品质。如果在实际操作中遇到新的问题不妨再回头看看网络抓包和页面结构分析这两个基础步骤它们往往是解决问题的钥匙。
返回列表