☰
A股股票基础信息抓取与数据标准化:从全量清单到RAR快照
2026/10/15 11:23:42 网站建设 项目流程

简介:压缩包收录了沪深两市三千余只A股股票的基础信息,涵盖股票代码、名称、所属市场与板块、行业分类、公司基本信息、核心财务数据、市值、市盈率、市净率、流通股本及分红派息等字段,可作为投资者进行个股筛选、行业对比和财务健康度评估的底层数据参考。压缩包内共四十八个文件,主要包含XML数据文件、C#工程源码、DLL动态库、配置文件以及可执行程序,并附有说明文档与少量图片,整体大小仅5.2MB,便于快速下载与离线解析。目前已有1722人学习使用,适合具有一定数据处理基础的分析师或量化入门者。借助包内数据,可复用Python Pandas等工具完成数据清洗、统计分析与可视化,进而挖掘估值与成长性指标;也可作为开发股票信息查询或分析系统的数据源。需要注意的是,基础信息属于静态历史数据,实际投资仍需结合宏观经济、政策变化及市场情绪综合判断。

1. 这份rar背后,是一个被低估的数据工程问题

第一次拿到《获取所有A股股票基础信息.rar》这类打包数据时,我先入为主地以为解压就能直接用。结果打开一看,满屏都是最新价、涨跌幅这类行情字段,真正关心的行业、上市日期、流通股本全散在另外几个文件里,编码还不对。做量化或财报分析的人都懂:最耗精力的不是策略本身,而是把全市场五千多家A股的股票代码、简称、行业、上市时间、股本结构这类基础字段一次性洗干净存下来。这份基础信息包要解决的正是这个起点问题——它不是给你选股结论,而是给你一张能放心做二次加工的全市场快照。适合谁?想跳过逐个查行情页面的重复劳动、需要一份本地全量清单做筛选池或入库开发的从业者。

2. 数据从哪来:三条路线怎么选

2.1 交易所披露、数据接口与终端导出的取舍

获取A股基础信息,常见的做法是三条路线里选一条走。第一条路,直接抓取交易所公开披露的挂牌公司名单和公司概况公告。这条路最权威,但公开内容形态太散,有PDF、有网页、有不同命名的附件,解析成本很高,更新节奏也跟不上你要做快照的时点。第二条路,对接某数据服务商的批量接口。服务商把公开信息整理成结构化JSON返回,一次分页请求能拿几百条记录,字段从股票代码到实控人都有,是实际工作中最常走的路线。免费的额度和频率做每日全量快照足够,付费版本主要多出的是分钟级频率和更多衍生指标,而不是基础字段本身。第三条路,用行情终端手动导出。在软件里选好板块,右键导出CSV或Excel,体感最快,但导出列数有限,也没法做到定时自动拉取,适合临时用一次,不适合长期维护。

路线权威性字段完整度自动化程度初始成本适用场景
交易所公开披露最高字段分散,需自行整合低,需解析多种格式人力成本高做数据源交叉校验
数据服务商接口较高字段宽,结构化返回高,脚本定时拉取注册即可用自动化构建快照
行情终端导出一般受终端字段限制低,手动操作需安装终端一次性临时提取

这三条路线我建议按「接口为主、披露文件为辅」来搭配。主力数据源负责产出全部基础字段,交易所披露文件只在怀疑某个字段对不上时用来人工核查。这样既不用把解析PDF的苦活全揽下来,又能保证口径出问题时你有地方追根溯源。

2.2 为什么基础信息用快照而不是数据库

我一般不会为这些基础字段直接搭一套数据库。原因有三点。第一,数据变更频率是公告级的,一家公司改行业、改股本,一年里也没几次,没必要用支持毫秒写入的系统去维护一份每天变不了几十条的表。第二,rar包本质是一份全量快照,放网盘、放NAS、发同事都方便,解压即用,不依赖内网数据库连接。对于一个后续要拿去写策略或喂给建模流程的文件,降低使用门槛是第一位的。第三,数据库方案要求你搭建同步任务、处理主键冲突、管理连接池,这些工作量对基础信息这种低频数据来说严重超配。

但这不意味着永远不碰数据库。更合理的做法是:采集端只产出快照文件,谁要谁自己导走,入库是下游使用者的行为。团队里一旦有人说要把基础信息做成接口服务,直接从快照文件反向灌库就行,不需要在采集端维护两份存储。这个取舍直接决定了你交付的rar包能不能成为团队内部的通用数据资产。

2.3 动手前先定字段,别抓到什么存什么

接口返回的JSON嵌套很深,同一个字段在不同接口里含义可能完全不同,抓到什么存什么是标准化翻车的第一来源。动手前我先落一张字段清单表,把每个字段的业务含义固定下来,之后再写代码就只是按单子取数,不会边写边改。

字段名业务含义数据类型说明
code股票代码str,6位唯一主键,需补齐前导零
name中文简称str不含N、C等临时前缀
name_en英文简称str缺失时可空
industry所属行业str以数据源分类为准,注明版本
area注册地省份str缺失时置空
list_date上市日期date统一成YYYY-MM-DD
total_share总股本(万股)float关注数值单位
float_share流通股本(万股)float不等于总股本减限售股
total_mv_yi总市值(亿元)float收盘口径
float_mv_yi流通市值(亿元)float收盘口径
controller实际控制人str缺失率高,不插补
exchange所属板块str沪市主板/深市主板/科创板等

这张表里真正决定基础信息可用性的其实是后三行。实际控制人字段缺失率可能超过四成,早期版本里我直接删掉,后来做舆情分析需要穿透股权关系时才发现缺了它很麻烦,所以现在保留字段但允许空值。总市值和流通市值这类派生指标,每个数据源算法都有一点差异,包里只存源端原始值,不做二次加工,否则对账时说不清口径。

3. 拉全量清单到打包rar:一套可照抄的流程

3.1 拉取全量证券清单

把全市场股票基础清单拿到手,本质是一次分页拉取。下面是这套流程里最核心的一段代码,按数据服务商常见的JSON返回格式写成,接口地址替换成你自己的数据源即可。

import requests import pandas as pd import time def fetch_stock_list(api_url, page, page_size): # 常见的批量数据接口为POST,用JSON传分页参数 payload = { "page": page, "pageSize": page_size, "querys": {"market": "all"} } resp = requests.post(api_url, json=payload, timeout=30) # 约定code为0表示业务正常,部分接口返回HTTP状态码非200 body = resp.json() if body.get("code") != 0: raise RuntimeError(f"接口异常:{body.get('msg')}") return body["data"]["list"] all_rows = [] for page in range(1, 50): items = fetch_stock_list("https://api.example.com/market/list", page, 500) if not items: break all_rows.extend(items) time.sleep(0.2) # 每页间隔200ms,避免对接口造成访问压力 df_stock = pd.DataFrame(all_rows) print(f"全量证券清单:{len(df_stock)} 条") df_stock.to_csv("stock_list_raw.csv", index=False)

这段代码的核心逻辑是循环取页直到接口返回空列表,循环条件没有写死页数上限,未来全市场上市公司数量增加时不会漏数据。time.sleep放在页面级而不是请求级,200ms间隔在性能和频率限制之间取一个平衡值。pageSize取500,单次响应体控制在1MB左右的常见量级;timeout设30秒,避免弱网环境下请求挂死。接口返回的业务码不一定叫code,有些叫status或者success,写代码前先用一条curl确认返回结构,这是最省事的习惯。

注意分批拉取后先看一眼列表里混进了什么。正常的全量接口往往不止返回A股股票,还有可转债、ETF、优先股、REITs等品种,这些都是后续标准化的隐患。此时先不急着过滤,保留原始表,过滤动作放到清洗阶段统一做。

3.2 逐个补齐公司和股本资料

全量清单里通常只有代码、简称、所属市场这类基础字段,行业、上市日期、股本结构还要对每个代码单独调一次信息接口。这块的逻辑就是一个for循环套一个接口调用,没有太多花活,但有两个细节值得注意。

import requests import pandas as pd import time def fetch_stock_detail(code, api_url): # 个股资料接口通常只接受单代码查询 params = {"code": code} resp = requests.get(api_url, params=params, timeout=10) body = resp.json() if body.get("code") != 0: return None detail = body.get("data", {}).get("detail") if detail is None: return None detail["code"] = code return detail code_list = df_stock["code"].tolist() detail_rows = [] for code in code_list: detail = fetch_stock_detail("https://api.example.com/stock/detail", code) if detail is not None: detail_rows.append(detail) time.sleep(0.1) # 单代码接口限频更严格,间隔收紧到100ms df_detail = pd.DataFrame(detail_rows) df_merged = df_stock.merge(df_detail, on="code", how="left", validate="one_to_one") df_merged.to_csv("stock_basic_info_tmp.csv", index=False)

merge时指定validate="one_to_one"是防止数据源侧返回重复行造成记录数膨胀,一旦接口异常出现重复数据,pandas会直接抛错而不是悄悄把行数翻倍。这个报错本身就是在提醒你:先检查数据源,不要带着重复数据继续做。time.sleep放在请求之后而不是之前,保证即使某个请求超时抛异常,循环退出后也有个缓冲间隔。接口里返回的detail,常见字段包括总股本、流通股本、上市日期、注册资本,不同数据源的字段命名差异很大,在这里统一改好名字,后续清洗阶段就不用手忙脚乱。

还有一类接口会把注册资本和总股本混在一个字段里返回,如果看到数值明显大于总股本了十倍以上,基本就是拿注册资本当股本用的脏数据,需要在合并前按业务常识预筛选。

3.3 加时间戳、分离板块和文件落盘

数据合并完成后,下一步不是急着打包,而是给数据加上生成日期标识并确定落盘目录。文件名里带日期是这份rar包能否长期使用的分水岭,不带日期的快照文件三个月后再看就是一坨无法回溯的混沌状态。

from datetime import datetime today = datetime.now().strftime("%Y%m%d") out_path = f"output/stock_basic_info_{today}.csv" df_merged.to_csv(out_path, index=False, encoding="utf-8-sig") # 按板块分组统计数量,便于解压后先核对 print(df_merged.groupby("exchange").size())

CSV编码统一用utf-8-sig,这样Windows上Excel双击打开不会乱码,Python读入时也不需要额外处理BOM。按板块分组统计数量是个很实用的小动作,解压后肉眼核对第一道关卡就是看每个板块的数量是否在合理区间,比如沪市主板一千八九百家、创业板一千三四百家,偏差太大说明拉取过程出了问题,不用等入库后才发现。

打包这一步用rar命令行完成,比在脚本里调用压缩库要直接得多:

cd output rar a stock_basic_info.rar stock_basic_info_20250115.csv

习惯上我会在output目录里预留一个老快照目录,保留最近三个版本的历史快照而不是用新文件覆盖旧的。这样一旦发现数据源某天返回了异常字段,至少能回退到上一份可用的包。rar命令行在服务器上一般需要单独安装,安装好之后把路径加进环境变量,打包命令就能在任意目录执行。如果你的服务器上没有rar工具,先用zip打包过渡也可以,但交付给同事时Windows的rar工具链兼容性更好,这也是这份数据包选择rar格式的实际原因。

4. 字段口径和标准化:决定这份基础信息能不能用

4.1 全市场代码与板块的对应规则

A股股票代码的板块归属有一套相对稳定的规则,但每年都有新代码段启用,只靠口诀记代码不加校验代码,早晚会翻车。

代码段所属板块常见特征
600/601/603/605开头沪市主板存量老股为主
688开头科创板2020年后集中上市
000/001/002/003开头深市主板含原中小板合并标的
300/301开头创业板注册制后扩容明显
8开头、4开头、920开头北交所原新三板精选层平移
900、200开头B股用外币交易,A股包内不含

清洗时不能看到6位数字就放行。B股代码虽然也是6位,但它不在A股基础信息包的范围内,需要显式剔除;可转债代码11开头、ETF代码15开头,这些非股票品种也会有行情数据,同样要过滤。硬编码板块规则表也许是笨办法,但在没有官方统一代码表可下载的现实条件下,这张表就是最可靠的标准。遇到代码段规则之外的新代码,先查上市板块名单,不要一律放行,也不要一律丢弃,单独建一个unknown清单人工过一遍。

4.2 名称、市值和上市日期的清洗

名称清洗是看起来简单、实际最容易带脏数据过关的一步。新股上市首日简称前会带N,上市次日至第五日会带C,这些临时标记不属于公司真实简称,必须去掉。全角空格和中文里的特殊空白符也是高频噪声源,一个肉眼看不见的\u3000字符就能让两条本该匹配的记录对不上。

# 名称清洗:去全角空格、去新股标记、去尾部空格 def clean_name(raw): if not isinstance(raw, str): return "" s = raw.replace("\u3000", "").strip() if len(s) >= 3 and s[0] in ("N", "C", "U"): # U为科创板上市后首次实现盈利的标识 s = s[1:] return s # 上市日期统一成date类型,部分接口返回带时分秒的字符串 df_final["list_date"] = pd.to_datetime(df_final["list_date"]).dt.date # 股票代码严格取6位并补齐前导零 df_final["code"] = df_final["code"].astype(str).str.zfill(6)

