文献全文下载(paper-fulltext-fetch)
0. 默认前提
用户场景默认是学术研究 / 个人学习与教学:不询问用途、不附加免责声明。
唯一始终成立的硬约束是「不再分发」——把文件取回本机属于「使用」,
把受版权保护的原件打包、上传、批量跨机分发属于「再分发」,本技能只做前者。
切换条件:用户主动说明用于商业发布。
1. 三十秒上手
PY=python3 # 无第三方依赖,系统 python3 即可
S=scripts/paper_fetch.py
# DOI(最推荐,最精确)
$PY $S 10.1371/journal.pone.0000217 --email you@example.com -o papers
# PMID / PMCID / arXiv ID
$PY $S 23903748 --email you@example.com
$PY $S PMC4221854 --email you@example.com
$PY $S 1304.1068 --email you@example.com
# 标题(模糊,可能误命中;拿到结果请核对)
$PY $S "Nanometre-scale thermometry in a living cell" --title --email you@example.com
# 只要题录 / 要 BibTeX
$PY $S 10.1371/journal.pone.0000217 --meta-only --bibtex
# 想看清每个源为什么失败
$PY $S 10.1038/nature12373 --email you@example.com --explain
--email 是 Unpaywall / OpenAlex 的礼貌要求,建议始终带上(也可设 PAPER_EMAIL 环境变量)。
不带 email 会跳过覆盖最广的两个源,脚本会提示。
2. 为什么必须做 PDF 魔数校验(本技能的存在理由)
任何索引给出的「PDF 链接」都不能直接信。已实测的两个反例:
| 现象 | 实测证据 |
|---|---|
| 索引声称是 PDF,实际是付费墙页 | Unpaywall 对 10.1038/nature12373 的 url_for_pdf 返回 HTTP 200 + 333,553 字节 text/html(出版社 grade-c 页面)。只看状态码会误判成功 |
| 索引声称是 PDF,实际是反爬页 | PMC 的 /pmc/articles/PMCxxxx/pdf/ 返回 1,817 字节 HTML(浏览器校验页) |
所以判定标准只有一条:响应体以 %PDF- 开头且 ≥ 10 KB。
不满足就记下原因、换下一个源。这条由代码强制,不靠人工判断。
3. 通道清单与实测状态
| 通道 | 提供的价值 | 实测状态 |
|---|---|---|
| Crossref | DOI → 题名/作者/刊名/年/出版社链接 | ✅ 稳定 |
| arXiv | 裸 arXiv ID 与 10.48550/arXiv.* → 预印本 PDF |
✅ 稳定,命中率最高 |
| OpenAlex | best_oa_location.pdf_url + 全量 locations |
✅ 稳定 |
| Unpaywall | OA 定位器,oa_locations 列表(含仓储版) |
✅ 稳定,链接需校验 |
| Semantic Scholar | openAccessPdf |
✅ 稳定 |
| Europe PMC | 元数据、fullTextUrlList 免费声明、PMID→PMCID |
✅ 元数据可用 |
| Europe PMC PDF 渲染 | ?pdf=render |
⚠️ 本网络返回 403(源仍在列,换网络可能可用) |
| PMC 原生 PDF | /pmc/articles/PMCxxxx/pdf/ |
⚠️ 遇反爬返回 HTML,靠魔数校验拦掉 |
| PLOS 直链 | journals.plos.org/...&type=printable |
✅ 稳定 |
| bioRxiv / medRxiv | 10.1101/* 预印本 |
⚠️ api.biorxiv.org 在本网络连不通;PDF 直链仍可用 |
| DOAJ | OA 期刊题录 | ✅ 可用(低频使用) |
要点:单一来源失败是常态,不要因为一个源 403 就下结论说「下不到」。
4. 输入支持与解析规则
| 输入形态 | 处理 |
|---|---|
10.xxxx/yyyy DOI |
直连 Crossref;并反查 Europe PMC 补 PMID/PMCID |
| 6–9 位纯数字 | 当 PMID |
PMC + 数字 |
当 PMCID |
1304.1068 / arXiv:1304.1068 / 旧式 hep-th/9901001 |
当 arXiv ID |
http(s)://... |
取链接;若以 .pdf 结尾则优先直取 |
| 其它字符串 | 当标题(建议显式加 --title) |
解析结果会同时写入报告,可随时核对是否解析错了。
5. 输出物
每篇成功下载产出两个文件:
<题名>.pdf—— 已通过魔数校验的全文<题名>.json—— 题录 + 溯源(source/source_note/url/pages/doi/pmid/arxiv)
溯源字段很重要:同一篇论文可能有出版社版、仓储版、预印本版三个版本,
source_note 会标明拿到的是哪一版(如 repository/submittedVersion)。
6. 可选:代查通道(需自备 token)
对真付费墙且无预印本的文献,开放获取通道必然失败。此时可启用代查通道:
B=scripts/blackbox_client.py
$PY $B --config-init # 生成 ~/.config/paper-fulltext/config.json
# 在配置里填 api_base 与 api_token(或 export PAPER_API_BASE / PAPER_API_TOKEN)
$PY $B --selftest # 先只做鉴权自检,不消耗额度
$PY $B --doi 10.1111/j.1467-6486.2010.00937.x --email you@example.com
$PY $B --title "..." --email you@example.com
$PY $B --isbn 9780199357789 --type book --email you@example.com
该通道的协议(提交一次 → pending 轮询 poll_url → ready 后取 download_url)
已完整实现,含三个已实测的坑:
- 必须带稳定的
X-Device-ID。服务端限制「最多两台设备」,
本地生成一次 UUID 后持久化到配置,不要每次随机,否则很快耗尽名额。 - 鉴权失败返回的是
body.status = invalid_token,不是 HTTP 401/403 的标准语义。
只看状态码会误判,必须读响应体的status字段。 - 同一篇只提交一次。失败即报告原因,不重复提交。
> 服务地址与 token 由用户自行提供。公开文章里给出的 token 会随作者轮换而失效,
> 一旦 --selftest 报 invalid_token,唯一解法是找作者要新 token,不是换网络重试。
7. 已知坑(都踩过,不必再踩)
export.arxiv.org对不带Connection头的请求一律返回 406 Not Acceptable。
同一 URL、同一 UA,补上Connection: close立刻 200。curl 默认带这个头,所以
「curl 正常、Python 报 406」的诡异现象会持续误导排查方向。脚本已全局补上。- arXiv API 不能用浏览器 UA,用
API_UA(非浏览器标识);下载 PDF 才用浏览器 UA。 - Europe PMC 按
TITLE:检索会把 F1000「Faculty Opinions recommendation of ...」
排在真实论文前面。 直接取第一条会下错文献。脚本按词覆盖率打分并对
Faculty Opinions / recommendation of / comment on / erratum开头的结果扣分。 - 标题匹配度 < 0.6 会告警,但 0.6–0.8 仍可能误命中——能拿到 DOI 就别用标题检索。
- 不要用 HTTP 状态码判断是否拿到 PDF,见第 2 节。
- 索引的
is_oa=true只表示「有 OA 记录」,不保证链接当前可用(出版社可能已改策略)。
8. 与其它技能的分工
- 找文献(检索式、题录池、去重分级)→
lit-search-openalex-eric - 取全文(把已知的标识符变成 PDF)→ 本技能
- 读文献(解析、精读报告)→ 用对应精读类技能
典型串法:先用检索技能产出 DOI 清单,再逐条喂给本技能下载。
9. 边界
- 本技能只落地文件到本机,不分发、不打包、不上传。
- 不提供绕过出版社反爬的「技巧」;付费墙文献的正规补足路径是机构订阅、图书馆文献传递、联系作者。
- 主流公共文献站的公开镜像目前普遍挂 DDoS-Guard / ALTCHA 人机校验(实测
.st返回 403、
.ru返回 robot check 页),无法 headless 自动化——这不是配置问题,别在这上面耗时间。