arXiv论文自动抓取并整理进Zotero的文献巡检流水线
发布时间:2026/9/1 10:08:58 作者:尧图编辑部 阅读量:1,286

这次我们不看模型评测看一个科研效率问题arXiv 每天更新几百篇论文怎么才能不打开网页、不手动下载就把自己关心方向的论文自动整理进 Zotero如果你的工作流还停留在“每周手动刷一遍 arXiv → 看到感兴趣论文 → 下载 PDF → 在 Zotero 里逐条建条目、补作者、补链接”那这篇文章正好可以收藏。这里直接给出一套可落地的“arXiv × Zotero 文献自动巡检流水线”用 arXiv 官方 API 定时拉取论文自动去重、筛选再通过 Zotero 批量导入条目和 PDF 附件。整个过程的重点是能不能自动化、适不适合批量、以及如何稳定运行。这套流水线的门槛比很多人想象的低。它不依赖 GPU不占显存普通办公电脑就能跑真正需要你花时间的只有两步把 arXiv 的检索式写好以及把 Zotero 的导入流程跑通。下面我会按“核心能力 → 环境准备 → 流水线设计 → API 拉取 → 批量导入 → 去重校验 → 定时调度 → 问题排查”的顺序展开并把每一步的关键代码、判断标准和常见坑都写清楚。1. 核心能力速览能力项说明项目类型arXiv 论文追踪 Zotero 文献归档自动化流水线核心场景定时监控指定方向的 arXiv 论文自动整理条目与 PDF论文获取通过 arXiv 官方 API 拉取不依赖爬虫批量导入支持批量生成 BibTeX / RIS 文件再导入 ZoteroPDF 附件自动下载或链接 arXiv PDF 并挂到 Zotero 条目去重机制以 arXiv 论文 ID / DOI / URL 作为唯一标识脚本级去重检索定制支持按分类如 cs.CL、cs.CV、关键词、作者、时间窗口过滤扩展方向可接入摘要翻译、关键词标注、标签管理、Obsidian 联动硬件门槛普通办公电脑即可纯 CPU 任务启动方式Python 脚本 / 定时任务 / Zotero 本地接口接口能力arXiv API Zotero 本地 HTTP API可脚本化批量能力支持批量拉取、批量导入、日志记录与失败重试适合人群研究生、科研人员、文献管理重度用户需要注意的一点是arXiv 的元数据接口是公开提供的个人学习和科研场景下合理使用没有问题但 PDF 附件和论文内容应当只用于个人研究与学习不应在未授权的情况下二次分发或商用转载。2. 适用场景与使用边界这套流水线最适合下面几类人。第一类有固定追踪方向的研究生和科研人员。比如你一直在关注大模型推理、多模态检索、三维重建、强化学习某个子方向每天需要扫一遍新论文。人工刷网页很容易漏而且很费时间。用流水线把 arXiv 检索式固定下来每天定时拉取就能保证不漏。第二类需要维护大量文献库的人。比如课题组要整理一个“领域内近三年论文清单”或者每个月的文献汇报需要汇总新增论文。手工整理几十上百条条目流程繁琐且容易出错脚本批量生成 BibTeX 再导入 Zotero可以大大缩短整理时间。第三类想把 Zotero 真正用起来但不想每个插件都手工折腾的人。Zotero 本身只是文献库它的价值要靠导入机制和工作流来体现。这篇流水线把“Zotero 批量导入”“PDF 附件挂载”“去重”三个高频动作串成一条链路。同时也说清楚使用边界。不适合做“全 arXiv 无差别归档”。每天全量抓取会产生大量无关论文反而增加整理成本。建议用相对精确的检索式。不适合代替人工阅读。流水线只负责“把论文送到你面前”不代表你不需要读摘要和正文。不适合上传到公共资源库。涉及版权、署名、未公开数据的内容必须确认授权后再处理。如果论文涉及人脸图像、医疗数据、隐私信息归档后仍要注意传播范围。3. 环境准备与前置条件整套流水线对硬件没有特殊要求CPU 和内存足够即可。下面给出通用检查清单。3.1 软件依赖操作系统Windows / macOS / Linux 均可。Zotero 桌面客户端建议使用官网或系统包管理器安装的最新稳定版。Python建议 3.9 及以上版本。Python 依赖库requests调用 arXiv API 和 Zotero 本地 API。feedparser解析 arXiv API 返回的 Atom XML 数据。arxiv社区封装的 arXiv API 客户端比手动拼 XML 更省事。pyzotero如果后续要用 Zotero Web API 做云端同步可以安装。没有具体版本限制时安装命令统一为pip install requests feedparser arxiv pyzotero3.2 Zotero 本地 API 设置Zotero 桌面端自带一个本地 HTTP 服务器供其他程序通过 HTTP 访问当前文献库。默认端口是 23119具体以你的软件设置为准。开启方式通常是打开 Zotero → 编辑 → 设置 → 高级 → 设置 → 服务器勾选“允许其他应用程序通过 HTTP 通信”。如果没有勾选脚本调用本地 API 时会直接连接失败。这是新手最容易忽略的一步。3.3 磁盘与目录规划论文 PDF 大多在几 MB 到几十 MB 之间。如果每天拉取 20 到 50 篇论文一个月下来也就是几个 GB 的量级。建议单独建目录arxiv_zotero_pipeline/ ├── scripts/ # Python 脚本 ├── bibtex/ # 生成的 BibTeX 文件 ├── pdf/ # 下载的 PDF 附件 ├── logs/ # 运行日志 └── output/ # 其他中间产物这样脚本、临时文件、文献附件分开后续排查问题也方便。3.4 端口与网络Zotero 本地 API 默认监听127.0.0.1:23119启动脚本前先确认这个端口没有被其他程序占用。拉取 arXiv 数据需要能访问 arXiv 的 API 域名。如果网络环境无法稳定访问脚本会超时或返回空列表这种情况需要优先解决网络连通性而不是改脚本。4. 流水线总体设计与工作流程这条流水线的整体思路可以用一句话概括定时用 arXiv API 拉候选论文列表做去重和过滤再生成 Zotero 能识别的内容最后让 Zotero 完成条目与附件归档。具体流程分成四个阶段。4.1 采集阶段通过 arXiv API 执行检索式按SubmittedDate倒序返回最新论文。返回结果包括论文 ID 与官方 URL标题、作者列表摘要分类出版时间PDF 链接4.2 去重与过滤阶段因为同一个论文 ID 可能在多个分类下出现或者一次运行与上一次运行存在重复所以必须先做两层去重内部去重同一批次内按论文 ID 去重。历史去重对比 bibtex/ 目录或 SQLite 中已经处理过的论文 ID跳过旧记录。过滤规则可以根据需要调整比如只看标题或摘要中包含指定关键词的论文。4.3 生成阶段把去重后的论文生成 BibTeX 文件。每一个条目的url字段指向 arXiv 页面eprint字段可以写 arXiv ID这样 Zotero 导入后能识别出这是 arXiv 论文并自动补全摘要等信息。4.4 导入与归档阶段在 Zotero 中导入生成的 BibTeX 文件。导入方式有两种手动导入Zotero → 文件 → 导入适合低频使用。脚本半自动导入用 Zotero 本地 API 查询现有条目辅助判断某篇论文是否已经存在再决定是否需要导入。PDF 附件可以单独下载到 pdf/ 目录再拖入 Zotero 条目也可以后续用 Zotero 的“查找可用 PDF”功能补充。5. 第一步用 arXiv API 自动拉取论文5.1 arXiv API 简介arXiv 官方提供 HTTP API访问地址是http://export.arxiv.org/api/query。不需要注册账号直接 GET 请求即可。核心参数包括search_query检索式例如cat:cs.CL AND all:large language model。start返回结果起始位置。max_results单次返回数量。sortBy排序字段常用submittedDate。sortOrderdescending或ascending。这里推荐用社区封装更友好的arxiv库。它的底层仍然是 arXiv API但代码写起来更直观。5.2 用 arxiv 库拉取论文先安装依赖pip install arxiv feedparser requests然后写一个最简单的采集脚本import arxiv client arxiv.Client() search arxiv.Search( querycat:cs.CL AND (ti:large language model OR abs:large language model), max_results50, sort_byarxiv.SortCriterion.SubmittedDate, sort_orderarxiv.SortOrder.Descending, ) for result in client.results(search): print(result.entry_id) print(result.title) print(result.published) print(result.pdf_url) print(---)这段代码会拉取最近提交的、标题或摘要中包含“large language model”的论文。5.3 解析元数据并保存中间结果实际使用时建议把解析后的字段写成 JSON 或直接写入 SQLite避免每次都要重新请求 arXiv。import json import arxiv client arxiv.Client() def fetch_papers(query, max_results50): search arxiv.Search( queryquery, max_resultsmax_results, sort_byarxiv.SortCriterion.SubmittedDate, sort_orderarxiv.SortOrder.Descending, ) papers [] for r in client.results(search): papers.append({ arxiv_id: r.entry_id, title: r.title, authors: [a.name for a in r.authors], published: str(r.published), summary: r.summary, pdf_url: r.pdf_url, primary_category: r.primary_category, categories: r.categories, }) return papers if __name__ __main__: query cat:cs.CL AND (ti:retrieval augmented generation OR abs:retrieval augmented generation) data fetch_papers(query, max_results20) with open(output/latest_papers.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) print(f共拉取 {len(data)} 篇论文)这样就把结果缓存到了本地后续生成 BibTeX 和去重都不需要再请求网络。6. 第二步生成 BibTeX 并批量导入 Zotero6.1 为什么选择 BibTeXZotero 原生支持导入 BibTeX。与直接写 Zotero 本地 API 创建条目相比BibTeX 文件导入更稳定也更容易在多个工具之间复用。比如你以后想联动 Obsidian、写 LaTeX 论文BibTeX 仍然能用。6.2 从 JSON 生成 BibTeX假设上一步已经拿到了output/latest_papers.json现在把它转成 BibTeX。import json import re import html def clean_text(text): text html.unescape(text) text re.sub(r\s, , text).strip() return text def to_bibtex_key(arxiv_id): # 示例规则根据 arxiv 链接中的最后一段生成 key可根据需要调整 match re.search(r/([^/])$, arxiv_id) key match.group(1) if match else arxiv_id return key.replace(., _) def papers_to_bibtex(data, output_path): entries [] for paper in data: bibkey to_bibtex_key(paper[arxiv_id]) authors and .join(paper[authors]) title clean_text(paper[title]) abstract clean_text(paper[summary]) entry farticle{{{bibkey}, title {{{title}}}, author {{{authors}}}, journal {{arXiv preprint}}, year {{{paper[published][:4]}}}, eprint {{{bibkey}}}, url {{{paper[arxiv_id]}}}, pdf {{{paper[pdf_url]}}}, abstract {{{abstract}}}, }} entries.append(entry) with open(output_path, w, encodingutf-8) as f: f.write(\n\n.join(entries)) print(f已生成 {len(entries)} 条 BibTeX 记录) if __name__ __main__: with open(output/latest_papers.json, r, encodingutf-8) as f: papers json.load(f) papers_to_bibtex(papers, bibtex/latest.bib)这里需要注意几个细节摘要中如果包含{、}、%、等 LaTeX 特殊字符导入后可能显示异常建议做一次转义。作者列表可能有几十个BibTeX 中全部列出会让条目变得冗长Zotero 能正常解析但如果只想要第一作者可以只保留前三位。论文标题中的换行和多余空格要清理。6.3 批量导入 Zotero生成latest.bib后打开 Zotero执行文件 → 导入 → 选择一个 BIB 文件 → 导入。Zotero 会为每一条记录创建条目并尝试根据url或eprint字段匹配到 arXiv 论文。导入完成后可以检查一下“我的文库”中是否出现了对应条目。如果论文数量多导入过程可能需要一点时间但整体不会卡。导入完成后建议立即检查摘要字段是否完整、作者是否有乱码。如果乱码通常是 BibTeX 文件编码问题需要统一改为 UTF-8。7. 第三步自动下载 PDF 附件与去重校验7.1 批量下载 PDFBibTeX 导入只能建立条目不会自动把 PDF 下载到本地。如果需要离线阅读还需要一个下载 PDF 的步骤。import json import os import time import requests DOWNLOAD_DIR pdf def download_pdf(url, save_path): if os.path.exists(save_path): print(文件已存在跳过:, save_path) return True headers { User-Agent: Mozilla/5.0 (compatible; AcademicPipeline/1.0) } resp requests.get(url, headersheaders, timeout30) if resp.status_code 200 and resp.content: with open(save_path, wb) as f: f.write(resp.content) return True return False if __name__ __main__: os.makedirs(DOWNLOAD_DIR, exist_okTrue) with open(output/latest_papers.json, r, encodingutf-8) as f: papers json.load(f) success 0 for paper in papers: url paper[pdf_url] arxiv_id paper[arxiv_id].split(/abs/)[-1] save_path os.path.join(DOWNLOAD_DIR, f{arxiv_id}.pdf) if download_pdf(url, save_path): success 1 time.sleep(1) # 避免请求过快 print(f下载完成成功 {success} / 总数 {len(papers)})下载时注意控制请求频率避免对 arXiv 服务器造成压力。即使官方 API 允许批量访问也建议每下载一个 PDF 之间至少间隔 1 秒。7.2 把 PDF 挂到 Zotero 条目推荐两种方式手动方式在 Zotero 中选中对应条目把 PDF 文件直接拖到附件区域。自动方式Zotero 的文件导入功能只会建条目不负责附件。要让附件与条目关联可以使用 Zotero 的“查找可用 PDF”功能或者在 Zotero 中把 PDF 拖到条目上。更彻底的做法是使用 Zotero 本地 API 查询条目是否存在再判断是否需要导入。但这一步对不同版本差异较大建议先跑通手动流程再考虑自动化。7.3 去重校验去重是流水线中最关键的一环。没有去重的话每天跑一遍就会产生大量重复条目。去重策略按优先级排列第一层在 JSON 生成阶段使用arxiv_id去重。第二层读取历史记录文件过滤掉已经处理过的 ID。第三层导入 Zotero 前通过 Zotero 本地 API 查询 DOI 或 URL避免重复导入。最轻量的历史去重实现import json import os HISTORY_FILE output/processed_ids.json def load_history(): if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, r, encodingutf-8) as f: return set(json.load(f)) return set() def save_history(processed_ids): with open(HISTORY_FILE, w, encodingutf-8) as f: json.dump(sorted(processed_ids), f, ensure_asciiFalse, indent2) if __name__ __main__: history load_history() with open(output/latest_papers.json, r, encodingutf-8) as f: papers json.load(f) new_papers [] new_ids set() for paper in papers: arxiv_id paper[arxiv_id] if arxiv_id in history: continue new_papers.append(paper) new_ids.add(arxiv_id) print(f本次新增 {len(new_papers)} 篇历史已处理 {len(history)} 篇) save_history(history | new_ids) with open(output/new_papers.json, w, encodingutf-8) as f: json.dump(new_papers, f, ensure_asciiFalse, indent2)这套逻辑可以保证每次只处理新增论文不会重复生成 BibTeX。8. 接口能力、定时调度与批量任务8.1 用 Zotero 本地 API 查询条目如果你想把流程做到“脚本直接判断某篇论文是否已在 Zotero 中”可以用 Zotero 本地 HTTP API。Zotero 桌面端开启本地服务器后可以通过http://127.0.0.1:23119/api/users/0/items查询当前文库中的条目。下面是一个简单示例import requests ZOTERO_API http://127.0.0.1:23119/api/users/0/items def fetch_zotero_items(limit20): headers {Zotero-API-Version: 3} params {limit: limit, format: json} resp requests.get(ZOTERO_API, headersheaders, paramsparams, timeout10) resp.raise_for_status() return resp.json() if __name__ __main__: items fetch_zotero_items(limit20) for item in items: data item.get(data, {}) print(data.get(key), data.get(title))注意不同操作系统、不同 Zotero 版本对本地 API 的访问策略可能不同如果请求失败优先检查是否开启“允许其他应用程序通过 HTTP 通信”选项。8.2 定时调度流水线要真正做到“自动巡检”需要配合系统的计划任务。Windowsschtasks /create /tn ArxivZoteroPipeline /tr python D:\arxiv_zotero_pipeline\scripts\run_pipeline.py /sc daily /st 09:00Linux / macOScrontab -e0 9 * * * cd /home/user/arxiv_zotero_pipeline python3 scripts/run_pipeline.py logs/cron.log 21把采集、生成、下载、去重几个步骤整合到一个run_pipeline.py中import os import json import subprocess def run_script(script_name): print(f 运行 {script_name} ) result subprocess.run([python, script_name], capture_outputTrue, textTrue) print(result.stdout) if result.returncode ! 0: print(result.stderr) raise RuntimeError(f脚本 {script_name} 执行失败) if __name__ __main__: run_script(scripts/step1_fetch_arxiv.py) run_script(scripts/step2_generate_bibtex.py) run_script(scripts/step3_download_pdf.py) print(流水线执行完成)实际使用时建议每个脚本之间加日志和失败重试避免前一步失败还继续执行后一步。8.3 批量任务设计批量任务可以按“一批论文 一个批次任务”来设计。每次运行流程时生成一个批次 ID例如2025-06-01。所有中间文件都放到output/2025-06-01/下。日志统一写入logs/2025-06-01.log。处理完的论文 ID 追加到历史记录中。这样即使某天执行失败也能根据批次日志快速定位是哪一步出了问题。output/ ├── 2025-06-01/ │ ├── papers.json │ ├── papers.bib │ └── pdf/ └── processed_ids.json9. 资源占用与性能观察这套流水线不是 AI 推理任务不涉及显存和 GPU。我们关心的是 CPU、内存、磁盘和网络带宽。9.1 采集阶段一次拉取 50 篇论文元数据网络请求往返时间通常在一秒到几秒之间。解析 Atom XML 时内存占用会比 JSON 高一些但对现代电脑来说几乎可以忽略。如果一次拉取 200 篇以上建议加一个小退避避免请求超时。9.2 BibTeX 生成阶段纯本地字符串处理基本不耗时。需要注意的只是文件编码统一使用 UTF-8。9.3 PDF 下载阶段这是整个流水线中最耗资源的一步。假设一篇论文 PDF 是 5 MB下载 50 篇就是 250 MB 的网络流量。下载速度取决于网络环境时间长短会明显拉开。磁盘占用则要看你的论文总量按 5 MB 到 20 MB 一篇来估算即可。9.4 Zotero 导入阶段Zotero 在导入大量条目时会有索引和元数据抓取动作内存占用会短暂上升。如果一次导入几百条建议分批导入每批 50 到 100 条导入完成后等待索引稳定再做下一批。9.5 如何观察异常如果脚本运行时间比平时长很多优先看网络请求是不是在等待超时。如果 Zotero 导入后条目全是灰色说明元数据解析失败要看 BibTeX 文件是否损坏。如果 PDF 目录越来越大但 Zotero 里没有对应附件说明“下载 PDF”和“挂附件”两个步骤之间断了需要检查文件名是否与条目匹配。10. 常见问题与排查方法问题现象可能原因排查方式解决方案arXiv API 请求超时或无结果网络无法访问 arXiv或检索式语法错误手动用浏览器打开 API 地址验证检查检索语法的引号和括号修复网络连通性后重试简化检索式去掉中文标点Zotero 本地 API 连接失败未开启 HTTP 服务器或端口被占用检查 Zotero 设置中的 HTTP 服务器选项用 curl 访问127.0.0.1:23119开启端口或更换端口后重启 ZoteroBibTeX 导入后标题乱码BibTeX 文件编码不是 UTF-8用文本编辑器查看文件原始编码统一保存为 UTF-8不要用 UTF-8 BOM摘要中的特殊字符显示异常未转义%、、_等字符检查 BibTeX 中摘要字段内容在生成脚本中对特殊字符做 LaTeX 转义导入 Zotero 后条目重复没有做历史去重或去重 ID 规则不一致检查 processed_ids.json 中记录是否完整统一用 arXiv 官方链接作为唯一 IDPDF 下载失败网络波动或请求频率过高查看日志中失败的具体 URL 和状态码增加重试机制每次请求间隔 1 秒以上某篇论文没有摘要原论文本身未提供摘要或解析逻辑漏字段打印原始 JSON确认字段名调整字段解析逻辑WebDAV 同步失败同步服务器配置不正确或网络不稳定检查 Zotero 首选项中的同步设置按官方文档重新配置同步目标如果只是本地使用可先关闭同步排查流程建议遵循“先网络后脚本再 Zotero”的顺序。大多数问题不是脚本逻辑错而是网络不通、端口没开、编码不对这三类。11. 最佳实践与使用建议11.1 第一次先小批量测试不要一上来就拉 200 篇论文。先拉 5 篇走一遍“JSON → BibTeX → Zotero 导入 → PDF 挂载”确认整条链路通了再放大批量。这样能避免一次踩多个坑。11.2 保留一套最小可运行配置把检索式、去重规则、PDF 下载目录、定时调度命令都写进一个config.json脚本统一读取配置不要硬编码在代码里。{ arxiv_query: cat:cs.CL AND (ti:\retrieval augmented generation\ OR abs:\retrieval augmented generation\), max_results: 50, download_pdf: true, pdf_dir: pdf, bibtex_dir: bibtex, history_file: output/processed_ids.json, log_level: INFO }11.3 模型、输入素材、输出结果分目录管理你可能会扩展出翻译脚本、摘要脚本、关键词标注脚本。建议每个环节都使用独立目录中间产物带日期前缀避免把上一轮输出覆盖掉。11.4 批量任务必须加日志和失败重试每天自动运行时日志是最重要的排错依据。建议保留最近 30 天日志下载 PDF 失败的任务自动重试两次仍失败就记录到failed.log下次运行时优先处理失败列表。11.5 接口服务要限制访问范围如果将来把 Zotero 本地 API 暴露给局域网内其他设备使用一定要限制访问范围不要监听公网地址。默认情况下保持127.0.0.1监听即可。11.6 版权与合规提醒arXiv 论文和 PDF 仅供个人学术研究使用不应在未授权的情况下二次分发。如果涉及人脸图像、隐私数据、未公开项目的内部文献不要在流水线中自动归档到共享空间。团队共用文献库时要明确标注哪些论文已获得授权、哪些只做内部参考。11.7 与翻译和笔记工具联动热词中有大量 Zotero 翻译插件和 Obsidian 联动的需求。这套流水线生成 BibTeX 后可以进一步在 Zotero 中批量抓取摘要再用翻译插件自动翻译摘要也可以把 BibTeX 导入 Obsidian 的数据库插件中形成“论文追踪 → 文献管理 → 笔记”的完整链路。12. 总结与下一步这套“arXiv × Zotero 文献自动巡检流水线”最值得尝试的地方是把每天重复的论文筛选和文献归档工作压缩到了一个 Python 脚本里。你只需要维护好检索式和历史去重记录剩下的拉取、生成 BibTeX、下载 PDF 都能自动化完成。建议第一次测试时先把arxiv-id → JSON → BibTeX → Zotero 导入这条最短链路跑通不要一次性把所有功能都加上。最容易踩的坑集中在三个地方arXiv 网络不可达、Zotero 本地 HTTP 服务未开启、BibTeX 文件编码错误。这三个问题解决后整套流程基本就稳定了。下一步可以考虑的方向在 Zotero 中给论文自动打标签按子方向分类。接入摘要翻译插件让每条论文带上中文摘要。把 BibTeX 同步到 Obsidian建立自己的文献笔记库。用 GitHub Actions 或服务器定时任务实现每天自动执行。如果文献整理已经占用了你不少时间这套流水线值得花一个下午搭起来。建议收藏备用后面需要的时候直接照步骤跑一遍。