pyPaperFlow 0.3.0__tar.gz → 0.6.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. pypaperflow-0.6.1/.claude/settings.local.json +8 -0
  2. pypaperflow-0.6.1/.github/workflows/docs.yml +35 -0
  3. pypaperflow-0.6.1/.gitignore +24 -0
  4. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/PKG-INFO +229 -39
  5. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/README.md +228 -38
  6. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/README_zh.md +790 -75
  7. pypaperflow-0.6.1/mkdoc_site/index.md +61 -0
  8. pypaperflow-0.6.1/mkdocs.yml +53 -0
  9. pypaperflow-0.6.1/requirements-docs.txt +1 -0
  10. pypaperflow-0.6.1/scripts/sync_docs.py +228 -0
  11. pypaperflow-0.6.1/src/pyPaperFlow/__init__.py +1 -0
  12. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/cli.py +309 -33
  13. pypaperflow-0.6.1/src/pyPaperFlow/integrations/cloak_fallback.py +72 -0
  14. pypaperflow-0.6.1/src/pyPaperFlow/integrations/cloak_pdf.py +221 -0
  15. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/integrations/pdf_fetch.py +514 -51
  16. pypaperflow-0.6.1/src/pyPaperFlow/integrations/undetected_fallback.py +77 -0
  17. pypaperflow-0.6.1/src/pyPaperFlow/integrations/undetected_pdf.py +362 -0
  18. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/preprint/arxiv_fetcher.py +153 -58
  19. pypaperflow-0.6.1/src/pyPaperFlow/preprint/biorxiv_fetcher.py +830 -0
  20. pypaperflow-0.3.0/src/pyPaperFlow/preprint/biorxiv_fetcher.py → pypaperflow-0.6.1/src/pyPaperFlow/preprint/chemrxiv_fetcher.py +180 -137
  21. pypaperflow-0.6.1/src/pyPaperFlow/preprint/europepmc_fetcher.py +204 -0
  22. pypaperflow-0.6.1/src/pyPaperFlow/preprint/source_merge.py +52 -0
  23. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/preprint/source_utils.py +23 -2
  24. pypaperflow-0.3.0/.gitignore +0 -12
  25. pypaperflow-0.3.0/src/pyPaperFlow/__init__.py +0 -1
  26. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/LICENSE +0 -0
  27. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/pyproject.toml +0 -0
  28. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/integrations/github_export.py +0 -0
  29. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/integrations/mineru_parser.py +0 -0
  30. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/preprint/source_models.py +0 -0
  31. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/pubmed/__init__.py +0 -0
  32. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/pubmed/pubmed_fetcher.py +0 -0
  33. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/pubmed/pubmed_merger.py +0 -0
  34. {pypaperflow-0.3.0 → pypaperflow-0.6.1}/src/pyPaperFlow/utils.py +0 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(curl -s -m 40 --noproxy '*' \"https://www.ebi.ac.uk/europepmc/webservices/rest/search?query=SRC:PPR%20AND%20zinc%20AND%20finger%20AND%20263%20AND%20FIRST_PDATE:%5B2026-08-01%20TO%202026-12-31%5D&format=json&pageSize=25&cursorMark=*&resultType=core\" -o /tmp/epmc_date.json -w \"HTTP=%{http_code}\\\\n\")",
5
+ "Bash(python3 -c ' *)"
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,35 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "README_zh.md"
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+ pages: write
13
+ id-token: write
14
+
15
+ concurrency:
16
+ group: pages
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ deploy:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ - uses: actions/setup-python@v7
25
+ with:
26
+ python-version: "3.13"
27
+ - run: pip install -r requirements-docs.txt
28
+ - run: python scripts/sync_docs.py
29
+ - run: mkdocs build
30
+ - uses: actions/configure-pages@v6
31
+ - uses: actions/upload-pages-artifact@v5
32
+ with:
33
+ path: site
34
+ - id: deployment
35
+ uses: actions/deploy-pages@v5
@@ -0,0 +1,24 @@
1
+ # build product
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+
6
+ # Python cache
7
+ __pycache__/
8
+ *.py[cod]
9
+
10
+ # environment
11
+ .env
12
+ venv/
13
+ CLAUDE.md
14
+ pdfs/
15
+ refers/
16
+
17
+ # mkdocs build output
18
+ site/
19
+ .cache/
20
+
21
+ # generated docs (from scripts/sync_docs.py)
22
+ # ignore everything under mkdoc_site/ except the hand-written homepage
23
+ mkdoc_site/*
24
+ !mkdoc_site/index.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyPaperFlow
3
- Version: 0.3.0
3
+ Version: 0.6.1
4
4
  Summary: Automated paper fetching and analysis platform.
5
5
  Project-URL: Homepage, https://github.com/MaybeBio/pyPaperFlow
6
6
  Project-URL: Issues, https://github.com/MaybeBio/pyPaperFlow/issues
@@ -35,7 +35,7 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  <p><strong>An automated literature processing platform for scientific researchers.</strong></p>
37
37
 
38
- <p>Batch retrieve, fetch, parse, and structure papers from PubMed, arXiv, bioRxiv, and DOI-based sources.</p>
38
+ <p>Batch retrieve, fetch, parse, and structure papers from PubMed, arXiv, bioRxiv, ChemRxiv, and DOI-based sources.</p>
39
39
 
40
40
  <p>From paper retrieval to knowledge internalization, automate the heavy lifting and keep the judgment human.</p>
41
41
 
@@ -44,17 +44,20 @@ Description-Content-Type: text/markdown
44
44
  [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
45
45
  [![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
46
46
  [![Workflow](https://img.shields.io/badge/Workflow-7%20Stages-0366d6)](docs/Design.md)
47
- [![Sources](https://img.shields.io/badge/Sources-PubMed%20%2F%20arXiv%20%2F%20bioRxiv-f59e0b)](#features)
47
+ [![Sources](https://img.shields.io/badge/Sources-PubMed%20%2F%20arXiv%20%2F%20bioRxiv%20%2F%20medRxiv%20%2F%20chemRxiv-f59e0b)](#features)
48
48
  [![PyPI version](https://img.shields.io/pypi/v/pyPaperFlow.svg?logo=pypi&logoColor=white)](https://pypi.org/project/pyPaperFlow/)
49
49
  [![Python Versions](https://img.shields.io/pypi/pyversions/pyPaperFlow.svg?logo=python&logoColor=white)](https://pypi.org/project/pyPaperFlow/)
50
50
  [![Downloads](https://static.pepy.tech/badge/pyPaperFlow)](https://pepy.tech/project/pyPaperFlow)
51
+ [![Docs](https://img.shields.io/badge/Docs-online_docs-2ea44f)](https://maybebio.github.io/pyPaperFlow/)
51
52
 
52
53
  <p>
53
- Document here 👉
54
+ 文档阅读👉
54
55
  <a href="README.md">English</a> |
55
- <a href="README_zh.md">中文</a>
56
+ <a href="README_zh.md">中文</a> |
57
+ <a href="https://maybebio.github.io/pyPaperFlow/" target="_blank">中文在线文档</a>
56
58
  </p>
57
59
 
60
+
58
61
  <p>
59
62
  <a href="./docs/Design.md">Design</a> |
60
63
  <a href="./docs/Cases.md">Cases</a>
@@ -109,6 +112,7 @@ This tool is designed to `complement rather than replace` reference management s
109
112
 
110
113
  - **Automated Retrieval from Multiple Sources**: Automatically search and retrieve paper metadata and full-text records from `PubMed/Medline, arXiv, medRxiv, chemRxiv and bioRxiv`. The repository focuses primarily on biomedical research and computational interdisciplinary fields (`Biomedicine + Computational Biology`).
111
114
  - **Full-Text Access**: Enable automatic downloading of open-access full texts in XML/Text format from `PMC`. For preprints and other publications without accessible PMC full texts, alternative acquisition modules are integrated to fetch `original PDFs`, with `Sci-Hub` set as the fallback provider.
115
+ - **Preprint Full-Text Fetch (no PDF parsing)**: For preprints without an OA PDF, dedicated methods return clean section-headed text directly — `ArxivFetcher.fetch_full_text(arxiv_id)` reads ar5iv-rendered HTML (arXiv LaTeX → HTML), and `BioRxivFetcher.fetch_full_text(doi)` / `EuropePMCFullText.full_text_xml(doi)` read JATS full-text XML from Europe PMC (bioRxiv / medRxiv and other DOI-indexed preprints).
112
116
  - **Structured Storage**:
113
117
  - **Metadata**: Preserved in well-structured detailed JSON files.
114
118
  - **Full Text**: Stored in multiple formats including parsed JSON and Markdown for versatile downstream usage — JSON for programmatic data analysis, and Markdown optimized for LLM comprehension and processing.
@@ -179,6 +183,8 @@ flowchart TD
179
183
 
180
184
  ## 📦 Installation
181
185
 
186
+ > ⚠️ For typical usage, you only need to install our tool, seen below:
187
+
182
188
  ```bash
183
189
  # 1. install our tool
184
190
  ## ✏️1️⃣ option1: Install via pip (Recommended)
@@ -188,9 +194,11 @@ pip install pyPaperFlow
188
194
  git clone https://github.com/MaybeBio/pyPaperFlow.git
189
195
  cd pyPaperFlow
190
196
  pip install -e .
197
+ ```
191
198
 
192
- --------------------------------------------------------
199
+ > Some optional dependencies, if you do not need to use the corresponding functional modules, you can ignore the installation. seen below:
193
200
 
201
+ ```bash
194
202
  # 2. install MinerU
195
203
  # follow the official installation guide: https://github.com/opendatalab/MinerU
196
204
  # verify installation: mineru --help
@@ -210,10 +218,11 @@ pip install openai anthropic
210
218
  pip install paperscraper
211
219
  ```
212
220
 
213
- > ⚠️ For typical usage, you only need to install the repository from source and MinerU, which are steps 1 and 2.
214
221
 
215
222
  ## 🛠️ Usage
216
223
 
224
+ > 📌 **Tip**: If you want to get started directly, please refer to the usage examples in [Cases.md](./docs/Cases.md). The content below is theoretical process analysis and can be skipped.
225
+
217
226
  We designed pyPaperFlow as a versatile academic research tool built strictly around the `real‑world workflow of researchers conducting literature investigation, paper reading, literature comprehension and analysis, and corpus utilization`.
218
227
 
219
228
  Therefore, please follow our step‑by‑step operations, which mirror your full literature research process. Through this hands‑on experience, you will fully grasp the design philosophy and usage of this tool.
@@ -227,33 +236,39 @@ Current available modules include (`will be continuously updated`):
227
236
 
228
237
  ```python
229
238
  ❯ paperflow --help
230
-
231
- Usage: paperflow [OPTIONS] COMMAND [ARGS]...
232
-
233
- pyPaperFlow CLI
234
-
235
- ╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
236
- │ --install-completion Install completion for the current shell. │
237
- │ --show-completion Show completion for the current shell, to copy it or customize the installation. │
238
- │ --help Show this message and exit. │
239
- ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
240
- ╭─ Commands ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
241
- │ pubmed-search Search PubMed using Your customized query and return PMIDs. │
242
- │ pubmed-meta Fetch paper metadata from PubMed using Your customized query, pmid list file and save to storage. │
243
- │ pubmed-content Download full text (PMC) for given PMIDs if the paper has a PMC ID. │
244
- │ pubmed-all Fetch BOTH metadata and full text (if available) for papers. │
245
- │ Also extracts URLs from full text and updates metadata links. │
246
- │ pubmed-merge-json Create a merged JSON (or JSONL) file from PubMed paper directories. │
247
- │ pubmed-export-md Export a single Markdown view from a merged JSON file using optional YAML config. │
248
- │ arxiv-search Search arXiv and write matching IDs to a text file. │
249
- │ arxiv-fetch Fetch arXiv metadata and attempt to download PDFs. │
250
- │ biorxiv-search Search bioRxiv and write matching IDs to a text file. │
251
- │ biorxiv-fetch Fetch bioRxiv metadata and attempt to download PDFs. │
252
- │ paper-fetch Fetch PDFs by DOI — passes through to the paper-fetch engine. │
253
- │ pdf-parse Parse a PDF file using MinerU engine, and clean up the output directory. │
254
- │ mineru-parse Parse mineru output content_list_v2.json into canonical sectioned JSON. │
255
- │ mineru-export-md Export structured mineru JSON to a clean Markdown file for LLM processing. │
256
- ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
239
+
240
+ Usage: paperflow [OPTIONS] COMMAND [ARGS]...
241
+
242
+ pyPaperFlow CLI
243
+
244
+ ╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
245
+ │ --install-completion Install completion for the current shell. │
246
+ │ --show-completion Show completion for the current shell, to copy it or customize the installation. │
247
+ │ --help Show this message and exit. │
248
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
249
+ ╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
250
+ │ pubmed-search Search PubMed using Your customized query and return PMIDs. │
251
+ │ pubmed-meta Fetch paper metadata from PubMed using Your customized query, pmid list file and save to storage. │
252
+ │ pubmed-content Download full text (PMC) for given PMIDs if the paper has a PMC ID. │
253
+ │ pubmed-all Fetch BOTH metadata and full text (if available) for papers. │
254
+ │ Also extracts URLs from full text and updates metadata links. │
255
+ │ pubmed-merge-json Create a merged JSON (or JSONL) file from PubMed paper directories. │
256
+ │ pubmed-export-md Export a single Markdown view from a merged JSON file using optional YAML config. │
257
+ │ arxiv-search Search arXiv and write matching IDs to a text file. │
258
+ │ arxiv-fetch Fetch arXiv metadata and attempt to download PDFs. │
259
+ │ biorxiv-search Search bioRxiv and write matching IDs to a text file. │
260
+ │ biorxiv-fetch Fetch bioRxiv metadata and attempt to download PDFs. │
261
+ │ medrxiv-search Search medRxiv and write matching IDs to a text file. │
262
+ │ medrxiv-fetch Fetch medRxiv metadata and attempt to download PDFs. │
263
+ │ chemrxiv-search Search ChemRxiv and write matching IDs to a text file. │
264
+ │ chemrxiv-fetch Fetch ChemRxiv metadata and attempt to download PDFs. │
265
+ │ paper-fetch Fetch PDFs by DOI — passes through to the paper-fetch engine. │
266
+ │ pdf-parse Parse a PDF file using MinerU engine, and clean up the output directory. │
267
+ │ mineru-parse Parse mineru output content_list_v2.json into canonical sectioned JSON. │
268
+ │ mineru-export-md Export structured mineru JSON to a clean Markdown file for LLM processing. │
269
+ │ github-export Export GitHub links from merged PubMed JSON, validate accessibility, │
270
+ │ and aggregate `ghresearcher parse <owner/repo> --view` outputs into one markdown. │
271
+ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
257
272
 
258
273
  ```
259
274
 
@@ -275,11 +290,20 @@ bioRxiv Modules:
275
290
  - biorxiv-search # search bioRxiv and return matching IDs
276
291
  - biorxiv-fetch # fetch bioRxiv metadata and attempt to download PDFs
277
292
 
293
+ medRxiv Modules:
294
+ - medrxiv-search # search medRxiv and return matching IDs
295
+ - medrxiv-fetch # fetch medRxiv metadata and attempt to download PDFs
296
+
297
+ ChemRxiv Modules:
298
+ - chemrxiv-search # search ChemRxiv and return matching IDs
299
+ - chemrxiv-fetch # fetch ChemRxiv metadata and attempt to download PDFs
300
+
278
301
  Third-party Modules:
279
302
  - paper-fetch # fetch PDFs by DOI
280
303
  - pdf-parse # parse PDF files into JSON, Markdown format using the MinerU engine
281
304
  - mineru-parse # Based on your custom section configuration, re-parse the MinerU output file into a structured JSON format clustered by standard literature sections
282
305
  - mineru-export-md # Based on your custom section configuration, export the structured mineru JSON to a clean Markdown file for LLM processing (🌟 e.g., batch export of introductions as your research background)
306
+ - github-export # export GitHub links from merged PubMed JSON, validate accessibility, and aggregate `ghresearcher parse <owner/repo> --view` outputs into one markdown
283
307
  ```
284
308
 
285
309
  > ⚠️ `Other preprint platforms modules are under development, please stay tuned!`
@@ -323,6 +347,20 @@ Our literature database primarily covers biomedical research and computational i
323
347
  - arXiv
324
348
  - bioRxiv,medRxiv,chemRxiv
325
349
 
350
+ > **⚠️ — Preprint search = Crossref relevance search + local boolean re-check (not a full-corpus pull, and not each server's official API).** Each request asks Crossref to search ONLY that platform's prefix (`filter=prefix:10.64898 / 10.26434,type:posted-content`) — platform scoping happens server-side, not by post-filtering a global result set. bioRxiv and medRxiv share the openRxiv prefix `10.64898`, so those two are then told apart locally by DOI-accession digit count (6 = bioRxiv, 8 = medRxiv).
351
+ >
352
+ > How the relevance step behaves: `query.bibliographic` is a fuzzy, OR-like ranking (a two-term query returns more than either single term — e.g. chemRxiv "base editing" ≈ the union of "base" and "editing"), i.e. a **superset** of the strict matches. The fetcher cursor-paginates the whole result set (not a capped top-N), then keeps only records in which **every** query term is actually present in the metadata (local boolean AND over title/abstract/…). Because strict matches ⊆ relevance superset, ranking never drops a metadata-level exact match — it only changes the order.
353
+ >
354
+ > **Limitations of the relevance search:**
355
+ >
356
+ > ① **Deposit lag** — a preprint posted minutes ago may not be indexed in Crossref yet.
357
+ >
358
+ > ② **Metadata-only matching** — Crossref scores deposited metadata (title/abstract/…), so query terms that appear only in the paper body are invisible to it. Only the bioRxiv/medRxiv Europe PMC leg indexes full text; **ChemRxiv has no full-text leg at all**, so body-only-term misses are expected there.
359
+ >
360
+ > ③ **Version duplication** — every revision is its own DOI work, so `.../v1` and `.../v2` both match a search and may need manual dedup.
361
+ >
362
+ > **Difference vs. exhaustive full-corpus enumeration:** a relevance search is a heuristic over the deposited metadata. The "no-omission-by-construction" alternative is to list the *whole* platform corpus (`filter=prefix…` with no `query`, cursor-paging every record — ≈ 55k ChemRxiv / 436k openRxiv) and run the boolean AND locally, with no relevance engine in the loop; recall is then exactly "all records whose metadata fully matches the query" (a `--start/--end-date` window shrinks the pull). The cost is downloading the full corpus per search, and it still inherits the source-level boundaries above (deposit lag, metadata-only, version duplication). This tool's `search()` path is relevance-based today; the exhaustive mode is not currently exposed as a flag.
363
+
326
364
  We recommend that you proactively learn and master the search syntax of these databases, as our built‑in search module functions similarly to the search bar on official web portals.
327
365
 
328
366
  For instance, here is a typical complex query example tailored for PubMed:
@@ -599,6 +637,8 @@ examples:
599
637
 
600
638
  We acknowledge the work of [paper-fetch](https://github.com/Agents365-ai/paper-fetch)!We have modified, refactored, and encapsulated one of its core scripts for tailored integration into our pipeline.
601
639
 
640
+ > 🔙 Rollback boundary for the paper-fetch module: commit `bc8394c` (`update paper-fetch module according to upstream repo`) contains the **original upstream script**. Commit `89eda06bac1f853254b04aee9e8916109c7771a1` is the first local **modification** to it. To restore the original module, use `89eda06` as the boundary — e.g. `git show 89eda06^:src/pyPaperFlow/integrations/pdf_fetch.py` (identical to `bc8394c`).
641
+
602
642
  The workflow of our paper acquisition module is outlined below:
603
643
 
604
644
  ```bash
@@ -624,6 +664,8 @@ The workflow of our paper acquisition module is outlined below:
624
664
  ┌─────────────────────────────────────────┐
625
665
  │ 3. arXiv (via S2 externalIds.ArXiv) │
626
666
  │ 4. Europe PMC → PMC (via PMCID) │
667
+ │ No PMCID: DOI→PMCID recovery │
668
+ │ (Europe PMC hasPDF=Y / OpenAIRE) │
627
669
  │ 5. bioRxiv / medRxiv (DOI prefix: 10.1101/)
628
670
  └─────────────────────────────────────────┘
629
671
  Total Failure ↓
@@ -634,7 +676,12 @@ The workflow of our paper acquisition module is outlined below:
634
676
  └─────────────────────────────────────────┘
635
677
  Persistent Failure ↓
636
678
  ┌─────────────────────────────────────────┐
637
- │ 7. Sci‑Hub Mirror Fallback (enabled by default, configurable)
679
+ │ 7. CORE Repository Aggregator (optional, requires CORE_API_KEY)
680
+ │ → core.ac.uk aggregates OA full-text downloadUrl
681
+ └─────────────────────────────────────────┘
682
+ Persistent Failure ↓
683
+ ┌─────────────────────────────────────────┐
684
+ │ 8. Sci‑Hub Mirror Fallback (enabled by default, configurable)
638
685
  │ → 1 request‑per‑second rate‑limiting to prevent CAPTCHA triggers
639
686
  │ → Automatic discovery of active new mirrors
640
687
  └─────────────────────────────────────────┘
@@ -647,9 +694,10 @@ Resolution Priority Sequence
647
694
  Unpaywall: The optimal open‑access source covering the broadest range of publishers with the highest hit rate.
648
695
  Semantic Scholar: Retrieves OA PDF links and cross‑platform external identifiers.
649
696
  arXiv: Activated when an arXiv identifier is available for the target paper.
650
- PubMed Central (PMC) OA Subset: Activated when a PMCID is associated with the paper.
697
+ PubMed Central (PMC) OA Subset: Activated when a PMCID is associated with the paper; when no PMCID is known, a DOI→PMCID recovery is attempted first (Europe PMC search hasPDF=Y / OpenAIRE originalId).
651
698
  bioRxiv / medRxiv: Triggered for preprints with the DOI prefix 10.1101/.
652
699
  Publisher Direct Links: Enabled only under institutional mode (PAPER_FETCH_INSTITUTIONAL=1), authorized via the caller’s institutional subscription IP, cookies, or EZproxy access.
700
+ CORE Repository Aggregator: Optional, requires CORE_API_KEY; aggregates full texts from many repositories, attempted only when all other OA sources miss (free tier ~5 req/10s).
653
701
  Sci‑Hub Mirror Fallback: Enabled by default as the final retrieval backup.
654
702
  Mirrors are attempted in the order specified by the environment variable PAPER_FETCH_SCIHUB_MIRRORS (default list: sci‑hub.ru, sci‑hub.st, sci‑hub.su, sci‑hub.box, sci‑hub.red, sci‑hub.al, sci‑hub.mk, sci‑hub.ee).
655
703
  If all predefined mirrors fail, the module fetches the latest live mirror list from https://www.sci‑hub.pub/ and retries.
@@ -665,6 +713,120 @@ export UNPAYWALL_EMAIL=you@example.com
665
713
  ```
666
714
 
667
715
 
716
+ **Cloudflare-blocked PDFs (optional)**
717
+
718
+ Some publishers (e.g. `science.org`) sit behind Cloudflare and return `403`/`429` or a "Just a moment…" JS challenge page instead of the PDF. Set `PAPER_FETCH_CLOAK=1` to retry those URLs through [CloakBrowser](https://github.com/CloakHQ/CloakBrowser) (a stealth Chromium that can pass the challenge). This fallback lives at the download layer (covers all sources), fails silently when CloakBrowser is unavailable, is operator-controlled (agents cannot enable it), and re-validates returned bytes through the same `%PDF` + 50 MB checks. Successful cloak downloads carry `via:"cloak"`.
719
+
720
+ Setup — install once, then treat `PAPER_FETCH_CLOAK` as an always-on safety net:
721
+
722
+ ```bash
723
+ # One-time install: put cloakbrowser into the same Python that runs paperflow
724
+ # (auto-detected), or into a separate venv and point CLOAKBROWSER_PYTHON at it.
725
+ pip install cloakbrowser
726
+
727
+ # Recommended: keep as a safety net (fires ONLY when a download is blocked;
728
+ # normal OA downloads are unaffected).
729
+ export PAPER_FETCH_CLOAK=1
730
+
731
+ # Cleaner per-command alternative, when you only occasionally hit a blocked URL:
732
+ PAPER_FETCH_CLOAK=1 paperflow paper-fetch 10.1126/sciadv.aee6105 --out ./pdfs
733
+
734
+ # ⚠️ PAPER_FETCH_CLOAK_HEADED=1 is NOT a default. Set it only for strong
735
+ # challenges (e.g. science.org) AND only on a machine with a display:
736
+ # export PAPER_FETCH_CLOAK_HEADED=1
737
+ ```
738
+
739
+ > **Practical notes (from testing)**
740
+ > - Cloak is **not** a paywall bypass. It only fires when an *OA* download is Cloudflare-blocked; a paywalled paper (no OA copy, e.g. `10.1016/j.cels.2025.101486`) never reaches the download layer, so Cloak is never invoked.
741
+ > - `science.org` is a "strong" challenge that **headless cannot pass** (it stalls on "Just a moment…") — it requires `PAPER_FETCH_CLOAK_HEADED=1` on a real display (a desktop, or a headless server wrapped with `xvfb-run`).
742
+ > - Cloak only has a URL to retry when a source returned a direct `url_for_pdf`. Papers whose only OA copy is in PMC (Unpaywall reports a landing page with no `url_for_pdf`) are **not** attempted by the stock script.
743
+ > - Keeping `PAPER_FETCH_CLOAK=1` always on has no effect on normal downloads, but once `cloakbrowser` is installed, each blocked URL adds a ~30–90 s browser attempt before falling through; use the inline form if you'd rather not wait.
744
+ > - Sci-Hub discovery contacts `www.sci-hub.pub` — on networks where that DNS is blocked you'll see `scihub_discover_failed`, which removes the last fallback. For genuinely unavailable papers, use institutional mode (`PAPER_FETCH_INSTITUTIONAL=1`) or download via a browser / interlibrary loan.
745
+
746
+
747
+ **Institutional access (optional)**
748
+
749
+ Some paywalled papers are exactly what your institution's subscription covers — a generic OA source just can't see them. Set `PAPER_FETCH_INSTITUTIONAL=1` to enable the publisher-direct-link source (step 6 of the chain): the script builds a direct PDF URL for the DOI's publisher and downloads it.
750
+
751
+ ```bash
752
+ export PAPER_FETCH_INSTITUTIONAL=1 # only effective while on the institutional network
753
+ ```
754
+
755
+ > **Key requirement (verified by testing):** authorization comes from the *caller's network*, not the script. Publisher-direct downloads succeed only when the machine runs **on-campus or on the institutional VPN** — the publisher recognizes your institution's IP range (or cookies / EZproxy). From an off-network IP (a datacenter or home machine) the URL is still built correctly but the publisher answers `HTTP 403 Forbidden`, and the run ends with `download_network_error` (retryable) instead of `not_found` — that error-type change is how you can tell institutional mode actually fired.
756
+ > - The result envelope's `auth_mode` field reports `"institutional"` (vs `"public"`).
757
+ > - Direct-link templates are matched by DOI prefix; Elsevier (`10.1016/`) needs an extra PII lookup via Crossref and `sciencedirect.com/.../pdfft` — verified working. Supported publishers: Nature, Science, Wiley, Springer, ACS, PNAS, NEJM, SAGE, Taylor & Francis, Elsevier, MDPI.
758
+ > - Auto rate-limits to **1 req/s** to respect publisher ToS (protects your institution's IP from throttling).
759
+ > - In public mode, when a paper looks paywalled the error payload sets `suggest_institutional: true` and hints to set this variable and re-run from on-campus / VPN.
760
+
761
+
762
+ **CORE repository-aggregator fallback (optional)**
763
+
764
+ When a paper misses across all OA sources (Unpaywall / Semantic Scholar / arXiv / PMC / bioRxiv) but an institutional or subject repository may still hold a copy, set `CORE_API_KEY` to enable the [CORE](https://core.ac.uk) (core.ac.uk) aggregator fallback. CORE aggregates full-text metadata from thousands of OA repositories and journals worldwide; its v3 search API queries by DOI and the `downloadUrl` field of a matching record is the directly downloadable OA full-text link (paywalled records leave this field empty and are skipped automatically).
765
+
766
+ ```bash
767
+ export CORE_API_KEY=your_core_api_key # free signup: https://core.ac.uk/services/api
768
+ ```
769
+
770
+ > **Principle & notes**
771
+ > - CORE is a *repository aggregator*, not a single publisher: it pools full texts from institutional and subject repositories, covering repository copies that other sources miss.
772
+ > - This source fires **only** when all earlier OA sources (Unpaywall / Semantic Scholar / arXiv / PMC / bioRxiv) have missed — it never interferes with the normal OA download path, so keeping it on is harmless.
773
+ > - Requires a free API key (Bearer auth); the source is silently skipped when `CORE_API_KEY` is unset.
774
+ > - The free tier rate-limits to roughly **5 requests / 10 seconds** and returns `403` when exceeded — imperceptible for single papers, and batch fetching is serialized/throttled.
775
+ > - Returned links pass the same `%PDF` magic-byte + 50 MB checks; successful hits carry `source:"core"`.
776
+ > - Only records with a non-empty `downloadUrl` are used, naturally filtering out paywalled entries with no OA copy.
777
+
778
+
779
+ **Recommended setup (best practice)**
780
+
781
+ For a mixed workload — OA papers, Cloudflare-gated OA papers, and the occasional paywalled paper your institution subscribes to — keep three things on:
782
+
783
+ ```bash
784
+ export UNPAYWALL_EMAIL=you@example.com # fastest / broadest OA source
785
+ export PAPER_FETCH_CLOAK=1 # safety net for Cloudflare-gated OA PDFs (harmless otherwise)
786
+ # Run the line below only while on campus / the institutional VPN:
787
+ export PAPER_FETCH_INSTITUTIONAL=1 # publisher-direct links for paywalled papers
788
+ ```
789
+
790
+ Decision logic for a single paper:
791
+
792
+ - **OA paper** → Unpaywall / Semantic Scholar / arXiv / PMC handle it; `PAPER_FETCH_CLOAK` only adds a retry when the download is Cloudflare-blocked.
793
+ - **Cloudflare-blocked OA** (e.g. `science.org`) → Cloak retries it (headless may stall; use `PAPER_FETCH_CLOAK_HEADED=1` on a machine with a display).
794
+ - **Paywalled paper** (e.g. `10.1016/j.cels.2025.101486`) → only the institutional chain can fetch it, and only from on-campus / VPN: Unpaywall → Semantic Scholar → publisher-direct (Elsevier via PII lookup → `sciencedirect.com/.../pdfft`) → Sci-Hub fallback. From an off-network IP the publisher answers `403` and no automated path remains — use your library portal / EZproxy or interlibrary loan.
795
+
796
+
797
+ **Notes & Environment Variables**
798
+
799
+ All settings are read from environment variables when the process starts — there is no config file. Every var above can be combined freely.
800
+
801
+ | Env var | Effect | Default | When to set |
802
+ | --- | --- | --- | --- |
803
+ | `UNPAYWALL_EMAIL` | Contact email for the Unpaywall API (sent in the User-Agent); without it the Unpaywall source is skipped. | empty | Recommended — Unpaywall is the fastest / broadest source |
804
+ | `CORE_API_KEY` | API key for the CORE (core.ac.uk) aggregator; without it the `core` repository source is skipped. | empty | When you need coverage for institutional / subject repository OA copies |
805
+ | `PAPER_FETCH_NO_SCIHUB` | Set to `1` to disable the Sci-Hub mirror fallback. | Sci-Hub ON | If your institution / compliance forbids Sci-Hub |
806
+ | `PAPER_FETCH_SCIHUB_MIRRORS` | Comma-separated mirror list, tried in priority order (hostnames only). | built-in default list | When the default mirrors stop working |
807
+ | `PAPER_FETCH_INSTITUTIONAL` | Set to `1` to enable publisher direct links (authorized by your institutional IP / cookies / EZproxy). Auto rate-limits to 1 req/s to respect publisher ToS. | off | If you have an institutional subscription |
808
+ | `PAPER_FETCH_CLOAK` | Set to `1` to retry Cloudflare-blocked PDFs through CloakBrowser. | off | For publishers behind Cloudflare (e.g. `science.org`) |
809
+ | `CLOAKBROWSER_PYTHON` | Python interpreter that can `import cloakbrowser`. | auto-detected | Only if not auto-detected |
810
+ | `PAPER_FETCH_CLOAK_HEADED` | Set to `1` for a headed browser (needs a display). | headless | For strong challenges that fail headless (e.g. `science.org`) |
811
+
812
+ > ⚠️ **Boolean flags are presence-based, not value-based.** The code checks `os.environ.get(...)`, so *any* non-empty value enables the feature. Setting `PAPER_FETCH_CLOAK=0` or `=false` still **enables** Cloak; setting `PAPER_FETCH_NO_SCIHUB=0` still **disables** Sci-Hub. To turn a feature off, `unset` the variable — never write `=0`/`=false`.
813
+
814
+ **Usage notes**
815
+
816
+ - The output directory flag is `--out` — there is **no `-o` short option** (the `paperflow paper-fetch` passthrough does not rewrite args).
817
+ - One DOI as a positional arg; `-` reads a single DOI from stdin; `--batch FILE` (or `--batch -`) reads DOIs line-by-line.
818
+ - Files already downloaded are skipped by default; pass `--overwrite` to force a re-download.
819
+ - stdout carries the machine-readable JSON envelope, stderr carries progress; `--format json|text` plus TTY auto-detection control the shape. Exit codes: `0` all resolved, `1` some unresolved, `3` bad arguments, `4` transport error (retryable).
820
+ - `paperflow paper-fetch schema` prints the machine-readable CLI schema (no network).
821
+ - `--idempotency-key KEY` replays the original result envelope on retry, with no network I/O.
822
+
823
+ **Known limitations**
824
+
825
+ - Some publisher redirects land on HTML pages instead of PDFs — the `%PDF` magic-byte check rejects them.
826
+ - No browser automation by default (no CAPTCHA solving) — only the optional `PAPER_FETCH_CLOAK` CloakBrowser fallback.
827
+ - SSRF protection rejects private IPs, non-`http(s)` schemes, non-80/443 ports, and cloud metadata hosts.
828
+ - Each PDF is capped at 50 MB.
829
+
668
830
  Unlike PMC parsing, non‑PubMed papers can only be obtained as PDF files via the paper‑fetch module.
669
831
 
670
832
  We recommend standardizing all paper information into Markdown or JSON formats.
@@ -1289,6 +1451,31 @@ In theory, all DOI‑driven literature workflows can be standardised following t
1289
1451
 
1290
1452
  > Modules dedicated to the aforementioned preprint platforms are still under development and refinement. Preprint‑related subcommands are provided for testing purposes only. For detailed test cases, refer to [Cases](./docs/Cases.md)
1291
1453
 
1454
+ #### Preprint full-text fetch (Python API)
1455
+
1456
+ For preprints without an open-access PDF, each fetcher exposes a `fetch_full_text()` method that returns clean section-headed text without any PDF parsing:
1457
+
1458
+ ```python
1459
+ from pyPaperFlow.preprint.arxiv_fetcher import ArxivFetcher
1460
+ from pyPaperFlow.preprint.biorxiv_fetcher import BioRxivFetcher
1461
+ from pyPaperFlow.preprint.europepmc_fetcher import EuropePMCFullText
1462
+
1463
+ # arXiv → ar5iv rendered HTML (LaTeX → HTML)
1464
+ arxiv = ArxivFetcher(root_dir="./papers")
1465
+ text = arxiv.fetch_full_text("1706.03762") # "" on failure
1466
+
1467
+ # bioRxiv / medRxiv → Europe PMC fullTextXML
1468
+ biorxiv = BioRxivFetcher(root_dir="./papers", platform="biorxiv")
1469
+ text = biorxiv.fetch_full_text("10.1101/2023.06.22.546069")
1470
+
1471
+ # Any DOI-indexed preprint → Europe PMC fullTextXML directly
1472
+ epmc = EuropePMCFullText()
1473
+ xml = epmc.full_text_xml("10.1101/2023.06.22.546069")
1474
+ epmc.close()
1475
+ ```
1476
+
1477
+ All three return an empty string `""` on failure, so callers can gracefully fall back to the abstract. The returned text is section-headed (`## Section`) plain text ready for LLM input.
1478
+
1292
1479
  ### 6. Critical Reading and Knowledge Graph Analysis: Downstream End‑Use
1293
1480
 
1294
1481
  Upon completing literature retrieval, parsing, and structured processing as outlined above, users obtain chapter‑organised Markdown files and structured JSON files, which serve as the fundamental inputs for subsequent critical reading and knowledge graph analysis.
@@ -1328,8 +1515,8 @@ You may directly run the test scripts to verify the correctness and completeness
1328
1515
  <details>
1329
1516
  <summary><b>3. Literature Acquisition (and Full‑Text Download)</b></summary>
1330
1517
 
1331
- > - [ ] Refine and encapsulate the `paper‑fetch` module. Refer to [2026‑05‑08 paper‑fetch Encapsulation](https://github.com/Agents365‑ai/paper‑fetch); evaluate integration or replacement with more robust modules offering higher hit rates.
1332
- > - [ ] The `pdf‑parse` module currently wraps basic MinerU parsing commands with the CPU backend (`‑b pipeline`). Future integration of GPU‑accelerated features; see [MinerU Repository](https://github.com/opendatalab/MinerU) for details.
1518
+ > - [x] Refine and encapsulate the `paper‑fetch` module. Refer to [2026‑05‑08 paper‑fetch Encapsulation](https://github.com/Agents365‑ai/paper‑fetch); evaluate integration or replacement with more robust modules offering higher hit rates.
1519
+ > - [x] The `pdf‑parse` module currently wraps basic MinerU parsing commands with the CPU backend (`‑b pipeline`). Future integration of GPU‑accelerated features; see [MinerU Repository](https://github.com/opendatalab/MinerU) for details.
1333
1520
 
1334
1521
  </details>
1335
1522
 
@@ -1348,7 +1535,10 @@ You may directly run the test scripts to verify the correctness and completeness
1348
1535
  <details>
1349
1536
  <summary><b>5. Processing for Other Literature Databases</b></summary>
1350
1537
 
1351
- > - [ ] Develop a unified `search‑fetch‑parse` pipeline for non‑PubMed databases and complete corresponding modules. Refer to open‑source implementations such as [paperscraper](https://github.com/jannisborn/paperscraper) and [paper‑tracker](https://github.com/RainerSeventeen/paper‑tracker).
1538
+ > - [x] Develop a unified `search‑fetch‑parse` pipeline for non‑PubMed databases and complete corresponding modules. Refer to open‑source implementations such as [paperscraper](https://github.com/jannisborn/paperscraper) and [paper‑tracker](https://github.com/RainerSeventeen/paper‑tracker).
1539
+ > - [ ] Multi‑source retrieval merge (primitive ready, orchestration pending): `preprint/source_merge.py` already provides `merge_papers` (backfill missing DOIs → dedupe by DOI, key cascade DOI → title+authors → source_id). The cross‑source orchestration command is not yet implemented; when adding a unified `search --sources arxiv,biorxiv,medrxiv,chemrxiv` that merges results into a single corpus, call `merge_papers` directly.
1540
+ > - [ ] Optional connectors (add on demand): skip a standalone Semantic Scholar metadata connector — it overlaps with the S2 usage inside `pdf_fetch.py` (`openAccessPdf`/`externalIds`), avoiding dual maintenance. Introduce an OpenAlex connector (`api.openalex.org/works`, inverted‑index abstract reconstruction + authors + citations/references) only when there is a concrete need for batch DOI‑based metadata completion or citation‑graph harvesting; emit `SourcePaper`.
1541
+ > - [ ] Infrastructure alignment (partially done): `extract_doi` unified into `source_merge.py`; `safe_filename` already present (`source_utils.py`); `get_env` deferred (mismatches the existing flat `os.environ.get` style); OAI‑PMH base class deferred until a generic OAI repository is actually integrated.
1352
1542
 
1353
1543
  </details>
1354
1544