| name | download-fulltext-pdf |
| description | 当用户明确要求"下载文献全文"或"获取论文PDF"时使用。通过 DOI 号下载学术论文全文 PDF,支持 arXiv、Sci-Hub、Unpaywall、期刊官网等多源策略。⚠️ 不适用:用户只是想解析或处理已有的 PDF 文件(应使用 pdf skill)、只是想搜索论文信息而无需下载全文、没有提供 DOI/标题/BibTeX 任何标识符。 |
| metadata | {"author":"Bensz Conan","keywords":["download-fulltext-pdf"]} |
下载文献全文 PDF
BenszAPI 任务工作区
本 Skill 的新任务中间文件统一写入 ./.bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/{skill名}/input|output|log/。同一任务复用一个任务根目录;多 Skill 协作才创建 shared/。正式交付物不写入该目录,历史隐藏目录只允许显式兼容读取、迁移或清理。
与 bensz-collect-bugs 的协作约定
- 因本 skill 设计缺陷导致的 bug,先用
bensz-collect-bugs 规范记录到 ~/.bensz-skills/bugs/,不要直接修改用户本地已安装的 skill 源码;若有 workaround,先记 bug,再继续完成任务。
- 只有用户明确要求“report bensz skills bugs”等公开上报时,才用本地
gh 上传新增 bug 到 huangwb8/bensz-bugs;不要 pull / clone 整个仓库。
通过 DOI 号或其他标识符下载学术论文全文 PDF,确保"一定要获得文献PDF"。
核心工作流
1. 输入验证与规范化
必需参数:
doi: DOI 号(如 10.1038/nature09492)
output_path: 输出目录或完整 PDF 文件路径
可选参数:
title: 论文标题(作为 DOI 的备选)
bibtex: BibTeX 条目(包含 DOI 或标题信息)
验证逻辑:
- DOI 格式规范化(移除多余空格、补全前缀)
- 输出路径检查(目录存在性、写入权限)
- 至少提供一种标识符(DOI/title/bibtex)
2. 多源下载策略
优先级顺序(按成功率和速度排序):
| 优先级 | 数据源 | 优势 | 局限 | 触发条件 |
|---|
| 1 | arXiv | 稳定可靠 | 仅限预印本论文 | 检测到 arXiv ID(如 10.48550/arXiv.*) |
| 2 | Sci-Hub | 覆盖较广、速度快 | 可能有 CAPTCHA | 非 arXiv 或 arXiv 失败 |
| 3 | Unpaywall | 合法开放获取(OA) | 取决于论文是否 OA | Sci-Hub 失败时兜底 |
| 4 | 期刊官网 | 合法渠道 | 常需订阅/登录 | 最后兜底 |
策略执行:
1. 如 DOI 可解析出 arXiv ID → 优先 arXiv
2. 尝试 Sci-Hub(通过 scihub 库)
3. 尝试 Unpaywall(合法 OA 获取)
4. 尝试 DOI 落地页并猜测常见 PDF 路径(期刊官网兜底)
3. 下载后验证
必需检查(由 scripts/verify_pdf.py 处理):
失败处理:
- 验证失败 → 记录错误 → 尝试下一个数据源
- 所有源失败 → 返回详细错误报告
4. 输出格式
成功时:
✅ 成功下载论文全文
**来源**: Sci-Hub
**DOI**: 10.1038/nature09492
**文件**: /path/to/paper.pdf
**大小**: 2.3 MB
失败时:
❌ 无法下载论文全文
**尝试过的源**:
- Sci-Hub: CAPTCHA 验证失败
- arXiv: 非 arXiv 论文
- Unpaywall: 无开放获取版本
- 期刊官网: 未找到直接 PDF 链接
**建议**:
1. 手动访问 Sci-Hub 并完成 CAPTCHA 验证
2. 检查 DOI 是否正确
3. 尝试通过期刊官网获取
AI 动态判断
AI 需要动态处理的场景:
| 场景 | 处理方式 |
|---|
| CAPTCHA 检测 | 识别错误消息中的 "captcha" 关键词 → 切换数据源 |
| 网络超时 | 增加重试次数(最多 3 次)或切换源 |
| 404/未找到 | 直接切换下一个源,不重试 |
| 文件损坏 | 删除已下载文件,尝试下一个源 |
| arXiv 检测 | DOI 中包含 arXiv ID → 优先使用 arXiv 源 |
硬编码操作(scripts/)
scripts/validate_input.py
输入验证与规范化脚本:
import sys
import re
from pathlib import Path
def normalize_doi(doi: str) -> str:
"""规范化 DOI 格式"""
doi = doi.strip()
if not doi.startswith("10."):
doi = f"10.{doi}"
return doi
def validate_output_path(path: str) -> Path:
"""验证输出路径"""
path = Path(path).expanduser().resolve()
if path.exists() and path.is_dir():
return path / "paper.pdf"
if path.parent.exists():
return path
raise ValueError(f"输出路径无效: {path}")
def main():
if len(sys.argv) < 3:
print("用法: python validate_input.py <doi> <output_path>", file=sys.stderr)
sys.exit(1)
doi = normalize_doi(sys.argv[1])
output_path = validate_output_path(sys.argv[2])
print(f"DOI:{doi}")
print(f"OUTPUT:{output_path}")
__name__ == :
main()
scripts/download_pdf.py
核心下载逻辑(多源策略):
import sys
import requests
from pathlib import Path
from typing import Optional, Tuple
try:
from scihub import SciHub
HAS_SCIHUB = True
except ImportError:
HAS_SCIHUB = False
class PDFDownloader:
"""多源 PDF 下载器"""
def __init__(self, doi: str, output_path: Path):
self.doi = doi
self.output_path = output_path
self.sources_tried = []
def download_from_scihub(self) -> Tuple[bool, str]:
"""从 Sci-Hub 下载"""
if not HAS_SCIHUB:
return False, "scihub 库未安装"
try:
sh = SciHub()
result = sh.download(self.doi, path=str(self.output_path))
if result and self.output_path.exists():
return True, "Sci-Hub"
return ,
Exception e:
error_msg = (e).lower()
error_msg:
,
, (e)
() -> [, ]:
arxiv_id = ._extract_arxiv_id()
arxiv_id:
,
url =
:
response = requests.get(url, timeout=)
response.status_code == :
.output_path.write_bytes(response.content)
,
,
Exception e:
, (e)
() -> [, ]:
url =
:
response = requests.get(url, timeout=)
response.status_code == :
data = response.json()
data.get():
pdf_url = data.get(, {}).get()
pdf_url:
pdf_resp = requests.get(pdf_url, timeout=)
pdf_resp.status_code == :
.output_path.write_bytes(pdf_resp.content)
,
,
Exception e:
, (e)
() -> []:
() -> [, , ]:
.sources_tried.append()
success, source = .download_from_scihub()
success:
, source,
.sources_tried.append()
success, source = .download_from_arxiv()
success:
, source,
.sources_tried.append()
success, source = .download_from_unpaywall()
success:
, source,
, ,
():
(sys.argv) != :
(, file=sys.stderr)
sys.exit()
doi = sys.argv[]
output_path = Path(sys.argv[]).expanduser().resolve()
downloader = PDFDownloader(doi, output_path)
success, source, message = downloader.download()
success:
()
()
()
:
(, file=sys.stderr)
sys.exit()
__name__ == :
main()
scripts/verify_pdf.py
PDF 验证脚本:
import sys
from pathlib import Path
def verify_pdf(path: Path) -> Tuple[bool, str]:
"""验证 PDF 文件完整性"""
if not path.exists():
return False, "文件不存在"
if path.stat().st_size == 0:
return False, "文件大小为 0"
with open(path, "rb") as f:
header = f.read(5)
if header != b"%PDF-":
return False, "无效的 PDF 文件头"
try:
import PyPDF2
with open(path, "rb") as f:
PyPDF2.PdfReader(f)
return True, "有效 PDF"
except ImportError:
return True, "未安装 PyPDF2,跳过深度验证"
except Exception as e:
return False, f"PDF 解析失败: {(e)}"
():
(sys.argv) != :
(, file=sys.stderr)
sys.exit()
path = Path(sys.argv[]).expanduser().resolve()
is_valid, message = verify_pdf(path)
is_valid:
()
sys.exit()
:
(, file=sys.stderr)
sys.exit()
__name__ == :
main()
配置参数(config.yaml)
download:
scihub:
enabled: true
timeout: 30
retries: 3
arxiv:
enabled: true
timeout: 30
unpaywall:
enabled: true
emails:
- "hwb2012@qq.com"
- "huangwb886@gmail.com"
email_strategy: "round_robin"
state_file: ".bensz-api/task-{yyyymmdd-hhmm}-{简短描述}/download-fulltext-pdf/state/unpaywall_email_state.json"
email: ""
verification:
check_pdf_header: true
require_pypdf2: false
max_file_size: 100_000_000
output:
default_filename: "paper.pdf"
overwrite: false
依赖说明
必需依赖:
可选依赖(自动安装):
scihub(Sci-Hub 支持,首次使用时自动安装)
PyPDF2(PDF 验证,推荐安装)
自动安装机制
当 AI 执行下载任务时,scripts/download_pdf.py 会:
- 检测缺失的可选依赖
- 提醒用户将要安装的包(如
📦 检测到缺失依赖: scihub)
- 自动安装(使用
pip install)
- 安装失败时自动降级到其他数据源
用户无需手动安装,skill 会"开箱即用"。
手动安装(可选)
如果自动安装失败,用户可以手动安装:
pip install requests scihub PyPDF2
使用示例
示例 1:使用 DOI 下载
/skill download-fulltext-pdf "10.1038/nature09492" ./downloads/
示例 2:使用标题下载
/skill download-fulltext-pdf --title "Deep Learning" ./downloads/
示例 3:批量下载(从 BibTeX 文件)
/skill download-fulltext-pdf --bibtex references.bib ./downloads/
错误处理
| 错误类型 | 表现 | 处理方式 |
|---|
| CAPTCHA | Sci-Hub 返回验证码页面 | 切换到 Unpaywall 或期刊官网 |
| 未找到 | 404 或 "not found" | 切换下一个数据源 |
| 超时 | 请求超时 | 重试(最多 3 次) |
| 文件损坏 | PDF 头无效 | 删除并重新下载 |
质量检查清单
注意事项
- 版权合规:仅用于学术研究,遵守当地版权法律
- Sci-Hub 可用性:Sci-Hub 域名可能变化,需要定期更新
- 网络环境:某些地区可能需要代理才能访问 Sci-Hub
- CAPTCHA 处理:遇到 CAPTCHA 时,建议手动完成验证