专利下载(patent-batch-download)
用途
用户给一份专利号清单,本技能批量抓取 PDF 全文(可选权利要求 + 说明书文本),落盘为本地文件,并生成一份带下载状态与失败原因的核对清单。
设计对象是「突发批量、平时不用」的场景:简单、可断点续传、失败可追溯,不追求花哨功能。
硬约束(任何情况下不得突破)
- 禁止伪造。下载失败必须如实记录原因,严禁用占位文件、空 PDF 或编造的标题充数。
- 限速。默认每次请求间隔 ≥1.5 秒 + 随机抖动,绝不并发打接口。
- 用途限定。仅用于用户个人的研究/存档,不得转售或公开分发下载文件。
- 密钥缺失时不猜测。没有 PatentMax 密钥就下不了中国专利,直接告知申请入口,不要换数据源硬凑。
- PDF 有效性校验。落盘前必须确认字节流以
%PDF开头,防止把错误页存成.pdf。 - 区分产品线。PatentMax(
api.ip930.com,Bearer 鉴权)与知嘟嘟 API(www.iprdb.com,t=参数)
共用数据库但密钥不通用,混淆会得到误导性的「非法 token」。 - 号匹配要精确,改前先跑回归。禁止用子串包含判号(
target in doc会让错号命中真号,
静默下错专利)。动normalize/match_patents/pick_variant/done_key之前和之后
都必须跑python scripts/test_match.py——离线、免费、58 个用例。
通道路由(已实测,勿凭印象改动)
| 号段 | 主通道 | 状态 | 成本 |
|---|---|---|---|
| CN | PatentMax /api/pdf |
✅ 8/8 实测命中 | 按次计费 |
| US | USPTO image-ppubs 直连 | ✅ 免费、无凭证、4/4 通过 | 0 |
| EP / WO / JP / KR | 无可用通道 | ❌ 如实记失败 | — |
元数据(标题/申请人/法律状态/公开日)统一由 PatentMax /api/search 补齐,中、美都能查到,
所以美国通道的清单字段同样完整。检索必须先于下载——documentDate 是拼 PDF 路径的必要输入。
完整端点、实测样本、失败原因见 @references/endpoints.md。改动任何端点前必须先重读该文件并重新实测。
密钥配置(三选一,优先级从高到低)
- 命令行
--token - 环境变量
PATENTMAX_TOKEN config.json的patentmax_token字段(该文件在 skill 根目录,不在scripts/下;技能包内只带config.example.json,复制一份改名为config.json再填入自己的密钥)
首次使用:获取免费密钥(约 2 分钟)
技能包不附带任何密钥。每个使用者用自己的 PatentMax 密钥,额度互不相干:
- 注册/登录 PatentMax 开放平台
https://api.ip930.com(新用户前 30 次检索免费,注册约 2 分钟) - 进入「API 密钥」页 → 填一个名字(如
专利下载)→ 下拉框选 生产密钥 → 点「创建」
- ⚠️ 生产密钥按实际调用计费;测试密钥不计费但只返回示例数据,选错会下到假文件 - 复制生成的
pm_live_...密钥,按上面三种方式任选其一配置
成本预期(务必提前告知用户)
- 美国专利:0 成本(USPTO 官方直连,不消耗任何额度、无需注册)
- 中国专利:按次计费——每条至少消耗 1 次检索,外加 PDF 下载的调用次数;新用户前 30 次检索免费,之后具体单价以 PatentMax 开放平台「计费与套餐」页为准
- 额度没有公开查询接口,只能登录控制台「查看用量与账单」;批量前建议先确认余额
- 余额耗尽的表现:所有接口(含检索)统一返回
409 CONFLICT / API 余额不足,当前 ¥0.00。
脚本识别到该状态会立即停止发请求,把剩余条目如实记为「未执行」并打印醒目提示——
别把它误判成通道故障或号码问题。充值后重跑同一命令即可续上(已下载的自动跳过) --text(抓权利要求/说明书全文)额外消耗 2–4 次/条,按需开启- 批量前可用
--max-calls N设一个硬上限兜底(额度查不到,只能自保);
脚本按「检索 1 + PDF 1(--text再加 3)」预留每条预算,预算不足的条目直接标「未执行」,
不会出现钱花了、文件没拿到的空跑
网络与安全
- TLS 默认开启证书校验。密钥以
Authorization: Bearer头发送,不校验等于把它交给中间人;
早期版本无条件CERT_NONE是没必要的降级(实测本机严格校验直接可用)。
脚本启动时探测一次:校验通过就用严格模式(日志打印TLS:证书校验已开启);
仅当本机证书链确实不可用(企业代理 / 自签 CA)时才降级,并打印醒目警告。
强制关闭:PATENT_DL_INSECURE_TLS=1(不推荐)。 - 有限重试:对
429/500/502/503/504与连接超时自动重试 2 次(退避 2s、4s)。
GET 幂等,重试不会重复计费出两份文件;<u>不</u>重试 4xx 业务错误(如密钥、参数类)。 - 密钥/额度类错误(
TOKEN/UNAUTHORIZED/FORBIDDEN/额度)立即冒泡终止该条,
不做无意义重试,也不退化成别的数据源硬凑。
固定流程
第 1 步 · 清单确认
拿到清单后先问清三件事(一次性问完,不要挤牙膏):
1. 号码形态:公开号、申请号、带不带国家码、带不带小数点都能吃,详见下表。
只有真歧义的形态才需要和用户确认。
2. 是否需要文本版全文(权利要求/说明书):默认不抓;需要时加 --text,按次计费。
3. 输出目录与命名:默认 ./patents/,文件名为库内标准号(如 CN114824336B.pdf)。
号段写法兼容表(2026-09-14 实测)
同一件专利,用户可能上传成下面任意一种写法,脚本都能识别,且归并成一条(不会各下一遍):
| 写法 | 例子 | 解析结果 | 需要确认? |
|---|---|---|---|
| 公开号(带种类码) | CN122719416A |
cc=CN, num=122719416, kind=A | 否 |
| 公开号(无种类码) | CN122719416 / 122719416 |
cc=CN, num=122719416 | 否(9 位=中国公开号) |
| 申请号(带国家码) | CN202611224173.0 |
申请号 202611224173.0 |
否 |
| 申请号 | 202611224173.0 |
申请号 202611224173.0 |
否(12 位序号+校验位) |
| 申请号(13 位无小数点) | 2026112241730 |
申请号 202611224173.0 |
否 |
| CN 前缀 + 13 位 | CN2026112241730 |
申请号 202611224173.0 |
否 |
| 申请号缺校验位 | 202611224173 |
校验位由算法补全 | 否 |
| 全角 / 带空格 / 带括号注释 | 202611224173.0、CN 122719416 A、202611224173.0(申请号) |
自动洗净 | 否 |
| 7–8 位、10–11 位纯数字 | 11000000、1234567 |
按 US 处理 | 是,必须确认 |
| 非中/美号段 | EP1000000A1 |
会去试,失败即如实记录 | 说明无通道即可 |
判定依据(不要凭印象改):
- 中国公开号是 9 位(2003 年起);美国公告号最长 8 位、申请公开号 11 位
→ 所以 9 位只能是中国公开号,7–8 位才是中/美真歧义。 - 中国申请号是 13 位 = 4 位年 + 8 位序号 + 1 位校验位;去掉校验位是 12 位
→ 12/13 位只可能是中国申请号,无歧义,脚本自动归一并按 CN 走 PatentMax。 - 校验位是前 12 位的确定函数:各位乘权重(2,3,4,5,6,7,8,9 循环)求和后对 11 取模,10 记作
X。
已用 3 个真实申请号验证:202010783517→8、202111491053→4、202310894263→0。
两个用途:给缺校验位的 12 位号补全;发现抄错(如2026112241731会提示
「校验位对不上,按前 12 位应为 .0」)。脚本只警告不改号——静默改号会下到另一件专利。 - 7–8 位纯数字按美国号处理会得到「USPTO 404 + 检索 0 命中」的双重失败,
所以这类必须停下来问用户,不可擅自下载。
第 2 步 · 试运行
python scripts/patent_dl.py --nums "US11000000B2,<用户的一条CN号>" --out ./patents --dry-run
python scripts/patent_dl.py --nums "<挑 2-3 条>" --out ./patents
确认通道路由正确、PDF 真能落盘,再跑全量。
> 解释器:想同时拿到 _manifest.xlsx 就得用装了 openpyxl 的解释器,例如隔离 venv
> ~/.workbuddy/binaries/python/envs/default/Scripts/python.exe(Windows)或 /bin/python。
> 用裸 python 且没装 openpyxl 时脚本只出 CSV 并提示——不是错误,但少了 Excel 交付物。
> 改匹配逻辑前先跑回归:python scripts/test_match.py
> 58 个离线用例覆盖:公开号/申请号的 20 余种写法(含带国家码、缺校验位、全角、带空格)、
> 13 位申请号、显式种类码、纯数字美国号、错号不得误命中、--variant 取值、
> 校验位算法(3 个真实号)与「抄错校验位要报警」、同族写法归一键。
> 不联网、不消耗额度。号匹配出错会静默下错专利,比下载失败严重得多,
> 必须能在不花钱的前提下验。
第 3 步 · 全量执行
python scripts/patent_dl.py --list 清单.txt --out ./patents --max-calls 200
| 参数 | 说明 |
|---|---|
--list 文件 |
每行一个号,取第一列(兼容 Excel 复制粘贴、CSV;分隔符支持中英逗号/分号/顿号/制表符) |
--nums "A,B" |
直接给号码串,分隔符同样支持 , , 、 ; ; 与换行 |
--out 目录 |
输出目录,默认 ./patents |
--text |
额外抓权利要求与说明书全文(MCP,按次计费,默认关闭) |
--variant |
同号多篇时下哪篇,见下节。auto(默认)/first/granted/all |
--max-calls N |
本次运行的 API 调用上限,0=不限(默认)。超限前会预留单条所需调用数,不会出现「检索钱花了、PDF 却被拦下」的空跑 |
--channel |
强制指定通道(默认按号段自动路由) |
--delay 1.5 |
每次请求间隔秒数 |
--dry-run |
只解析路由,不下载 |
--variant:同一申请号往往不止一篇
按申请号检索,库内常返回两篇:CN111735730A(申请公布,2020-10-02)与
CN111735730B(授权公告,2020-12-08)。旧版静默取第一篇,用户想要可主张的授权文本时拿到的是公开文本。
| 取值 | 行为 |
|---|---|
auto(默认) |
取公开日最晚的一篇——A/B 并存时即授权公告 B;只有一篇时就是那一篇 |
first |
保持旧行为,取检索返回顺序第一篇 |
granted |
强制取授权/公告篇(号尾 B),没有则退到最晚一篇 |
all |
同号各篇全部下载(清单里各占一行,标准号区分) |
无论取哪篇,命中多篇时都会在日志里打印全部候选与公开日,不做静默取舍。
第 4 步 · 核对与交付
每次运行结束后必须向用户报告,不得只说"完成了":
- 成功 / 失败 / 跳过(已存在)条数
- 失败条目逐条列出原因(检索未命中、该地区无通道、接口 503 等)
- 消耗的 API 调用次数估算,并提示以 PatentMax 控制台账单为准
- 清单文件路径(_manifest.csv / _manifest.xlsx)
断点续传
重跑同一命令只补失败的。判断走 _manifest.csv 里「状态=成功 且 PDF 仍以 %PDF 开头」的行
(键是归一化后的号码:2020107835178、202010783517.8、CN202010783517.8 视为同一条),
不是按输入号去找文件名——下载成功后文件已重命名为标准号(CN111735730A.pdf),
按输入号找必然落空、白扣额度。
清单幂等:同键重跑覆盖原行,不会堆重复记录。行键优先用标准号,
所以 --variant all 下 A/B 两篇各占一行、互不覆盖。
同一件专利不会被重复下载,三层保障:
| 层 | 触发条件 | 效果 |
|---|---|---|
| 批内去重 | 清单里同一件专利写了多种写法(2026112241730 / 202611224173.0 / CN202611224173.0) |
预处理阶段即合并,0 次调用 |
| 同族跳过 | 清单里同时有公开号与申请号(写的是同一件),且本次没有显式指定种类码、用的是 auto |
第一次检索后识别为同族,不再下第二遍 |
| 单篇续传 | 该标准号此前已下成功 | 按篇目粒度跳过,缺哪样补哪样 |
> 想同时要 A、B 两篇时,用 --variant all,或在清单里显式写 CN…A 与 CN…B
> ——显式点名要另一篇时不会被上述规则拦下。
续传分三级,目的是「能省则省,但不能省错」:
| 场景 | 行为 | 花费 |
|---|---|---|
--variant auto 且该号此前已下成功、文本也齐 |
直接跳过 | 0 次调用 |
| 文件名恰好是输入号(用户直接给标准号时常见)且文本齐 | 跳过,仍落一行清单(否则会出现「文件在、清单里没有」) | 0 次调用 |
显式指定 first/granted/all |
先检索(历史那行未必是这次要的篇目),再按单篇粒度决定 | 每条 1 次检索 |
缺哪样补哪样:PDF 已在本地就只抓 --text 全文,不存在就只抓 PDF,都齐才整体跳过。
「后来才想加 --text」不会把 PDF 重买一遍。
> 取舍:0 次调用那两条会沿用清单里已有的元数据。若清单被清空或换了输出目录,
> 跳过项就只记路径、标题/申请人留空——想补齐字段就重跑一次(每条多花 1 次检索)。
> 省钱优先,因为额度不可查、也退不回来。
扩写号段支持(维护指南)
用户又传来一种没见过的写法时,照这个顺序改。不要直接改代码试——号解析错了会静默下错专利,
必须先能在不花钱的前提下验证。
改动的落点只有 4 处,全在 scripts/patent_dl.py:
| 位置 | 职责 | 什么情况下要动 |
|---|---|---|
clean_number() |
洗净:全角→半角、去空格/短横线、截掉括号注释、去尾部顿号分号 | 用户从 Word/PDF/微信复制的脏字符(新类型标点、不可见字符) |
normalize() |
解析:拆出 cc / num / kind / appnum,判定归属 |
新写法(多一种格式、多一种判定规则) |
done_key() |
归并键:把同一件专利的多种写法归一 | 出现「同一件专利的两行重复」 |
match_patents() |
比对:把输入号与库内标准号对齐 | 出现「明明在库里却 0 命中」 |
固定流程:
- 先写用例:往
scripts/test_match.py的PARSE_CASES(解析契约)或CASES(比对结果)
里加一行,写明期望值。此时跑python scripts/test_match.py应当是 FAIL —— 这才叫复现了问题。 - 改上面 4 处里对应的那一处,再跑
python scripts/test_match.py,必须 全部通过。 --dry-run演练一次真实写法,确认通道路由与提示语对:
bash python scripts/patent_dl.py --nums "<新写法>,<旧写法>" --out ./.tmp-fmt --dry-run
同一件专利的多种写法应当打印[去重] … 与前面的写法指向同一件专利,跳过。- 更新本文档的号段写法兼容表与故障速查表。
踩过的坑(改之前先读一遍):
- 种类码绝不能丢。
^([0-9]+)([A-Z]?)$会把US9000000A吃成num='9000000A',
种类码没了 → 404。号码段与种类码必须分开捕获。 - 禁止子串包含判号。
"1234567" in "US12345678B2"为真 → 静默下错文件。 - 加国家码前缀要兼容两种含义:
CN后面是 9 位=公开号,是 12/13 位=申请号。 - 不要替用户静默改号。校验位对不上就如实报警告,让人确认;自动纠正会下到别的专利。
- 去重比字面字符串要不得:
2026112241730与202611224173.0是同一件,
必须按done_key()归一后比。 - 改
normalize()的note文案时注意,route()用note.startswith("缺国家代码")分流,
改文案要同步改判断。
新增一个国家通道(例如将来 EP 有源了):在 route() 里给出主通道,在 references/endpoints.md
记下实测样本与失败原因,并把 SKILL.md 的通道表更新。没有实测过的通道不要写进表里。
用户需要提供什么
| 项 | 必需性 | 说明 |
|---|---|---|
| 专利号清单 | 必需 | 每行一个,支持公开号或申请号 |
| PatentMax 密钥 | 下中国专利必需 | 美国专利不需要 |
| 输出目录与命名偏好 | 可选 | 有默认值 |
| 是否要文本版全文 | 可选 | 影响成本,默认不要 |
已知局限(必须主动告知用户)
- PDF 是扫描图像版:不可检索、不可复制。下游要做文本分析必须用
--text抓结构化全文。 - 同一申请号常有 A/B 两篇(申请公布 + 授权公告)。默认
--variant auto取公开日最晚的一篇,
即通常意义上的授权文本;若用户明确要申请公布版,用--variant first(或all两篇都要)。 - 只有中国专利有 PDF 自动通道。美国靠 USPTO 免费直连;EP/WO/JP/KR 目前下不了。
USPTO 直连按公告号取值,无法指定同号的其他篇目(--variant all时第二篇会如实记为失败)。 - 额度无法通过 API 查询,只能看网页控制台。批量前建议先确认余额。
seq位规律来自 8 个样本(恒为 1)。若遇到 503,先确认documentDate是否正确,
再人工核对https://app.ip930.com上的原文链接。- 国知局 epub 在沙箱内测不准,真实网络下可能是中国的免费替代通道,待重测。
故障速查
| 现象 | 原因 | 处置 |
|---|---|---|
409 CONFLICT + API 余额不足 |
账户余额耗尽——不是通道故障、不是号码有问题 | 脚本会自动把剩余条目记为「未执行」并立即停止发请求。去控制台充值后重跑同一命令:已下好的自动跳过 |
503 SERVICE_UNAVAILABLE + product:patent_data |
/api/pdf 路径不对,或该地区不在覆盖范围 |
中国专利核对 公开日 与 seq=1;US/EP 直接判定为无通道 |
400 VALIDATION_ERROR 缺少必要参数:key |
/api/pdf 少了 key |
说明端点存在,补上 path 参数 |
400 VALIDATION_ERROR 缺少必要参数:q |
/api/search 少了 q |
参数名是 q,不是 query(那是 MCP 的参数名) |
code 202(知嘟嘟域名下) |
key 不是知嘟嘟签发的 | 不是密钥失效,是产品线用错,改用 api.ip930.com |
检索 total=0 |
号不在库内,或写法与库内标准号不一致 | 退化成最简号再试;仍失败则列入失败清单并告知 |
| 检索「命中 N 条但无一条号匹配」+ USPTO 404 | 纯数字被当美国号处理(典型:13 位 CN 申请号) | 已修:12/13 位与 9 位自动按 CN 处理。若仍出现,说明是 7–8/10–11 位的真歧义号,先与用户确认号段 |
日志出现 ⚠ 校验位对不上 |
13 位申请号末位与算法结果不符,用户抄错了一位 | 不要自动纠正(会下到另一件专利);把提示原样告知用户核对 |
日志出现 [去重] … 指向同一件专利 |
同一件专利在清单里写了多种写法 | 正常,不是错误;只花一次检索 |
日志出现 与本批 … 是同一件专利,已下过 |
清单里公开号与申请号写的是同一件 | 正常。如需另一篇目,用 --variant all 或显式写号尾种类码 |
| 重跑时「成功」的条目仍被重新下载、清单出现重复行 | 旧版续传按输入号找文件名 | 已修:续传读 _manifest.csv 的成功行,按归一化号码匹配;如遇重复行,删掉 _manifest.csv 重跑一次即可 |
| 未安装 openpyxl,只出 CSV | xlsx 依赖缺失 | 用隔离 venv 装:python -m venv <envs>/default && <envs>/default/Scripts/python -m pip install openpyxl,再用该解释器跑脚本 |
PDF 落盘后不是 %PDF 开头 |
接口返回了错误页 | 删除该文件、记为失败,不要交付 |
缺少公开日,无法拼出中国 PDF 路径 |
检索未命中或 documentDate 为空 |
该条走不了 PDF,如实记录 |
缺国家代码,按 US 处理 警告 |
清单里是 7–8 位或 10–11 位纯数字(真歧义) | 必须停下来和用户确认,不可擅自下载。9/12/13 位已由脚本自动判定,不再出此警告 |
⚠️ TLS:本机证书链校验失败…已降级为不校验 |
企业代理/自签 CA 未导入 | 优先导入根证书;确认环境可信才继续。密钥在明文信道上传输有被截获风险 |
日志出现 ↳ 库内同号 N 篇:… |
同号多篇,--variant 在替你选 |
看清本次取的是哪篇;要授权文本用 granted,要全用 all |
多条连续 未执行:剩余额度不足 --max-calls |
设了 --max-calls 且预算已耗尽 |
提高上限或去掉该参数;这是护栏,不是故障 |
| 清单里某行标题/申请人是空的 | 走的是 0 次调用跳过,沿用旧元数据;清单被清过就无旧数据可沿用 | 属预期取舍;要补齐字段就重跑(每条多 1 次检索) |
显式给了 CN111735730A 却没命中 |
匹配改为精确比较后,输入的种类码必须拼进比对串 | 已修;回归用例 test_match.py 有覆盖 |
纯数字美国号 11000000 检索 0 命中 |
库内标准号带 US 前缀,直接比会不等 |
已修:无国家码时按「号码段」比较(_num_only) |