代码里的zfill处理的是类似1被存成"1"的情况,统一补成000001才能和其他数据源做关联。市值字段的清洗则要在数值和单位两个维度同时下手。有的接口返回的是字符串"1.2亿",有的返回的是原始元单位的浮点数,混着存进同一列就是灾难。我习惯统一换算成亿元并写入列名,写成total_mv_yi,让下游使用者一眼看到单位,不用去猜。

上市日期字段还要警惕一类特殊值:部分接口把尚未上市的公司上市日期返回成"1970-01-01"或者"1900-01-01"作为空值占位。遇到这种默认时间戳,直接置空比保留它更安全,否则按上市年份做筛选时这些幽灵纪录会混进结果。

4.3 缺失值和字段基准日的分级处理

全量数据不可能每个字段都完整,处理缺失值之前先想清楚每个字段的缺失是系统性的还是偶发的,再决定处置策略。系统性缺失是指一个区块的数据整个拿不到,比如个别数据源不提供实际控制人字段,那么这一列空值再多也不是清洗能解决的,只能换源或置空。偶发性缺失是指个别股票记录单独缺某个值,大概率是源头录入不完整,按下面这张表分级处理。

字段典型缺失率处理策略原因
实际控制人30%以上置空,不插补穿透披露口径复杂,插补结果不可信
上市日期低通过招股说明书披露日推算拟上市公司数据源更新时间滞后
总股本极低查找披露公告核对新股上市后数据源未及时同步
行业分类中等按最近一期报告分类更新行业分类每年会做再分类调整

不插补实际控制人是血泪教训换来的规矩。早期版本为了追求字段完整率,用公司名称相似度去推断实控人,结果把两家名称相似但股权毫无关联的公司插补成同一实控人,直接污染了下游的关联关系分析。从那以后,凡是无法从权威源交叉验证的字段,一律置空。

字段基准日比缺失值更容易被忽略。接口返回的当前股本总额,实质上是公司最新一期披露报告的数据,遇到增发、回购、限售解禁时,它的更新时点可能比本地文件生成日期早几十天。包里需要加一个data_asof_date字段,标记每个观测值在源端对应的基准日期,否则后面对比两个时期的快照时会发现股本数据对不上,那时候再查就跟大海捞针一样。

5. 避坑:基础信息抓取中的高频翻车现场

5.1 被封限流:前两千条正常,后两千条全部403

现象:脚本前两天跑得好好的,第三天开始跑到一半接口返回403 Forbidden,重试几次还是同样结果,日志里能看到连续出现status code异常。原因:请求频率固定且没有随机抖动,代码里写死了sleep(0.2),服务商的风控系统识别出这是机器行为,把该IP或账号的访问权限临时封禁。解决:把固定间隔改成随机区间,同时加上指数退避重试,把单次请求的UA和Cookie保持一致,模拟出正常浏览器的访问特征。

import random import time def sleep_random(): # 随机控制在100ms到450ms之间,避免固定节奏被识别 time.sleep(random.uniform(0.1, 0.45)) def request_with_retry(url, params, retries=3): for attempt in range(retries): try: resp = requests.get(url, params=params, timeout=10) return resp except requests.exceptions.RequestException: if attempt == retries - 1: raise # 重试等待时间按指数增长:2s、4s、8s time.sleep(2 ** attempt)

不要只在客户端加延时,服务端的风控还有一层是按账号维度统计每日调用总量。每天拉一次全量已经足够,别为了验证代码反复重跑,第二次跑就把当天的配额消耗掉一半。被封后最好的处理是停掉脚本等24小时自然解封,而不是换个代理继续打,后者容易把账号送上黑名单。

5.2 代码段判断失误:包里混进去一堆非股票品种

现象:拉完清单一看总记录数,比预期多了两千多条,解压后一查发现可转债、ETF、REITs全混在里面。原因:全量接口的market参数传了all,服务商返回的是全证券品种清单,不是A股股票清单。解决:用代码段白名单过滤,并确认每个代码段的板块属性符合预期。

def is_a_stock(code: str) -> bool: # 只保留A股股票代码段,B股、可转债、基金不在包内 valid_prefix = ( ("600", "601", "603", "605"), ("688",), ("000", "001", "002", "003", "300", "301"), ("8", "920"), ) return any(code.startswith(p) for p in valid_prefix)

这里用startswith匹配时注意8开头的北交所代码实际是8开头的三位段,比如82、83、87、88,写前缀时至少要保留两位,否则会把8开头的其他品种误收进来。过滤完还有一道复核,就是按第3章里按exchange分组的数量核对,数量对不上就知道过滤逻辑写错了。

5.3 股本数据日期错位:昨天拉的记录和今天对不上

现象:同一家公司的总股本字段,昨天快照里是5.2亿股,今天快照变成5.6亿股,但当天并没有除权除息或增发公告。原因:数据源返回的股本取自最近一期财报或公告,而不是拉取当日的最新市值口径,两天的快照实际对应的是公司两个不同里程碑的数据状态。解决:在字段表里单独维护股本基准日,并在文件名之外再给每条记录打上update_date标记,分析时优先按update_date对齐而不是按文件生成日对齐。

按文件生成日对齐数据是新手最容易踩的隐性坑。你以为两份文件都是当日快照,实际上其中一份的字段基准日是三个月前的财报发布日期,但基础信息场景下这种滞后通常可以接受,问题只在于你把它当成了当日数据用。

5.4 跨平台解压乱码:Windows打包Linux乱码

现象:rar包在Windows下解压一切正常,传到Linux服务器用unar解压后,CSV文件名变成乱码,文件里的中文列名和简称全是乱码。原因:Windows下的rar工具默认用GBK编码保存文件名,而Linux默认按UTF-8解码,两边编码表对不上。解决:CSV内容统一用utf-8-sig编码写入,打包时文件名全部使用英文,不要出现"最终版"、"股票数据"这类中文文件名。

文件名用英文、内容用UTF-8-SIG,这两个习惯配合在一起之后,跨平台问题基本消失。如果你收到的rar包已经乱码,先试unar -e GBK,一般能救回来,但这不是长久之计,规范的产出流程应该在源头就定好编码方案。

5.5 退市股消失:数据源悄悄把退市股剔除了

现象:拉取全量清单时总数看起来正常,但和上个月的快照对比,少了十几家公司,一查都是最近进入退市整理期的股票。原因:部分数据源的免费接口默认只返回已上市且在正常交易状态的股票,退市整理期的股票被静默过滤。解决:拉取时显式声明需要包含退市状态,并给退市股单独标注delist_date字段。

如果你做的是历史回测或财务风险分析,退市股本身就是一类非常重要的样本,把它们直接丢掉等于丢弃了A股市场最极端的一批尾部数据。拉取时多带一个参数就能解决,真正要注意的是别在不知道的情况下被数据源默认行为带偏。

6. 进阶:用校验和增量更新把这包数据用稳

拿到一份基础信息包,先别急着写策略,花十分钟跑一遍完整性校验,能省下后面几天的对账时间。我习惯上按五个维度做检查:数量核对、代码段分布、唯一性约束、空值率监控、时间戳比对。数量核对是最直观的,打开文件看一眼总行数,当前A股总数在五千家上下,出现四千或六千都值得警惕。代码段分布就是把上节那张板块规则表跑一遍分组统计,科创板数量大于沪市主板基本可以断定过滤逻辑出了问题。唯一性约束是校验股票代码有没有重复,一行代码就能跑完:

# 唯一性校验:重复代码会直接破坏下游关联 assert df["code"].is_unique, "发现重复股票代码" # 空值率监控:超过阈值直接预警,不用等到入库后才发现 null_rate = df.isnull().mean().sort_values(ascending=False) print(null_rate[null_rate > 0.3])

增量更新是这份数据包能否持续使用的关键设计。每周拉一次新快照,和上一份做一次全字段diff,新增记录大概率是新上市公司,消失的记录大概率是退市股,名称或行业变化的记录需要单独看是否正常。比对逻辑不复杂,用merge就能实现,重点是把diff结果输出成一份变更日志,随rar包一起交付,下游使用者一眼就能看出本周期内市场结构发生了什么变化。

数据源的接口文档会更新,代码段规则会调整,每年年底行业分类还会重新洗牌,单凭经验记规则总有覆盖不到的死角。我现在的习惯是每月手动抽查一次数据包,随机挑十家公司,去公开披露文件里人工核对两三个关键字段,这是数据质量保障的最后一道防线。

最后说一个让我自己印象深刻的教训。有一版数据包我只看数量没看字段口径,把总股本和流通股本口径当成一回事拿去和另一套数据源对账,整整折腾了两天,最后发现两边算法本身就不一致,并不是数据错误。从那以后,我把字段口径说明文件放进包内,和最新数据文件放在一起,每次交付都提醒自己先讲口径再讲数据。这个习惯希望能帮到你,让这份基础信息包从一开始就走在正确的路上。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询