1. 为什么你需要一个自己的A股数据源?
做量化分析、写策略回测,或者只是想更深入地研究一下市场,你是不是经常遇到这样的问题:网上找的数据要么不全,要么更新不及时,要么格式乱七八糟,还得手动整理。我之前也这样,到处找免费的数据源,结果发现要么接口不稳定,要么字段对不上,折腾半天数据还没到手,策略的想法都快凉了。
后来我琢磨,最靠谱的办法还是自己动手。直接从像东方财富网这样的主流财经网站抓取数据,然后存到自己的MySQL数据库里。这样一来,数据完全在你的掌控之中,想什么时候更新就什么时候更新,想怎么分析就怎么分析,自由度直接拉满。而且,这个过程本身就是一个绝佳的Python爬虫实战项目,能让你把网络请求、数据处理、数据库操作这些技能点一次性全练了。
我这次要带你做的,就是构建一个稳定、高效、可复用的A股基础数据采集系统。我们以获取“最新股东户数变化”这个数据维度为例,因为它包含了股票代码、名称、价格、市值、股东变化等核心信息,非常具有代表性。整个流程我会掰开揉碎了讲,从分析网站请求到写出健壮的爬虫代码,再到设计合理的数据库表结构并完成存储。哪怕你之前没怎么写过爬虫,跟着走一遍也能搞定。
2. 实战第一步:像侦探一样分析目标网站
很多新手一上来就急着写代码,结果往往卡在第一步:找不到数据在哪。我的经验是,花在分析上的时间,能帮你省掉后面一大半的调试时间。我们这次的目标是东方财富网的数据中心。
2.1 找到数据的大门——API接口
现代网站的数据大多通过API接口动态加载,而不是直接写在网页HTML里。所以,我们的核心任务是找到这个“后门”。
- 打开浏览器开发者工具:以Chrome为例,在东方财富官网相关页面(比如数据中心-股东户数)按
F12。 - 切换到“网络”(Network)标签:然后刷新页面,你会看到一大堆请求。
- 寻找数据请求:在纷杂的请求中,我们要找的是类型(
Type)为fetch或xhr的请求,它们通常是数据接口。仔细查看它们的“标头”(Headers)和“预览”(Preview),寻找包含我们想要数据(比如股票列表、股东信息)的请求。
经过一番查找,我发现了关键接口:https://datacenter-web.eastmoney.com/api/data/v1/get。这个接口通过URL后面携带的一系列参数,来告诉服务器我们需要什么数据。这就好比你去餐厅点菜,URL是餐厅地址,参数就是你写的菜单。
2.2 破解“点菜单”——理解请求参数
点击找到的那个请求,查看它的“载荷”(Payload)或“查询字符串参数”(Query String Parameters),你会看到一长串像 sortColumns、pageSize、reportName 这样的键值对。这些就是我们的“菜单”。
我帮你把几个关键参数翻译一下:
reportName: RPT_HOLDERNUMLATEST: 这是“菜名”,告诉服务器我们要“最新股东户数”这份数据。pageSize: 500和pageNumber: 1: 这是“分餐制”,一次上500条数据,这是第一页。columns: SECURITY_CODE,SECURITY_NAME_ABBR...: 这是指定要菜的哪些“配料”,比如股票代码、股票简称、股东户数等。sortColumns: HOLD_NOTICE_DATE,SECURITY_CODE和sortTypes: -1,-1: 这是“上菜顺序”,按公告日期和股票代码降序排列。
理解这些参数至关重要,因为我们的Python代码就是要完美地模拟这个“点菜”过程。你可以尝试在开发者工具里修改这些参数(比如把pageNumber改成2),然后重新发起请求,看看返回的数据是否变化,这能加深你的理解。
3. 构建稳健的爬虫核心代码
分析清楚了,接下来就是用Python把这个过程自动化。直接上代码可能会让人懵,我先给你讲讲我的设计思路。
3.1 打造一个“抗压”的请求会话
网络请求最怕两件事:一是网站把你当机器人封了,二是网络不稳定超时了。所以,我们不能用简单的 requests.get,得做些加固。
import requests
from typing import List
import pandas as pd
import json
class CustomedSession(requests.Session):
"""自定义会话,设置默认超时时间"""
def request(self, *args, **kwargs):
kwargs.setdefault('timeout', 60) # 默认60秒超时
return super().request(*args, **kwargs)
# 创建一个全局会话,并配置连接池
MAX_CONNECTIONS = 50
session = CustomedSession()
adapter = requests.adapters.HTTPAdapter(
pool_connections=MAX_CONNECTIONS,
pool_maxsize=MAX_CONNECTIONS,
max_retries=5 # 请求失败重试5次
)
session.mount('http://', adapter)
session.mount('https://', adapter)
我创建了一个 CustomedSession 类,继承自 requests.Session。这样做的好


1506

被折叠的 条评论
为什么被折叠?



