财报下载归集:按公司名或代码抓取巨潮资讯 PDF 并归档到 ima 知识库

前言

手工收集上市公司财报的典型流程是:打开巨潮资讯网,搜索公司名,筛选公告类型,逐条点开 PDF 下载,再按目录存进本地文件夹或知识库。重复多了以后,主要成本不在下载本身,而在「查哪份报告、存到哪、叫什么名字」这三步的反复确认。

下面介绍的 Skill「财报下载归集」(坐标 @user_f7659217/financial-report-download-collection,版本 1.0.0,社区来源,SkillHub 分类 data-analysis,评分 100000,下载 806)把这条路自动化:用户给出公司名称或股票代码,它先从巨潮资讯网取回财报 PDF,再通过 ima 知识库接口归档到指定文件夹。全程串行,先下载后归档,任一环节失败即停止。

这是什么

一个基于通用 SKILL.md 格式的 Agent Skill,定位是「财报抓取 + 知识库归档」两步流水线。发布者 user_f7659217,标签 requires_api_key=true,说明调用外部服务前需要配置凭证。

它解决的是三个具体问题:

  1. 免手工检索:用 searchkey + seDate 一次查全某公司在某时间段内的公告,只保留 PDF 附件。
  2. 结构化取数:响应 JSON 里直接拿到公告标题、发布时间、大小、下载路径,可以列出候选让用户挑。
  3. 归档有归属:上传时可指定 ima 知识库和文件夹,并做重名检查。

凭证与依赖配置

这个 Skill 不自带下载上传逻辑的完整实现,阶段二依赖 ima-skill 提供封装好的脚本。开始前先把凭证准备好。

凭证来源有两个:环境变量 IMA_CLIENT_ID / IMA_API_KEY,或本地文件。安装说明(从 https://ima.qq.com/agent-interface 获取 Client ID 和 API Key 后写入):

mkdir -p ~/.config/ima
echo "your_client_id" > ~/.config/ima/client_id
echo "your_api_key" > ~/.config/ima/api_key

配置完成后用一条命令确认凭证到位:

test -f ~/.config/ima/client_id && test -f ~/.config/ima/api_key && echo "✅" || echo "⚠️ NO CREDENTIALS"

每次调用阶段二前还需要导出这几个变量:

export IMA_CLIENT_ID=$(cat ~/.config/ima/client_id)
export IMA_API_KEY=$(cat ~/.config/ima/api_key)
export IMA_SKILL_VERSION="1.1.7"
export SKILL_DIR="/workspace/skills/ima-skill"
export IMA_BASE_DIR="$SKILL_DIR"

其中 IMA_SKILL_VERSION 与 SKILL_DIR 指向 ima-skill 的安装位置,ima-skill 需自行安装到该目录,Skill 本身只负责调用。

触发条件

当用户消息中出现下列关键词时触发:财报、年报 / 年度报告 / 年度财报、季报 / 季度报告 / 一季报 / 半年报 / 三季报、下载财报 / 下载年报 / 下载季报、上市公司报告。

若用户同时给出公司名称/股票代码与目标文件夹名,直接执行完整流程;信息不全则分步收集。

阶段一:从巨潮资讯下载财报 PDF

这一阶段分四步:构造查询、发送请求、解析结果、下载 PDF。入口接口是巨潮资讯的公告全文检索接口:

POST http://www.cninfo.com.cn/new/hisAnnouncement/query
Content-Type: application/x-www-form-urlencoded

核心参数:

参数 值 说明
pageNum 1 页码
pageSize 30 每页条数
tabName fulltext 全文搜索模式
searchkey {公司名/代码} {报告类型关键词} 空格分隔多词
seDate 可选,如 2024-01-01~2025-12-31 限定公告日期范围

报告类型关键词映射:年报 → 年度报告,一季报 → 第一季度报告,半年报 → 半年度报告,三季报 → 第三季度报告,未指定时默认 年度报告。

以搜索平安银行 2024 年年度报告为例:

curl -s -X POST "http://www.cninfo.com.cn/new/hisAnnouncement/query" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "pageNum=1" \
  --data-urlencode "pageSize=30" \
  --data-urlencode "tabName=fulltext" \
  --data-urlencode "searchkey=平安银行 年度报告" \
  --data-urlencode "seDate=2024-01-01~2025-06-01"

响应是 JSON,公告列表在 announcements[],每条包含 secCode、secName、announcementId、announcementTitle、announcementTime(Unix 毫秒)、adjunctUrl(PDF 相对路径,拼下载地址的核心字段)、adjunctSize、adjunctType。解析时筛选 adjunctType=PDF 且标题匹配报告类型的条目。

若 totalAnnouncement > 1,列出候选报告(标题、日期、大小)让用户选择,可输入序号或输入「全部」。只有一条匹配时直接进入下载。

下载 URL 为 http://static.cninfo.com.cn/{adjunctUrl}:

TMP_DIR=$(mktemp -d)
PDF_NAME="{公告标题}.pdf"
curl -sL -o "$TMP_DIR/$PDF_NAME" "http://static.cninfo.com.cn/{adjunctUrl}"

下载后验证文件存在且大小大于 0:

test -s "$TMP_DIR/$PDF_NAME" && echo "✅ 下载成功: $(ls -lh "$TMP_DIR/$PDF_NAME" | awk '{print $5}')" || echo "❌ 下载失败"

失败即终止流程并报告原因。这一步把 PDF 落到临时目录,阶段一才算完成。

阶段二:上传 PDF 至 ima 知识库

阶段一完全成功后才启动。同样分步执行,用到的脚本都在 ima-skill 里。

Step 5 前置检查,用 ima-skill 的 preflight 脚本校验文件,返回 pass=false 就终止:

PREFLIGHT=$(node "$SKILL_DIR/knowledge-base/scripts/preflight-check.cjs" --file "$PDF_FILE")
echo "$PREFLIGHT"

从返回 JSON 里提取 file_name、file_size、media_type、content_type、file_ext 供后续步骤使用。

Step 6 获取知识库 ID。用户指定了知识库名称时用搜索接口匹配:

ima_api "openapi/wiki/v1/search_knowledge_base" '{"query":"{知识库名称}","cursor":"","limit":20}'

未指定时列出可添加的知识库供用户选择,只有一个就自动选择。

Step 7 定位目标文件夹。用户给了文件夹名就在知识库里搜:

ima_api "openapi/wiki/v1/search_knowledge" "{
  \"query\": \"{文件夹名称}\",
  \"knowledge_base_id\": \"{kb_id}\",
  \"cursor\": \"\"
}"

从 info_list 里筛 media_id 以 folder_ 开头的条目,取到即作为 FOLDER_ID;找不到就告知用户文件夹不存在,改上传到根目录(后续不传 folder_id)。未指定文件夹时跳过此步。

Step 8 先查重名,再创建媒体,最后做 COS 上传。重名检查:

ima_api "openapi/wiki/v1/check_repeated_names" "{
  \"params\": [{\"name\": \"$FILE_NAME\", \"media_type\": $MEDIA_TYPE}],
  \"knowledge_base_id\": \"{kb_id}\"
  ${FOLDER_ID:+, \"folder_id\": \"$FOLDER_ID\"}
}"

is_repeated=true 时在文件名后追加时间戳。接着创建媒体拿到 media_id 和临时 COS 凭证:

CREATE_RESP=$(ima_api "openapi/wiki/v1/create_media" "{
  \"file_name\": \"$FILE_NAME\",
  \"file_size\": $FILE_SIZE,
  \"content_type\": \"$CONTENT_TYPE\",
  \"knowledge_base_id\": \"{kb_id}\",
  \"file_ext\": \"$FILE_EXT\"
}")

响应 code 非 0 即终止。从响应中提取 secret_id、secret_key、token、bucket_name、region、cos_key、start_time、expired_time,再调用 COS 上传脚本:

node "$SKILL_DIR/knowledge-base/scripts/cos-upload.cjs" \
  --file "$PDF_FILE" \
  --secret-id "{secret_id}" \
  --secret-key "{secret_key}" \
  --token "{token}" \
  --bucket "{bucket_name}" \
  --region "{region}" \
  --cos-key "{cos_key}" \
  --content-type "$CONTENT_TYPE"

上传完成后还有 Step 9「添加知识」,把已上传的媒体登记进知识库,随后向用户报告结果并清理临时文件。这一步的具体接口调用未在官方资料中展开,实际使用前需以 ima-skill 内封装的脚本为准。

交互边界与失败处理

官方资料给了一张缺项处理表,逻辑是一致的:缺公司名/代码就追问;缺报告类型默认最新年度报告;缺目标文件夹就上传到根目录;搜索到多条匹配就列候选让用户选;目标文件夹不存在就告知并退回根目录;凭证没配就引导先配置。

三条硬规则值得单独记:

  1. 串行执行,不可并行。阶段二的任何步骤必须在阶段一完全成功之后启动。
  2. 出错即停。任一环节失败立即终止并报告原因,不做补救性跳过。
  3. 仅输出一个 SKILL.md。不产生 api.md、示例 PDF 等附属文件。

适用场景与限制

适合需要把上市公司财报批量收进 ima 知识库的场景,例如按季度拉取一组标的的定期报告、做跨公司财报语料归档。触发词覆盖年报、季报、半年报,默认走年度报告。

限制主要有两点。一是只支持巨潮资讯网这个数据源,allowed_domains 限定为 www.cninfo.com.cn、static.cninfo.com.cn、ima.qq.com、*.myqcloud.com,不涉及港美股或新三板公告。二是它不自带上传实现,阶段二完全依赖 ima-skill 及其 ima_api.cjs、preflight-check.cjs、cos-upload.cjs 脚本,ima-skill 没装好或版本不对,归档这一步就跑不起来。另外当前 SkillHub 显示安装数为 0,属于发布初期,建议先小范围试一次完整流程再推广。

结尾

这个 Skill 的价值在于把「检索巨潮公告 + 下载 PDF + 归档 ima」这条重复链路收敛成一条串行流水线,输入只要公司名或代码,中间的候选确认、重名处理、文件夹定位都按既定规则走。官方地址:https://api.skillhub.cn/user_f7659217/financial-report-download-collection ,SkillHub 页面:https://www.skillhub.cn/skills/user_f7659217/financial-report-download-collection 。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye