大家好,今天给大家分享一个金融/会计/实证研究必备工具: 一键批量下载 2005–2025 上市公司年报 PDF;支持多线程提速、自动去重、失败日志记录 ;代码结构清晰、注释完整、可直接运行下面我从功能、结构、核心逻辑、使用方法一步步讲清楚,新手也能直接上手。
一、这个工具能做什么?
核心功能
自定义公司名单 + 年份范围
自动搜索巨潮资讯年报、过滤摘要/公告/半年报
串行/多线程下载(线程数可配置)
自动跳过已下载文件
失败自动重试 + 日志记录
进度条实时显示
适用人群
金融/会计实证研究
上市公司分析
量化投资/基本面分析
学生论文数据收集
二、代码整体结构(五大模块)
整个项目只有一个 .py 文件,结构非常清晰,不用复杂部署,直接运行即可:
配置区:年份、路径、延迟、线程数(可一键修改)
公司名单:分层设计(核心→试跑→正式样本),降低测试成本
辅助函数:自动建目录、缓存去重、失败日志记录
核心爬虫:搜索→解析→提取PDF链接→下载(精准不抓错)
执行入口:串行/并行调度 + 结果统计(直观看到下载情况)
下面把核心代码片段 + 详细解析拆解,帮大家快速理解、直接复用。
三、核心代码解析(可直接复制运行)
1. 配置区(最关键,一键修改)
所有个性化需求,都在这里调整,不用改其他代码:
# 配置部分:根据实际情况修改
START_YEAR = 2005 # 起始年份(建议2005,覆盖完整样本)
END_YEAR = 2025 # 结束年份(年报次年4月前发布,无需改)
PDF_DIR = "./PDF" # 年报保存目录(自动创建)
LOG_DIR = "./PDF/logs" # 失败日志目录(自动创建)
REQUEST_DELAY = 1.5 # 请求延迟(1-2秒,防封号)
MAX_RETRIES = 3 # 失败重试次数
PARALLEL_DOWNLOAD = True # 是否多线程(建议开启)
MAX_WORKERS = 3 # 最大线程数(≤5最稳)
SAMPLE_LEVEL = "main_extended" # 样本层级(试跑用"trial")
2. 公司名单(分层设计,超实用)
避免一开始大规模请求被封,先试跑再全量,新手优先用「trial」试跑:
# 第一层:核心案例公司
CORE_COMPANIES = ["科大讯飞"] # 002230.SZ
# 第二层:小规模同行试跑样本(推荐先跑这6家)
TRIAL_COMPANIES = [
"科大讯飞", "用友网络", "中国软件", "东华软件", "宝信软件", "航天信息"
]
# 第三层:正式主样本(约20家,试跑成功后切换)
MAIN_SAMPLE_CORE = [
"科大讯飞", "用友网络", "中国软件", "东华软件", "宝信软件", "航天信息", "华胜天成", "恒生电子"
]
MAIN_SAMPLE_EXTENDED = [
"久其软件", "万达信息", "卫宁健康", "润和软件", "恒华科技", "中科创达", "科大国创", "太极股份", "超图软件", "天源迪科", "同花顺", "易华录"
]
3. 核心亮点:精准提取年报链接(不抓错文件)
这部分是爬虫的灵魂,能自动过滤摘要、公告、半年报,只抓完整年报:
def extract_pdf_url_from_json(response_json: Dict, company_name: str, year: int) -> Optional[str]:
base_url = "http://static.cninfo.com.cn/"
announcements = response_json.get("announcements", [])
for item in announcements:
title = item.get("announcementTitle", "")
title = re.sub(r"|", "", title) # 清除高亮标记
# 核心筛选规则(确保只抓完整年报)
if ("年度报告" in title and
"摘要" not in title and
"公告" not in title and
"补充" not in title and
"半年" not in title and
str(year) in title):
adjunct_url = item.get("adjunctUrl", "")
if adjunct_url:
return base_url + adjunct_url
return None
4. 多线程下载(速度翻倍,不卡顿)
开启多线程后,下载速度提升3-5倍,线程数控制在3-5个,避免被平台限制:
def run_parallel() -> None:
ensure_dirs()
total_tasks = len(COMPANY_LIST) * len(YEAR_LIST)
success_count = 0
failed_tasks = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
# 提交所有下载任务
future_to_task = {
executor.submit(fetch_annual_report, company, year): (company, year)
for company, year in tasks
}
# 进度条实时显示
with tqdm(total=total_tasks, desc="下载进度", unit="份") as pbar:
for future in as_completed(future_to_task):
company, year = future_to_task[future]
try:
result = future.result()
if result:
success_count += 1
else:
failed_tasks.append(f"{company} ({year}年)")
except Exception as e:
failed_tasks.append(f"{company} ({year}年) - 异常: {e}")
pbar.update(1)
pbar.set_postfix_str(f"成功: {success_count}")
四、运行效果预览(直观可见)
运行后自动创建 PDF/(保存年报)和 logs/(记录失败)文件夹
进度条实时显示:当前处理公司、成功数量/总任务数
失败文件会记录到 failed_年份.log,标注失败原因,方便后续补爬
年报文件名规范:2025_科大讯飞.pdf,便于整理归档
五、新手使用步骤(3步搞定,零门槛)
1. 安装依赖(复制命令,CMD运行)
pip install requests tqdm
2. 修改配置(只改2处,新手也会)
3. 运行代码(CMD进入代码所在文件夹,执行命令)
python annual_report_spider.py
六、为什么这个代码值得收藏?
不同于网上零散的爬虫代码,这个工具是「生产级」的,兼顾实用性和稳定性:
结构规范:配置、名单、工具、爬虫、执行完全分离,便于修改和扩展
防封策略完善:请求延迟、失败重试、多线程限速,避免被巨潮资讯限制访问
精准过滤:严格的标题筛选规则,只抓完整年报,不抓摘要、公告等无关文件
自动去重:已下载的年报直接跳过,节省时间和存储空间
日志完整:失败原因清晰记录,后续可批量补爬,不遗漏数据
学术研究、工作汇报、数据分析都能直接用,收藏一次,终身受益。
七、总结
这是一套开箱即用、零门槛的上市公司年报批量下载工具,覆盖2005-2025年,适配金融、会计、量化等多场景需求,代码可直接复制运行,新手也能快速上手。
如果需要:评论区留言「年报爬虫」,我直接发给你,帮你省去所有摸索时间~
#Python #爬虫 #金融数据 #上市公司年报 #实证研究 #数据收集