Skip to content
maki49Public

About

Turn a folder of PDFs into a Zotero library — Crossref-matched, folder hierarchy preserved, auto-accept based on confidence level.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Strict PDF Literature Scanner

只读扫描一个 PDF 文献库,逐篇到 Crossref 做匹配,产出 CSV,再转成 Zotero RDF。 脚本不修改 PDF、文件名、目录或 Zotero 数据库。

安装

pip install pypdf pymupdf requests rapidfuzz tqdm

pymupdf 用于按字号定位标题(见下文「标题提取」)。缺失时会自动退回 pypdf 的行启发式,准确率明显下降。

用法

python scan_papers_strict.py "/path/to/Papers" --output scan_result \
       --mailto you@example.org

强烈建议提供 --mailto(或设 $CROSSREF_MAILTO)。它把请求送进 Crossref 的 polite pool,配额远高于匿名池。不提供也能跑通,只是更慢、更多重试。

常用参数:

参数 默认 说明
--mailto 无 Crossref polite pool 联系邮箱
--workers 4 并发线程数
--rate 5.0 初始请求/秒,会按 Crossref 返回的 X-Rate-Limit-* 头自适应
--cache <output>/crossref_cache.sqlite 响应缓存;重跑时几乎不再打 API
--no-cache 禁用缓存
--doi-tries 4 每篇最多尝试几个 DOI 候选
--head-pages / --tail-pages 3 / 1 读取的首尾页数
--top-n 10 保留的候选数
--limit 0 只扫前 N 篇,用于试跑
--no-title-search 只走 DOI,不做书目搜索

--sleep 已废弃:限流现在按请求而非按文件进行,用 --rate 控制。

模块

文件 职责
scan_papers_strict.py 编排:遍历、并发、写 CSV
crossref_client.py polite pool、令牌桶限流、重试退避、SQLite 缓存
pdf_extract.py 按版面字号提取标题/作者,NFKC 归一化
filename_signal.py 从文件名解析年份/期刊/作者(独立证据)
matching.py DOI 提取、相似度、打分、置信度、回验
csv_to_zotero_rdf.py papers.csv → Zotero RDF(保留 collection 树结构)

匹配信号

打分维度及权重:

维度 权重 来源
title 0.44 PDF 版面/元数据
filename 0.30 文件名
author 0.14 PDF 正文
year 0.06 PDF 正文
journal 0.06 PDF 正文

文件名维度是独立于文本提取的证据,这一点很关键:其余四个维度都来自同一份可能 已经损坏的 PDF 文本,它们会一起失效。本库的命名规范 YY期刊-作者-主题.pdf 实测 年份准确率 98%、期刊 97%、姓氏 89%,比正文提取更可靠。

缺失的维度不参与加权平均(而非记 0 分)。

标题提取

按优先级:

  1. 版面 — 第一页字号最大的非样板文字。逐级下探字号,因为 Elsevier 等出版商 把刊名 masthead 排得比标题还大。
  2. PDF /Title — 必须通过垃圾模式检查(untitled、Changepdftitle、 Microsoft Word - ...、.dvi、期刊页眉…),且与第一页正文能对上才采信。
  3. 行启发式 — 仅在 PyMuPDF 不可用时使用。

title_source 列记录实际来源。

DOI 提取

正文中的 DOI 候选按来源排序后逐个尝试解析,而非取第一个就信:

  • 第 1 页 > 第 2-3 页 > 尾页
  • 一页上出现 ≥3 个 DOI ⇒ 判为参考文献页,其上所有 DOI 作废
  • References 标题之后的 DOI 作废
  • 排除数据仓库前缀(Mendeley 10.17632、Zenodo 10.5281、figshare 10.6084 …)
  • 剥离补充材料后缀(/-/DCSupplemental*、.s001)与尾部 #
  • 同时扫描「去掉换行」的文本变体,以修复被断行截断的 DOI

Crossref 侧过滤掉 component / dataset / peer-review 类型的记录。

回验

拿到 Crossref 记录后,用文件名年份、标题、作者姓氏做交叉检查,结果写入 verified / verify_note 两列:

  • confirmed — 有证据支持
  • unverified — 没有可用于核对的独立证据(不代表有问题)
  • conflict — 证据反对这条匹配 ⇒ 强制 status=REVIEW

DOI 命中不再等于自动通过。

置信度

等级 含义
HIGH 证据充分,status=OK,可自动进入下一阶段
MEDIUM 较可信,人工检查
LOW 可疑,人工检查
NONE 证据不足

硬性下限:title_score < 0.55 且无强文件名证据时,无论其余字段如何相加都不能到 HIGH。文件名证据强(≥0.85)可升一级,明显矛盾(<0.40)降一级。

doi_crossref 有值不等于匹配成功;只有 match_confidence=HIGH 且 status=OK 才建议自动接受。

输出

  • papers.csv — 每篇 PDF 一行
  • review.csv — 需要人工检查的条目
  • candidates.csv — 每篇搜索的前 N 个候选,便于判断 top 1 是否正确
  • summary.txt — 统计,含 HTTP 请求数/缓存命中/重试/429 次数
  • crossref_cache.sqlite — 响应缓存

转 Zotero RDF

python csv_to_zotero_rdf.py scan_result/papers.csv zotero_import.rdf

collection 列中的 / 会还原为 Zotero 的子集合层级: beyond-dft/QEDFT/application 导入后是三层嵌套。; 和 | 分隔并列集合。 条目只挂在叶子集合上。

输出会剥离 XML 1.0 非法字符——PDF 元数据里的控制字节曾导致生成的 RDF 无法被 Zotero 解析(报 "The selected file is not in a supported format.")。

About

Turn a folder of PDFs into a Zotero library — Crossref-matched, folder hierarchy preserved, auto-accept based on confidence level.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages