专利之星 · 专利下载 V1.7.2
回复约定(用本技能的 agent 必须遵守)
用户通常只说一句"下载专利"再丢一串号码。默认按工具化方式响应:
- 不逐步汇报、不预先确认、不复述本技能内容、不问"要不要开始"——直接执行。
- 成功时只回三行以内的结果:输出目录名、份数(成功/总数)、文件清单(可只列文件名)。
- 只在下列情况展开说明(各 1–2 句,不写成长文):
- 有号码被跳过(缺字母/缺校验位/老申请号)或「查无此号」
- 有条目落空(无 PDF 全文 / 取址失败)
- 站点异常:限流、超时、验证码、登录态失效
- 出现了需要用户决策的歧义(例如同名不同件、版本语义未验证)
- 不要把 CSV 内容、耗时明细、站点内部细节贴给用户,除非他明确要。
> 一句话:默认输出"目录 + 份数 + 文件清单",异常才多说话。
>
> 例外(V1.7.1):环境未就绪时不要按上面收敛输出——该展开引导就展开。
> 按 references/onboarding.md 的分步话术走,把「用户要做什么」讲清楚,
> 并把 agent 已经代做的部分说明白(用户需要知道哪几步是自己动手)。
用途
在专利之星(国家知识产权局中国专利信息中心主办,免费)下载中国专利全文 PDF
(公开文本说明书),支持两种入口:
- 关键词检索:给检索词,下载结果集里前 N 条(可用
--start分批)。 - 号单精确下载:给公开号/公告号清单,逐个精确命中后下载。
归档命名 = 序号 + 标题 + 申请号 + 号;可选输出逐条耗时 CSV 与限速探测结果。
本技能解决的是「专利之星没有批量下载 PDF 入口、PDF 地址又拿不到」的问题:
真实 PDF 藏在详情页「全文PDF」标签的 <embed src> 里,指向
api.patentstar.com.cn/api/Image/CnPdf/<token>,该地址 curl 直连报 406、
在 cprs 页内 fetch 被 CORS 拦。脚本用「落到 api 域的普通 JSON 页 + 同源 fetch +
受信任点击」把文件直接写进目标目录。
开工前:环境就绪检查(新用户必读)
面向三类用户:没装 Chrome(只有 Edge)、没装 browser-skill、没注册专利之星。
首次使用、或用户说"帮我把环境弄好",先跑这一条——它会告诉你缺哪一层:
python scripts/env_check.py # 人话报告;加 --json 给机器读
报告分五层:bsk CLI / daemon / 扩展连接 / 可用浏览器 / 扩展已安装。
按缺失项只引导那几层,不要把全部步骤一次倒给用户。
前置四件事
- bsk CLI —— agent 可代装,无梯子也可以(实测
raw.githubusercontent.com中国 5 节点全通) - bsk daemon 常驻 —— agent 用受管后台任务起
- 浏览器扩展 —— 用户必须自己点安装(见下)
- 专利之星已注册并登录,且浏览器密码管理器保存了该站密码
> 浏览器多于一个时(Chrome 与 Edge 都装了扩展,或同一浏览器多个 profile):
> 下载时必须用 --browser <instance_id> 指定用哪个,否则脚本会中断并列出实例。
> 先跑 scripts/env_check.py 看 [3] 扩展连接 那段清单即可。
浏览器扩展怎么装(本版重点)
agent 不代装扩展 —— browser-skill 官方硬约束:
Never use sudo; the user installs the browser extension. 三条路径:
| 用户浏览器 | 走哪条 | 用户操作量 |
|---|---|---|
| Edge(或没装 Chrome) | Edge 加载项商店一键装,无梯子可达 | 点 2 下 |
| Chrome,无梯子 | python scripts/install_extension.py --open --make-bat<br>离线包 + 开发者模式 |
点 3 下 |
| Chrome,有梯子 | Chrome 网上应用店 | 点 2 下 |
为什么 Chrome 无梯子只能走离线包(实测结论):
chromewebstore.google.com 在中国 5 个节点 5/5 BLOCKED;且 Chrome 137 起官方
移除了 --load-extension(本机 Chrome 155 实测已失效)。官方给出的替代方案就是
"开发者模式 → 加载已解压的扩展程序"。
scripts/install_extension.py 会自动完成 取最新包 → 校验 → 解压 → 打开扩展页,
用户只剩三下:开开发者模式 → 点「加载已解压的扩展程序」→ 选中脚本打印的目录。
(Edge 上 --load-extension 实测仍可用,但不持久、且用户仍要手动开 popup 连接开关,
不采用,仅在 references/onboarding.md 里作为说明保留。)
完整引导手册
分步话术、失败排查表、Edge↔Chrome 差异、专利之星注册流程,见
references/onboarding.md —— 首次引导前先读它。
使用前提
- browser-skill 已安装,且
bskCLI 可用(脚本按顺序探测--bsk、$BSK_BIN、
PATH上的bsk)。 - 本机 Chromium 浏览器里登录过专利之星,且浏览器保存了该站点密码。
脚本会在登录态失效时自动登录:点进密码框触发 Chrome 密码管理器自动填充
(只判断长度,绝不读取密码内容),再点登录。 bsk daemon已运行。脚本会尝试自动启动;若本机沙箱会收割子进程,
需先由受管的后台任务常驻运行bsk daemon start --foreground。
> DSH 侧补充:bsk daemon 必须在宿主终端启动,不能从 DSH 沙箱内起
> (沙箱会收割子进程)。若 bsk 不在 PATH 上,用
> --bsk "C:/Users/Liu Fuqiang/.local/bin/bsk.exe" 显式指定。
输入号码格式(自动归一化)
号单里的号码可以很脏,脚本会自动归一化(V1.4 起):
自动修复:
- 补 CN 前缀、统一大写
- 去空格 / 全角空格 / 连字符 / 括号 / 引号,去"申请号:""公开号:""公告号:"等中文标签
- ZL 开头的专利号 → 去掉 ZL 按申请号处理(专利号 = 申请号)
- 13 位连写的申请号(缺小数点)→ 在第 12 位后补 .
会被跳过并提示原因(无法安全恢复,不硬猜):
- 文献号缺类型字母(如 CN101214869)→ 按种类号提示候选 A/B、U/Y 或 S
- 申请号缺校验位(12 位、无 .X)→ 校验位无法补算
- 2003 年前的老申请号(8/9 位带点,如 94190001.0)→ 站点无收录
分隔符:换行 / 逗号 / 分号 / 中文逗号顿号分号。不再用空格分隔——号码内部可能含空格(如 CN 105323644 A)。
检索后仍可能「查无此号」:格式合法但站内无此记录(号码打错、或本来就是示例/伪造号),脚本会明确标注,不会静默跳过。
执行
脚本位于本技能目录的 scripts/patentstar_download.py(纯标准库,无第三方依赖)。
Python 解释器用 DSH 内置运行时——先调 load_workspace_dependencies 拿绝对路径,
再以该路径执行;若宿主 PATH 上已有可用的 python 也可以直接用。
按关键词批量下载:
python scripts/patentstar_download.py \
--query "固态电池 电解质" \
--count 10 \
--out "C:/abs/path/输出目录" \
--timing
按公开号/公告号清单精确下载(号单场景):
python scripts/patentstar_download.py \
--pubnums "CN101214869B,CN204416150U,CN209137395U,CN2873190Y,CN107160150A" \
--out "C:/abs/path/输出目录" \
--timing
> Windows 下 --out 用正斜杠或转义反斜杠;路径不要带尾部无意义空格。
> 站点的「号单检索」只接受申请号标准格式(CN+12位+校验位);给公开号/公告号会 0 命中。
> 因此 --pubnums 的实现是逐个用智能检索精确命中后再取 PDF。
常用参数:
| 参数 | 说明 |
|---|---|
--query |
智能检索关键词,多词用空格(站点按 AND 处理)。与 --result-url / --pubnums 三选一 |
--pubnums |
按号码清单精确下载(公开号/公告号/申请号都行),逗号/分号/换行分隔,自动归一化,如 CN101214869B,CN204416150U |
--pubnums-file |
号单文件,一行一个公开号/公告号 |
--result-url |
直接给结果页完整 URL。必须包含末尾完整 base64 片段,漏字符会得到 0 条 |
--count |
下载条数,默认 10 |
--start |
从结果集第几条开始,默认 1(配合 --count 做分批) |
--out |
输出目录;省略时自动在当前目录新建 专利下载<年月日时分秒>(一次指令一个目录) |
--page-size |
结果页每页条数 5/10/15,默认 15,越大越省列表加载次数 |
--name-template |
文件名模板,默认 {idx:02d}_{title}_{an}_{pn} |
--fetch-batch |
每批并发抓取的 PDF 数,默认 5 |
--timing |
输出 下载耗时记录.csv |
--retries |
每条取址/下载的重试次数,默认 3(NOPDF 也走这个重试) |
--pdf-version |
取哪一版:auto(默认,按号码类型)/ public(只要公开文本)/ granted(只要授权文本)/ all(两版都要) |
--pdf-index |
底层逃生口:强制取第几份(发明时 1=公开文本、2=授权文本) |
--all-pdf |
等价于 --pdf-version all(保留兼容) |
--dry-run |
只打印清单与元数据,不下载 |
--bsk |
显式指定 bsk 路径 |
--browser |
会话绑定哪个浏览器实例(instance_id / 唯一标签 / auto)。本机同时连了 Chrome 和 Edge 时必填;auto = 自动选第一个可用实例。实例 ID 用 bsk browsers --json 查,或先跑 scripts/env_check.py 看实例清单 |
用不同关键词、不同 --start 分批跑即可覆盖任意条数(结果集固定排序下顺序稳定)。
输出目录与版本选择
输出目录:不给 --out 时,每次运行自动在当前目录新建
专利下载<年月日时分秒>(例 专利下载20261010012846)——一次指令一个目录,
多次指令的产物不会混在一起。给了 --out 就按给的路径。
多份 PDF 的版本选择(V1.5 起,V1.6 补版本命名与提示):发明记录站点常提供
2 份 PDF,顺序固定——[1] = 公开文本(首页印「(10)申请公布号」)、
[2] = 授权文本(首页印「(10)授权公告号」)。该顺序已用 pypdf 读下载文件的
首页文本核实。脚本按用户给的号码类型自动选版本:
| 用户给的号码 | 角色 | 下载哪一版 |
|---|---|---|
申请号(CN+12位+.X) |
app | 公开 + 授权,两版都下 |
公开号(结尾 A) |
pub | 公开文本 |
公告号(结尾 B/C/U/Y/S) |
grant | 授权文本 |
也可用 --pdf-version public|granted|all 直接指定要哪一版(比记"第几份"直观);
--pdf-index N 是底层逃生口。实用新型与外观设计通常只有 1 份 PDF,照常下载。
文件名直接写版本:发明多份时命名为 ..._公开文本.pdf / ..._授权文本.pdf,
不用打开 PDF 就知道哪份是哪份。命令行与 CSV 也都标注版本
(1.53 MB(公开文本) + 3.83 MB(授权文本))。
两条版本推断提示(V1.6):
- 给公开号 A 但站点有 2 份 → 提示└ 该申请已授权:公告号 CN…B——
代理人拿 A 号时通常不知道授权了没,这条直接回答了。
- 给申请号但只解析到 1 份 → 提示└ 仅解析到公开文本(站内未见授权文本,可能尚未授权)。
同一件给多个号 → 按记录去重(V1.6.1):申请号/公开号/公告号指向同一件时只下一次,
并把各请求号要的版本取并集。例:同时给 CN105323644A + CN105323644B +
ZL201510662000.2 → 1 条记录、2 份 PDF(公开+授权),而不是 4 份重复。
预期耗时(V1.6.1 实测,20 次真实下载)
| 场景 | 平均 | 最快 | 最慢 | 成功 |
|---|---|---|---|---|
| 单条 ×10 | 33.4s | 16.7s | 57.1s | 9/10 |
| 5 条 ×10 | 71.8s | 27.0s | 113.4s | 8/10(2 次站点异常) |
- 单条成本偏高是因为固定的登录检查/会话建立要摊在 1 条上;批量时每条约 14–24s。
- 站点限流是最大变量:同一号码不同轮次时而命中时而失败;站点慢时
navigate
会从 2–3s 涨到 10s。站点正常时 V1.3 曾实测 5 件 44.6s ≈ 8.9s/件。 - 关键词模式:单条约 10–15 秒,10 条约 2–3 分钟。
- 对照传统逐条串行点下载约 58 秒/条,提速 5 倍以上。
- 每次运行都新建独立目录:实测 20/20。
输出
<序号>_<标题>_<申请号>_<号>.pdf—— 校验过%PDF-文件头与体积;
发明多份时直接用版本名:..._公开文本.pdf/..._授权文本.pdf
(例:01_一种隐藏式云台摄像头_CN202111194234.0_CN113983326B_授权文本.pdf);
版本语义未验证的类型仍用_pN。
给的号码是申请号时,文件名末段会自动改用站点的公告号/公开号,避免与申请号重复下载耗时记录.csv(加--timing时)—— 列含「记录PDF数 / 已下份数 / 版本 /
字节数合计 / 大小MB / 下载ms(每份自己的耗时)/ 状态 / 文件名」未下载清单.txt—— 仅在有条目落空时生成,逐行记录「号码 / 标题 / 原因」;
「无 PDF 全文」只表示站点不提供 PDF 文件,其说明书与权利要求仍可能在线可读
关键约束(改动脚本前必读)
- 详情页只能从结果列表点击标题进入。直接导航
/Search/Detail?ANE=...
会被反爬 302 到verifypage.html图像验证码。 - 检索必须用「赋值 + 派发 input/change/keyup/blur + 点
a.search_btn」。
仅bsk fill不足以触发站点读取输入框。 - 程序化
a.click()触发下载会被 Chrome「多文件自动下载」策略拦截,
同页只有第一个能落盘;必须用bsk download(受信任点击),
它每个文件都算一次用户点击,可在同一页面连续下载。 - 每页条数控件
#rowcount_se直接改 value 无效,必须点#filter-box2里的
<a>10</a>;且该设置不随 URL 保留,每次重载结果页都要重设。 - 偶发「假成功」:HTTP 200 但返回 20KB 站点 HTML 壳而非 PDF。脚本已校验
文件头,失败会自动重试。 - 站点不限速(同一 PDF 连续 15 次抓取全 200、耗时稳定 3.8–4.3s),
并发抓取安全。 - 「全文PDF」标签有两种形态,取址必须同时认(V1.3 定型,这里踩过坑):
· 形态 A(单份):点开标签后<embed src>直接出现,指向 api 域。
· 形态 B(多份):点开标签后出现若干a.pdfbtn,地址只在data-url
属性上(href与onclick都是空的),必须先点其中一个,embed 才出现。
两者都没出现,才是真的没有 PDF。 - 取址必须「链接优先、embed 兜底」(V1.5):站点会记住上次选过的版本,
点开标签时可能直接 embed 那一份。若先认 embed,就会绕过按下标选版本、拿到
非本次指定的版本(实测踩到:按公告号请求时只解析出 1 份)。所以先找
a.pdfbtn按下标定位版本;没有链接(单份记录)才采信 embed,且已有 embed 后
再宽限约 1.5 秒等链接出现。 - 绝不能用
#pdfcontainer判 NOPDF:它在真实 DOM 里始终为空,据此判定
会把整个形态 B 误报成「站点未收录全文」——曾导致 5 件里 2 件被白白跳过。 - NOPDF 必须重试后才采信:站点渲染慢时会误判,脚本现在每次都重试,
只有--retries次全部判 NOPDF 才写进未下载清单.txt。 a.pdfbtn是渐进渲染的:确认存在后要再观察约 1 秒取观察到的最大数量,
否则「站点提供几份」与版本命名会抖动。- 自动登录成功后要"铺开登录态"再检索(V1.6):实测刚自动登录成功那一小段时间里,
检索会偶发空结果——同一号码可能连续 3 次 0 命中并被误记成「查无此号」,
而复跑立刻命中。所以自动登录后回首页等待约 4 秒并再确认一次登录态,才进正式检索。
(ensure_login里已实现;若日志出现连续无命中,先怀疑这一条。) - 智能检索会整体返 0,必须留「号单检索」兜底(V1.6.4 实测):站点「智能检索」
会对所有查询返回检索结果共 [ 0 ] 条——连"电池"这种常见词也是 0,而同一时刻
「号单检索」用同一账号正常返回(CN202111194234.0 命中 1 条)。所以
collect_by_pubnums现在在「智能检索 0 命中 且请求号是申请号」时自动改走
list_search()(/Search/ListSearch→ 填#TxtSearchCn→ 点
button[onclick*="TxtSearchCn"])。号单检索结果页
(/Search/ListSearchResult)的列表结构与智能检索一致
(#listcontainer > div/label.title-color[title]/ 行内含申请号·公开号·公告号),
故取址与下载环节零改动。号单检索只吃申请号标准格式,公开号/公告号没有这条兜底;
--query关键词模式也没有兜底(号单检索不支持关键词)。 - 若某记录确实没有 PDF,说明书与权利要求通常仍在线:
#dscontainer/
#clcontainer仍可能有全文(实测 13061/3793 与 6848/1098 字节)。
降级交付(导出说明书+权利要求)尚未实现,列为待办。 - 收尾绝不能只调
session stop:实测会留下再也无法关闭的孤儿窗口(会话
记录已清、窗口还在,而bsk window只有resize,没有 close)。必须走
close_agent_window()—— 先tab list取scope=agent的标签逐个
tab close,Chrome 在窗口最后一个标签关闭时销毁窗口、bsk 同时注销会话;
之后session stop报session is not registered属正常终态。收尾必须
以assert_no_sessions_left()断言会话表为空,禁止静默失败。
另:tab list会同时列出用户标签,只允许关scope=agent的,关用户标签会被
沙箱拒绝(operation denied by the Agent Window sandbox)。
详细站点结构、选择器、验证过程见 references/site-notes.md。
版本记录
V1.7.2(2026-10-10)
主题:会话绑定哪个浏览器 —— 新增 --browser。
(面向 Chrome 与 Edge 都装了扩展、或同一浏览器多 profile 的场景)
- 新增
--browser参数(patentstar_download.py):指定会话绑定哪个浏览器实例,
取值instance_id/ 唯一标签 /auto,对应bsk session start --browser。 - 决策规则:0 个实例 → 报错并给装扩展指引;1 个 → 交给 bsk 默认;
≥2 个且未指定 → 中断,打印实例清单与用法。不硬猜——绑错会白跑一趟,
且现象表现为「查无此号」,极难排查。auto= 自动选第一个可用实例(省事但不推荐)。 env_check.py的[3] 扩展连接改为逐个打印实例(id / 浏览器 / 版本 / 标签),
实例 >1 时直接给出带--browser的示例命令。install_extension.py:--browser both的步骤顺序改为 Edge 优先(无梯子可达、
Windows 自带);--open只打开第一个浏览器,不再同时弹两个窗口。- 小修:
print("输出目录…")加flush=True(重定向到管道时 stderr 会抢在 stdout 前面)。 - 实测背景:多实例是常态——本机 Chrome 与 Edge 都装了 BrowserSkill 后,
bsk browsers同时列出两个实例(28775e18chrome /7ac34d1aedge)。 - 台本版本号与脚本
VERSION同步升到 1.7.2。
V1.7.1(2026-10-10)
主题:新用户环境引导(面向没装 Chrome / 没装 bsk / 没注册专利之星的用户)。
- 新增
scripts/env_check.py:五层探测(bsk CLI / daemon / 扩展连接 / 可用浏览器 /
扩展是否已装),输出人话报告(--json给机器读),并按缺失项给出下一步,不靠瞎猜。 - 新增
scripts/install_extension.py:解决 Chrome 网上应用店国内不可达导致装不上扩展的问题。
流程:GitHub API 取最新ext-v*发布包 → 下载 → 校验(zip 完整性 /name含 BrowserSkill /
MV3 / 有 background)→ 解压到~/.workbuddy/bsk-extension/v<版本>/→ 打印 Chrome 与 Edge
两套操作步骤。可选--open直接打开扩展管理页、--make-bat生成可转发给同事的一键脚本、
--zip用本地包离线装。
用户只剩三步:开开发者模式 → 点「加载已解压的扩展程序」→ 选中脚本给出的目录。 - 新增
references/onboarding.md:三条安装路径(Edge 商店 / 离线包 / Chrome 商店)、
专利之星注册与登录、每步的"成功长什么样"、失败排查表、agent/用户分工三档、话术模板、
Edge↔Chrome 差异速查表。 - 回复约定补一条例外:环境未就绪时不再收敛输出,按引导手册展开。
- 实测依据(详见《专利之星skill-新用户前置调研.md》):
- 中国 5 节点(京/深/蒙/黑/滇)拨测:
chromewebstore.google.com5/5 BLOCKED;
microsoftedge.microsoft.com、raw.githubusercontent.com、objects.githubusercontent.com
5/5 可达(对照组baidu.com全通,证明工具可信) - 本机 Chrome 155 实测
--load-extension已失效(基线 vs 实验组扩展 target 无增量);
Edge 155 仍可用,但不持久(浏览器重启即失效)且用户仍须手动开连接开关 → 不采用 - 本机自测无区分力:DNS 被 TUN 接管,
baidu.com/google.com一律解析到198.18.x.x - 台本版本号与脚本
VERSION同步升到 1.7.1;新增两个脚本均为纯标准库,无第三方依赖。
V1.7.0(2026-10-10)
主题:修「号单检索」误点「清空」按钮;并把三处版本号统一为 1.7.0。
- 事故(V1.6.4 实测):号单检索兜底用 button[onclick*="TxtSearchCn"] 找提交按钮,
但同页「清空」按钮的 onclick="clearTxt('TxtSearchCn')" 同样含 TxtSearchCn,
且它在 DOM 里排在前面 → 每次点击都点到「清空」:输入框被清空,既没开新标签
也没报错,表现为"点了没反应"。
- 修法:改用双属性 AND 选择器
SEL_LIST_SEARCH_BTN = 'button[onclick*="getResult"][onclick*="TxtSearchCn"]'
(CSS 两个属性选择器可做 AND);JS 兜底点击 JS_CLICK_CALC 按同规则过滤
(必须同时含 getResult)。
- 点击仍是两级:先 bsk.click 受信任点击(window.open 需要用户手势),
没出新标签再用 JS_CLICK_CALC 直接 click() 兜一次。
- 版本号统一:此前同一份代码挂着三个号——目录名 V1.6.3、台本 1.6.4、脚本
VERSION 1.6.5;本次一次性对齐为 1.7.0:目录名、frontmatter name/version、
H1 标题、脚本 VERSION 四处一致。台本版本号与脚本 VERSION 同步升到 1.7.0。
> 注:上一条选择器修复为版本号合并时依脚本注释补记(当日未留台本记录),
> 与脚本实际改动一致;若另有遗漏改动请补上。
V1.6.4(2026-10-10)
主题:智能检索整体 0 命中时的兜底(当日实测站点故障)。
- 现象:智能检索对任意查询都返回 检索结果共 [ 0 ] 条("电池"也是 0),
但 URL 里的 CurrentQuery 已正确带上 base64 编码的查询串 → 前端没问题,
是站点检索后端整体空结果。此时旧流程会把每个号都误记成「查无此号」。
- 新增 list_search() 兜底:智能检索 0 命中且请求号是申请号时,改走
「号单检索」(/Search/ListSearch,填 #TxtSearchCn,点
button[onclick*="TxtSearchCn"] ←⚠️ 此选择器有误,已在 V1.7.0 修正),
命中后照原路取址下载。结构同构,下游零改动。
- 实测:CN202111194234.0 智能检索 4 次全 0 → 兜底命中 1 条 → 26.4s 拿到
公开文本 1.53 MB + 授权文本 3.83 MB。
- 台本版本号与脚本 VERSION 同步升到 1.6.4。
V1.6.3(2026-10-10)
主题:稳定优先(交付给用户时速度不敏感、稳定敏感)。
- 限流自适应:nav 计时,单次导航 >5s 记一次「站点变慢」,<2.5s 清零;
Bsk.pace() 给出 0–3 秒缓冲,加在检索后等待与每次重试之前。站点慢时自动放慢。
- 重试退避:记录级重试不再连打——无命中/取址失败先按
min(4s, 尝试数×1.2s) + pace 退避再试;默认 --retries 由 3 提到 4。
- 每次 evaluate 的预算必须压到 30 秒 RPC 上限之下(重要教训):bsk evaluate
没有 --timeout 参数,单次超 30 秒整条 RPC 就废(tool RPC timed out after 30s)。
GET_PDF_URL_JS 的内部等待从最坏 ~20 秒压到 9 秒(等 #itemPdf 10s→4s、
找链接 10s→5s),站点慢时不再顶到上限。
- 「为什么出错」归类:结尾按原因分组打印(站点未收录 / 站点响应异常超时 /
站点不提供全文 / 号码格式),并写进 未下载清单.txt;取址失败会带出页面真实返回
(如 timeout[TAB_NOT_ACTIVE])。
- 实测:4 个不同类型号码 × 2 轮 = 8/8 全成功,且每轮都出现 1–2 次 TAB_NOT_ACTIVE
重试、全部被退避吸收、无记录丢失;单条 12.5–15.1s(比 V1.6.2 略慢,换稳定性)。
- 台本版本号与脚本 VERSION 同步升到 1.6.3。
V1.6.2(2026-10-10)
- 后台运行:
session start默认加--no-focus—— 开窗不抢焦点,跑的时候你可以继续
干别的。已实测后台窗口下bsk download(受信任点击)照常落盘。要抢焦点用--focus。 - 修 CDP detach 回归(重要):为"确保标签激活"而反复
tab.click()是错的——
多出来的点击会触发页面导航,把正在跑的长 evaluate 打成Detached while handling,
任务确定性中断(实测连续 3/3 全挂,且换--focus也一样)。现在只点一次,
靠循环观察;一直没激活就早退交给外层重试。 - 早退省时:
TAB_NOT_ACTIVE不再白等满 12 秒,约 2 秒即返回重试。 - 错误原因可读:取址失败会把页面的真实返回带进原因(如
取址失败(timeout[TAB_NOT_ACTIVE])),便于回答"为什么出错"。 - 记录一条死路:结果页 URL 的
CurrentQuery = base64(号码 + "/YY")编码已实测恒定
(5 个号码精确命中),直接导航也能拿到正确结果页;但会让 CDP 会话持续 detach,
加重试与 reload 都救不回来 → 已回退经典检索,result_url()保留待日后重试。 - 实测:4 个不同类型号码 × 3 轮 = 12/12 全部成功,单条记录 10.1–14.2s。
- 台本版本号与脚本
VERSION同步升到 1.6.2。
V1.6.1(2026-10-10)
- 按记录去重:同一件专利给了多个号(申请号/公开号/公告号)时,保留首次出现的那条
记录,把各请求号要的版本取并集再下,不再把同一份 PDF 下两遍。记录身份优先用
申请号,其次公告号、公开号;命令行打印[去重] N 个请求号指向同 M 条记录。 - 写入「回复约定」:默认工具化响应——不逐步汇报、不预确认、不复述技能内容;
成功只回"目录 + 份数 + 文件清单",异常才展开 1–2 句。 - 修回归:版本并集为空时必须回退。实测踩到——用公告号请求一条只有 1 份 PDF
的记录(实用新型 U / 外观 S 都是单份)时,并集{2}被s <= len(urls)全部过滤掉,
结果一份都不下(CN203066857U、CN3003001S 全军覆没)。现在并集为空就回退取现有那份,
并打印[注意] 记录只有 N 份 PDF,按现有份数下载。 - 修退出码:
0 if len(ok_files) == len(units)在"有记录但 0 份"时是0 == 0→ 假成功。
现在要求units非空且全部成功才返回 0。 - 台本版本号与脚本
VERSION同步升到 1.6.1。
V1.6(2026-10-10)
- 文件名直接写版本名(收益最大的一条):知道
[1]=公开 / [2]=授权之后,
发明多份时不再叫..._p1/_p2.pdf,而是..._公开文本.pdf/..._授权文本.pdf
—— 不用打开 PDF 就知道哪份是哪份。
安全边界:只在发明(申请号种类号 K=1/8)且正好 2 份时这样命名;
实用新型/外观若出现 2 份,其语义未验证,回退_pN(is_invention()+version_label())。 - 新增
--pdf-version public|granted|all:用「要哪一版」代替「取第几份」,
比--pdf-index 2直观得多。优先级:--all-pdf/--pdf-index>--pdf-version> auto。 - 两条版本推断提示:给公开号 A 且站点有 2 份 → 提示
该申请已授权:公告号 CN…B;
给申请号但只有 1 份 → 提示仅解析到公开文本(可能尚未授权)。对代理人"授权了没"这个
高频问题直接给答案。 - CSV 新增「版本」列(公开文本 / 授权文本 / 第N份)。
- 自动登录后加"铺开登录态"等待:实测刚自动登录成功时检索会偶发空结果,
同一号码连续 3 次 0 命中被误记为「查无此号」,复跑立刻命中。现在自动登录后
回首页等约 4 秒并复核登录态,才进正式检索。 - 台本版本号与脚本
VERSION同步升到 1.6。
V1.5(2026-10-10)
- 多份 PDF 按号码类型自动选版本。发明记录站点固定提供 2 份:
[1]=公开文本、
[2]=授权文本(已用 pypdf 读下载文件的首页文本核实:第 1 份印
「(10)申请公布号」,第 2 份印「(10)授权公告号」)。现在:
申请号 → 两版都下;公开号(A) → 第 1 份;公告号(B/C/U/Y/S) → 第 2 份。
--pdf-index N/--all-pdf仍可显式覆盖;命令行会标注每份是哪一版。 - 取址改为「链接优先、embed 兜底」。起因:站点会记住上次选过的版本,
点开标签时可能直接 embed 那一份——先认 embed 就绕过了按下标选版本,实测按
公告号请求时只解析出 1 份。现在先找a.pdfbtn,单份记录才用 embed(已有
embed 后再宽限约 1.5 秒等链接)。 - 命名重复缺陷修复:用户给申请号时
{pubnum}与{an}相同,文件名会出现
两次同一串号码(..._CN202111194234.0_CN202111194234.0.pdf);现在回退用
站点的公告号/公开号 →..._CN202111194234.0_CN113983326B_p1.pdf。 --out改为可选:省略时每次运行自动新建专利下载<年月日时分秒>(例
专利下载20261010012846),一次指令一个目录,多条指令的产物不互相覆盖。- 修正「本次取 N 份」措辞——那是解析到的份数,不是最终下载份数。
- CDP detach 恢复:站内首页用
document.write注入第三方统计脚本,会触发文档
重解析,把正在执行的 evaluate 打断成browser rejected the underlying CDP call/
Detached while handling;原地重试 4 次全废(实测连续两次任务因此中断)。
现在每次重试前先reload一次重置卡住的 DevTools 会话,并在检索前等
document.readyState == complete。 - 台本版本号与脚本
VERSION同步升到 1.5。
V1.4(2026-10-09)
- 号单输入自动归一化(
normalize_number+ 升级版load_numbers)。
起因:用户输入号码五花八门——带/不带 CN、发明公开号/授权公告号、实用/外观
授权公告号、申请号带/不带小数点,还有空格、全角、连字符、括号、ZL前缀等。
对站点智能检索实测(16 例脏号码 + 18 例格式矩阵)后确定规则:
· 补CN、统一大写;去空格/全角/连字符/括号/中文标签
·ZL前缀 = 专利号 = 申请号,去掉ZL按申请号处理
· 13 位连写申请号在第 12 位后补.(校验位就是最后一位,无需计算) - 分类 + 拒绝而非硬猜:文献号缺字母(按种类号给候选 A/B、U/Y、S)、
申请号缺校验位、2003 年前老申请号(实测站点不收录)——逐条打印[跳过]与原因。 - 「查无此号」与「格式错」分开:格式合法但站内无记录(伪造/示例号)单独标注,
不再混在"取址失败"里。 - 分隔符不再用空格(号码内部可能含空格),改为换行/逗号/分号/中文标点。
- 修正
matched判断:请求号是申请号时也认作命中,不再误报"站点号"。 - 顺带修掉"重试 3/2"这类 off-by-one 的尝试计数显示。
- 台本版本号与脚本
VERSION同步升到 1.4;--pubnums帮助与参数表同步。
V1.3(2026-10-09)
- 修掉一个会静默丢件的取址缺陷。起因:5 件号单里 CN101214869B 与
CN107150160A 被判「站点未收录全文 PDF」,而同两件在 WorkBuddy 侧能下成功。
看 DOM 才定位到:站点「全文PDF」标签有两种形态,脚本只认第一种。
· 形态 A(单份):<embed src>直接出现 —— 旧逻辑能处理。
· 形态 B(多份):渲染的是若干a.pdfbtn,地址只在data-url属性上
(href与onclick都是空的),必须先点其中一个 embed 才出现。
旧逻辑既不看a.pdfbtn,兜底又去查#pdfcontainer——而那个容器在真实 DOM
里始终为空,于是形态 B 全被误判成「站点未收录全文」。修前 3/5,
修后 5/5,耗时 57.6s → 44.6s。 - NOPDF 改为重试后才采信:站点渲染慢时会误判,而误判会永久跳过一件专利
(dry-run 已复现)。现在每条都重试,只有--retries次全判 NOPDF 才记账。 - 多份 PDF 支持:新增
--pdf-index(默认 1)与--all-pdf;一条记录多份时
命名为<基名>_pN.pdf,单份时命名不变(与既有产物兼容)。 a.pdfbtn渐进渲染:确认存在后再观察约 1 秒取最大数量,否则「站点提供
几份」与_pN命名会抖动。- 报告口径订正:「站点未收录全文 PDF」→「该记录站点无 PDF 全文」;
未下载清单.txt表头不再断言「非脚本失败」,改为逐条标注原因。 - 取址判定补一道闸:标签未真的被点开时返回可重试的
TAB_NOT_ACTIVE,
不再当成「没有 PDF」。 - 顺带修掉:CSV「下载 ms」原本是批次累计值,现在每份记自己的耗时;
请求号与公开号/公告号的比对改为两者都认(V1.3 首跑曾把命中的公告号误报成
「站点号」);检索命中多条且第一条不是目标件时开始告警,不再静默取第一条。 - 台本版本号与脚本
VERSION同步升到 1.3;参数表补上此前遗漏的--retries。
V1.2(2026-10-09)
- 收尾改为「先关 Agent Window 标签、再注销会话」。起因:一次 5 件号单任务
跑完后留下 4 个孤儿窗口,而bsk session list已为空——session stop清了
会话记录却没关掉窗口,且事后无任何命令能关(bsk window只有resize)。 - 新增
close_agent_window():bsk tab list枚举后只关scope="agent"的
标签;Chrome 在窗口最后一个标签关闭时销毁窗口,bsk 同时注销会话。实测
窗口数 4 →(开)5 →(关标签)4,session list归零。 session_stop()容忍session is not registered(关标签后的正常终态),
只对真失败重试一次。- 新增
assert_no_sessions_left():收尾断言bsk session list --json为空,
否则原样打印残留并告警。此前的实现「发完就不管」,静默失败无人知晓。 - 修掉
tab_close()死代码(此前定义但从未调用、并丢弃返回值)。 - 台本版本号与脚本
VERSION同步升到 1.2。
DSH 移植(2026-10-09)
- 从 WorkBuddy
~/.workbuddy/skills/专利之星-专利下载V1.1/导入。 - 技能名由
专利之星-专利下载V1.1改为patentstar-patent-download:
DSH 的技能名正则要求^[a-z0-9]+(?:-[a-z0-9]+)*$,中文名与点号会被直接拒收。 version/agent_created移入metadata(DSH 只识别metadata对象)。- 正文改动仅限:Python 执行方式改为使用 DSH 内置运行时、
--out路径写法、
bsk daemon 需在宿主终端启动、--bsk显式指定。站点知识、选择器、
关键约束与脚本本体未改动。
V1.0(2026-10-09)
- 关键词检索批量下载;
--query / --result-url / --count / --start / --page-size / --name-template / --fetch-batch / --timing / --dry-run。 - 定型链路:合并式取址(1 次 evaluate 完成「等详情页 → 切全文PDF → 轮询 embed」)、
api 域普通页 + 同源 fetch + 受信任点击直写目标路径、%PDF-校验 + 重试、
CDP 瞬时错误退避重试、点击确认(轮询location.pathname)。 - 实测 10 条约 12.8s/条(对照逐条串行点下载约 58s/条)。
V1.1(2026-10-09)
- 新增号单模式
--pubnums/--pubnums-file:按公开号/公告号清单精确下载。
站点「号单检索」只吃申请号标准格式,故实现为逐个用智能检索命中(公开号/公告号
均唯一命中,含 B/U/Y 尾的授权公告号),默认命名{idx:02d}_{title}_{an}_{pubnum}。 - 新增「站点未收录全文」判定:
#pdfcontainer为空即判NOPDF,属不可重试项,
直接跳过并生成未下载清单.txt,不再白重试 3 轮。 - 修复:号单模式下
stage_and_download因耗时字典未预填而 KeyError,导致整批在
下载阶段中断;改为timing.setdefault(idx, {})并在收集阶段预填。 - 元数据解析补上公告号(
gn),耗材 CSV 增加「公开号 / 公告号 / 请求号码」三列。 - 实测:5 个号码(CN101214869B、CN204416150U、CN209137395U、CN2873190Y、CN107160150A)
拿到 4 件,63.8s;缺的那件是站点未收录全文。