paper-fetch について
paper-fetchは、DOIまたは論文タイトルからPDFを探してダウンロードするコマンドラインツールです。文献取得の自動化向けに、優先順で複数の取得元を試し、バッチ入力、JSON出力、エージェント向けのCLIスキーマに対応します。
主な機能
- 01DOIまたはタイトルからPDF取得
- 02複数の取得元を順に試行
- 03DOIの一括ダウンロード
- 04JSON結果と進捗イベントの出力
- 05機械可読なCLIスキーマ
活用シーン
- 01DOIを指定して論文をダウンロード
- 02タイトルから論文を解決して取得
- 03DOIリストを一括処理
- 04自動化処理で取得結果を読み込む
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents365-ai/ 365-skills --skill paper-fetchインストール後、Claude Code で/paper-fetchを入力して読み込まれたか確認します。
同梱ファイルscripts/cloak_pdf.pyscripts/fetch.py
詳細
paper-fetch
DOI(またはタイトル)を指定して論文の PDF を取得します。優先順位に従って複数のソースを試し、最初に見つかった時点で停止します。
解決の優先順位
- Unpaywall —
https://api.unpaywall.org/v2/{doi}?email=$UNPAYWALL_EMAILを呼び出し、best_oa_location.url_for_pdfを読み取ります(UNPAYWALL_EMAILが設定されていない場合はスキップします)。 - Semantic Scholar —
https://api.semanticscholar.org/graph/v1/paper/DOI:{doi}?fields=openAccessPdf,externalIds - arXiv —
externalIds.ArXivが存在する場合、https://arxiv.org/pdf/{arxiv_id}.pdf - PubMed Central OA — PMCID が存在する場合、
https://www.ncbi.nlm.nih.gov/pmc/articles/{pmcid}/pdf/ - bioRxiv / medRxiv — DOI のプレフィックスが
10.1101の場合、https://api.biorxiv.org/details/{server}/{doi}を呼び出して最新バージョンの PDF URL を取得します。 - 出版社から直接取得(機関モードのみ —
PAPER_FETCH_INSTITUTIONAL=1)— DOI プレフィックスから出版社の PDF テンプレートを適用します(Nature / Science / Wiley / Springer / ACS / PNAS / NEJM / Sage / T&F / Elsevier)。取得を許可するのは呼び出し元自身の購読 IP / Cookie / EZproxy です。許可されていない応答は%PDFチェックに失敗し、ステップ 7 に進みます。 - Sci-Hub ミラー(デフォルトで有効。無効にするには
PAPER_FETCH_NO_SCIHUB=1を指定)— 最後の手段となるフォールバックです。PAPER_FETCH_SCIHUB_MIRRORSに指定されたミラー(または組み込みのデフォルトsci-hub.ru、sci-hub.st、sci-hub.su、sci-hub.box、sci-hub.red、sci-hub.al、sci-hub.mk、sci-hub.ee)を順に試します。すべて失敗した場合は、プロセスごとに 1 回だけhttps://www.sci-hub.pub/をスクレイピングして新しいミラーを取得します。CAPTCHA / 論文が見つからないページには PDF の iframe がないため、通知なしで次に進みます。 - それ以外の場合 → タイトルと著者を添えて失敗を報告し、ユーザーが ILL で依頼できるようにします。
CloakBrowser によるフォールバック(ダウンロード層、オプトイン — PAPER_FETCH_CLOAK=1)。 これは独立した取得元ではなく、ダウンロードのボトルネックに適用されるため、上記の取得元すべてに適用されます。解決された PDF URL が Cloudflare にブロックされた場合(HTTP 403/429、またはファイルの代わりに「Just a moment…」という HTML のインタースティシャルが返される場合)、オペレーターがオプトインしていれば、cloak_pdf.py コンパニオンを介して CloakBrowser(JS チャレンジを通過できるステルス Chromium)経由で URL を再試行します。返されたバイト列は、同じ %PDF マジックバイトと 50 MB のチェックで再検証されます。成功すると、結果には via: "cloak" が含まれます。デフォルトでは無効で、フェイルクローズドです(CloakBrowser がない場合は通知なしで次に進みます)。エージェントはオプトインできません。下記の CloakBrowser access を参照してください。
タイトルだけが指定された場合は、--title "<title>" で直接渡します。解決チェーンは次のとおりです。
- Crossref
query.title— 優先。主要なジャーナルや学会の DOI を網羅します。 - Semantic Scholar
/paper/search/match— Crossref の最上位候補の信頼度が低い(match_score < 40)場合、または次点候補との差が< 3の場合のフォールバックです。特に重要なのは、S2 が arXiv のみのプレプリント(Crossref DOI なし)も扱うことです。S2 で arXiv ID のみを持つ論文が見つかった場合は、正規の10.48550/arXiv.<id>を生成し、ダウンロードチェーンを統一します。 - Crossref の最有力候補(信頼度低) — 両方の解決サービスでうまく解決できなかった場合にのみ使用します。結果のエンベロープでは、
meta.title_resolution.low_confidence: trueとlow_confidence_reason(score_below_threshold/ambiguous_runner_up)が設定されます。これにより、エージェントは処理を中断するか、--dry-runで確認できます。
いずれの場合も、解決された DOI、採用された解決サービス、完全な resolvers_tried リスト、および最上位候補との一致状況が meta.title_resolution に表示されます。
semanticscholar-skill が登録されている場合は、タイトルから DOI への解決に、より充実した前段階の処理として利用できます。単に DOI が必要なだけでなく、関連度のランキング、スニペット検索、引用のコンテキストも必要な場合に便利です。エージェントは、このスキルの match_title() を使って externalIds.DOI を読み取る Python スクリプトを作成し、その後 paper-fetch <doi> を実行します。結果に ArXiv ID のみが含まれ、DOI がない場合は、10.48550/arXiv.<ArXiv> を生成して paper-fetch に渡します。
DOI だけが必要な場合は、--title が単一コマンドで実行する方法です。paper-fetch に組み込まれた Crossref → S2 チェーンが、ほとんどのケースに対応します。
使い方
python scripts/fetch.py <DOI> [options]
python scripts/fetch.py --title "<paper title>" [options]
python scripts/fetch.py --batch <FILE|-> [options]
python scripts/fetch.py schema # machine-readable self-description
フラグ
通常の使用時にエージェントが構成するフラグは以下のとおりです。--dry-run、--pretty、--stream、--overwrite、--timeout、--version、パラメーターの型、終了コードの対応関係を含む完全な仕様を確認するには、python scripts/fetch.py schema を実行してください(機械可読で、schema_version によるドリフトチェックに対応しています)。
| フラグ | デフォルト | 説明 |
|---|---|---|
doi | — | 取得する DOI(位置引数)。標準入力から単一の DOI を読み取るには - を使用します |
--title TITLE | — | 論文のタイトル。ダウンロード前に Crossref で DOI に解決します。位置引数の DOI/--batch とは併用できません |
--batch FILE | — | 一括ダウンロード用の、1 行につき 1 つの DOI を記載したファイル。標準入力から読み取るには - を使用します |
--out DIR | pdfs | 出力ディレクトリ |
--format | 自動 | エージェント向けの json、人間向けの text。自動判定では、stdout が TTY でない場合は json、TTY の場合は text を使用します |
--idempotency-key KEY | — | 安全な再試行用のキー。同じキーで再実行すると、ネットワーク I/O を行わずに <out>/.paper-fetch-idem/ から元のエンベロープを再生します |
エージェントによる検出:schema サブコマンド
python scripts/fetch.py schema
ネットワークアクセスなしで、CLI の完全な機械可読の説明を stdout に出力します。cli_version、schema_version、パラメーターの型、終了コード、エラーコード、エンベロープの形式、環境変数が含まれます。エージェントはこれを一度読み取って schema_version と照合してキャッシュし、キャッシュしたバージョンにドリフトがあった場合に再読み込みする必要があります。
出力仕様
stdout には、単一の JSON エンベロープが出力されます。すべてのエンベロープには meta スロットが含まれます。
成功(すべての DOI を解決):
{
"ok": true,
"data": {
"results": [
{
"doi": "10.1038/s41586-021-03819-2",
"success": true,
"source": "unpaywall",
"pdf_url": "https://www.nature.com/articles/s41586-021-03819-2.pdf",
"file": "pdfs/Jumper_2021_Highly_accurate_protein_structure_predic.pdf",
"meta": {"title": "Highly accurate protein structure prediction with AlphaFold", "year": 2021, "author": "Jumper"},
"sources_tried": ["unpaywall"]
}
],
"summary": {"total": 1, "succeeded": 1, "failed": 0},
"next": []
},
"meta": {
"request_id": "req_a908f5156fc1",
"latency_ms": 2036,
"schema_version": "1.9.0",
"cli_version": "0.13.1",
"sources_tried": ["unpaywall"]
}
}
一部成功(バッチモード — 一部の DOI が失敗し、終了コードは失敗の種類を反映します):
{
"ok": "partial",
"data": {
"results": [
{ "doi": "10.1038/s41586-021-03819-2", "success": true, "source": "unpaywall", ... },
{
"doi": "10.1234/nonexistent",
"success": false,
"source": null,
"pdf_url": null,
"file": null,
"meta": {},
"sources_tried": ["unpaywall", "semantic_scholar"],
"error": {
"code": "not_found",
"message": "No open-access PDF found",
"retryable": true,
"retry_after_hours": 168,
"reason": "OA availability changes over time; retry after embargo lifts or preprint appears"
}
}
],
"summary": {"total": 2, "succeeded": 1, "failed": 1},
"next": ["paper-fetch 10.1234/nonexistent --out pdfs"]
},
"meta": { ... }
}
next スロットは、推奨される後続コマンドの配列です。これらのコマンドを再実行すると、失敗した項目のみが再試行されます。--idempotency-key と組み合わせると、すでに成功した項目を再ダウンロードせずにバッチ全体を安全に再試行できます。
失敗(引数が不正、終了コード 3):
{
"ok": false,
"error": {
"code": "validation_error",
"message": "Provide a DOI or --batch file",
"retryable": false
},
"meta": { ... }
}
項目単位のスキップ(保存先がすでに存在し、--overwrite が指定されていない):
{
"doi": "10.1038/s41586-021-03819-2",
"success": true,
"source": "unpaywall",
"pdf_url": "https://...",
"file": "pdfs/Jumper_2021_...pdf",
"skipped": true,
"skip_reason": "file_exists",
"sources_tried": ["unpaywall"]
}
冪等性キーによる再生(同じ --idempotency-key を指定して再実行):
キャッシュされたエンベロープがそのまま返されますが、現在の呼び出しに合わせて meta.request_id と meta.latency_ms が再設定され、meta.replayed_from_idempotency_key が設定されます。ネットワーク I/O は発生しません。
stderr の進捗(NDJSON)
--format json の場合、stderr には稼働状況を示す JSON オブジェクトが 1 行につき 1 つ出力されます。
{"event": "session", "request_id": "req_...", "elapsed_ms": 0, "cli_version": "0.13.1", "schema_version": "1.9.0"}
{"event": "start", "request_id": "req_...", "elapsed_ms": 2, "doi": "10.1038/..."}
{"event": "source_try", "request_id": "req_...", "elapsed_ms": 2, "doi": "...", "source": "unpaywall"}
{"event": "source_hit", "request_id": "req_...", "elapsed_ms": 2036, "doi": "...", "source": "unpaywall", "pdf_url": "..."}
{"event": "download_ok", "request_id": "req_...", "elapsed_ms": 4120, "doi": "...", "file": "..."}
イベントの種類:session、start、source_try、source_hit、source_miss、source_skip、source_enrich、source_enrich_failed、download_ok、download_error、download_skip、dry_run、not_found、resolve_error。すべてのイベントには request_id と elapsed_ms が含まれるため、オーケストレーターは stderr の進捗状況と最終的な stdout エンベロープを関連付けられます。session イベントは各呼び出しにつき一度だけ、DOI の処理やネットワーク I/O より前に発生します。また、cli_version/schema_version が含まれるため、エージェントは最終エンベロープを待たずにキャッシュしたコピーとの照合でスキーマのドリフトを検出できます。
Semantic Scholar が、別のソースからすでに提供された PDF URL に対して、欠落している author/title の補完のみを目的に呼び出された場合、source_enrich が発生します。その fields 配列には、補完されたフィールドのみが正確に列挙されます。その補完呼び出しが失敗した場合は source_enrich_failed が発生します。その場合も Unpaywall の PDF URL が使用され、ファイル名は unknown_<year>_… にフォールバックします。
--format text の場合、stderr には人間が読める形式のテキストが出力されます。
終了コード
| コード | 意味 | 再試行可能な分類 |
|---|---|---|
0 | すべての DOI を解決/プレビューしました | — |
1 | 未解決 — 1 つ以上の DOI に OA コピーがなく、トランスポート障害もありません | 今は再試行しない(retry_after_hours 経過後に再試行) |
2 | 認証エラー用に予約(現在は未使用) | — |
3 | 検証エラー(引数が不正、入力が不足) | いいえ |
4 | トランスポートエラー(ネットワーク/ダウンロード/IO 障害) | はい |
この分類体系により、オーケストレーターは失敗を確実に振り分けられます。終了コード 4 はすぐに再試行する価値があり、終了コード 1 は再試行せず、終了コード 3 は呼び出し側のバグです。
JSON のエラーコード
再試行可能なエラーにはすべて、エラーオブジェクトに retry_after_hours のヒントが含まれているため、オーケストレーターは推測せずに再試行をスケジュールできます。
| コード | 意味 | 再試行可能 | retry_after_hours |
|---|---|---|---|
validation_error | 引数が不正、または入力が空です | いいえ | — |
title_resolve_failed | 指定された --title クエリに対して Crossref から項目が返されませんでした(より長く、整理されたタイトルを試すか、DOI を直接指定してください) | いいえ | — |
not_found | オープンアクセスの PDF が見つかりませんでした | はい | 168(1 週間 — OA が利用可能になるまでの期間は、エンバーゴ/プレプリントのタイムスケールに相当します) |
resolve_network_error | メタデータ解決サービスでトランスポートエラー(タイムアウト/5xx/403)が発生しました。OA の利用可否は不明です。not_found として扱わず、再試行してください。終了コード 4 に対応します。 | はい | 1 |
download_network_error | ダウンロード中にネットワーク障害が発生しました | はい | 1 |
download_not_a_pdf | PDF ではない応答でした(HTML のランディングページ) | いいえ | — |
download_host_not_allowed | PDF URL が SSRF 安全性チェックに失敗しました(プライベート IP/非 http(s)/80、443 以外/ブロック対象のメタデータホスト/プライベート空間に解決されるホスト名/安全でないリダイレクト先) | いいえ | — |
download_size_exceeded | 応答が 50 MB の上限を超えました | はい | 24 |
download_io_error | ローカルファイルシステムへの書き込みに失敗しました | はい | 1 |
internal_error | 予期しないエラーです | いいえ | — |
正式なマッピングは scripts/fetch.py の RETRY_AFTER_HOURS にあり、schema.error_codes で公開されています。
例
# Single DOI (JSON output when piped; text when in a terminal)
python scripts/fetch.py 10.1038/s41586-020-2649-2
# Single title (resolved to DOI via Crossref, then downloaded)
python scripts/fetch.py --title "Highly accurate protein structure prediction with AlphaFold"
# Dry-run preview (resolve without downloading)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --dry-run
# Title + dry-run — preview the resolved DOI and candidate matches
python scripts/fetch.py --title "Attention Is All You Need" --dry-run
# Force JSON (for agents even inside a terminal)
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format json
# Human-readable with pretty colors in a pipeline
python scripts/fetch.py 10.1038/s41586-020-2649-2 --format text
# Batch download, safely retriable
python scripts/fetch.py --batch dois.txt --out ./papers \
--idempotency-key monday-review-batch
# Pipe DOIs from another tool
zot -F ids.json query ... | jq -r '.[].doi' | python scripts/fetch.py --batch -
# Agent discovery
python scripts/fetch.py schema --pretty
# Streaming mode — one result per line as each DOI resolves
python scripts/fetch.py --batch dois.txt --stream
# Works without UNPAYWALL_EMAIL (skips Unpaywall, uses remaining 4 sources)
python scripts/fetch.py 10.1038/s41586-020-2649-2
環境
| 変数 | デフォルト | 目的 |
|---|---|---|
UNPAYWALL_EMAIL | 未設定 | Unpaywall API 用の連絡先メールアドレスです。任意ですが、設定を推奨します。設定しない場合、Unpaywall はスキップされます(ほかのソースは引き続き利用できます)。 |
PAPER_FETCH_INSTITUTIONAL | 未設定 | 任意の値(例:1)を設定すると、機関モードが有効になり、1 req/s のレート制限と出版社への直接フォールバックが有効になります。詳しくは以下を参照してください。 |
PAPER_FETCH_NO_SCIHUB | 未設定 | 任意の値を設定すると、Sci-Hub へのフォールバック(ステップ 7)が無効になります。 |
PAPER_FETCH_SCIHUB_MIRRORS | 未設定 | 優先順位順に試すミラーホスト名をカンマ区切りで指定します(例:sci-hub.ru,sci-hub.st,sci-hub.su)。組み込みのデフォルト設定を上書きします。 |
PAPER_FETCH_CLOAK | 未設定 | 任意の値を設定すると、CloakBrowser フォールバックが有効になります。Cloudflare にブロックされた PDF(HTTP 403/429、または PDF ではないインタースティシャル)は、ステルス Chromium 経由で再試行されます。以下の CloakBrowser へのアクセス を参照してください。 |
CLOAKBROWSER_PYTHON | 自動 | cloakbrowser を import できる Python のパスです。cloak フォールバックで使用されます。自動検出の順序:この変数 → ~/github/CloakBrowser/.venv/bin/python → 現在のインタープリター。 |
PAPER_FETCH_CLOAK_HEADED | 未設定 | 任意の値を設定すると、ヘッドレスではなく、画面に表示される ヘッド付きブラウザーを起動します。より難しい Cloudflare チャレンジ(例:science.org)ではヘッドレスモードが失敗し、実ウィンドウでのみ解除できることがあります。cloak フォールバックで HTTP 403/「Just a moment…」が返され続ける場合に設定してください。ディスプレイが必要です。 |
CloakBrowser によるアクセス(オプトイン)
一部の出版社(例:science.org)は Cloudflare の背後にあり、通常の HTTP クライアントに PDF ではなく 403/429 や「Just a moment…」という JavaScript チャレンジページを返します。そのため、ブラウザーからは正当にアクセスできる URL でも、デフォルトの urllib によるダウンロードでは取得できません。CloakBrowser は、こうしたチャレンジを通過できるステルス Chromium です。このスキルは cloakFetch のアプローチを取り入れています。
オプトイン: export PAPER_FETCH_CLOAK=1 を設定します(さらに、cloakbrowser をインポートできる Python が必要です。CLOAKBROWSER_PYTHON を参照してください)。
仕組み: フォールバックは新しい取得元としてではなく、ダウンロード層に組み込まれています。そのため、解決済みの URL(Unpaywall、出版社の直接配信、Sci-Hub など)すべてに適用されます。Cloudflare によってブロックされると、fetch.py は解決済みの Python を使って cloak_pdf.py コンパニオンを起動します。CloakBrowser は PDF ホストのオリジンを読み込んで JavaScript チャレンジを解決し、その後、ページ内の fetch() で PDF を取得します(これにより、ブラウザー本来のフィンガープリントと cf_clearance Cookie を伴うリクエストになります)。そして、バイト列を標準出力に返します。fetch.py 自体は標準ライブラリーのみを使用し、cloakbrowser をインポートすることはありません。
ヘッドレスとヘッド付き。 コンパニオンはデフォルトでヘッドレス実行されます。一部のチャレンジ(例:science.org)はヘッドレス Chromium では突破できず、「Just a moment…」のままになるため、PAPER_FETCH_CLOAK_HEADED=1 を設定してウィンドウを表示すると解決できます。確認済みの例:通常のクライアントでは 403 が返される www.science.org/doi/pdf/… の PDF を、ヘッド付きの Cloak フォールバック経由で正常にダウンロードできます。
同一オリジンのみ。 ページ内の fetch は同一オリジンで実行されるため、解決済み URL がブロックされたホスト上の PDF への直接リンクである場合(例:www.science.org/doi/pdf/…)にフォールバックが機能します。クロスオリジンにリダイレクトされる URL(例:単体の doi.org/… リンク)や、チャレンジが解決されない旧式のホストでは、フェイルクローズして次の取得元に移ります。
変更されない点:
- 返されたバイト列は、同じ
%PDFマジックバイト検査と 50 MB のサイズ上限で再検証されます。ブラウザーを起動する前に URL を検査する SSRF 防御も引き続き適用されます。 - CAPTCHA は解決しません。 CloakBrowser が突破するのは自動化された JavaScript チャレンジであり、インタラクティブなチャレンジではありません。インタラクティブな Turnstile では引き続きフェイルクローズし、次の取得元に移ります。
- フェイルクローズします。
cloakbrowserをインポートできる Python がない、ヘルパーが見つからない、またはエラーが発生した場合は、何も通知せず次の取得元に移ります。ブロックされた取得に成功したとエージェントに伝えることはありません。 - エージェント自身ではオプトインできません。
PAPER_FETCH_CLOAKは人間のオペレーターが設定する必要があります。機関モードと同じ信頼境界です。 - 成功すると、結果ごとのオブジェクトに
via: "cloak"が含まれ、オーケストレーターはフォールバックが使用されたことを確認できます。
コスト: フォールバックが発動すると、実際のブラウザーが起動します(約 20〜40 秒、初回ダウンロード時は Chromium が約 200 MB)。通常のダウンロードがブロックされた後にのみ発動するため、通常の成功パスには影響しません。
機関認証によるアクセス(オプトイン)
多くの研究者は、所属機関の IP 範囲(キャンパス内または VPN 経由)を通じて、正規のサブスクリプションアクセス権を利用しています。Paper-fetch は、PDF を提供するかどうかを出版社独自の認証(お使いの IP、セッション Cookie)に判断させることで、そのアクセスを利用できます。
モードによってホストへの到達性が変わることはありません。パブリックモードでも、OA API(Unpaywall、Semantic Scholar、bioRxiv、PMC)が返す URL をすでに信頼しており、SSRF 防御を通過した HTTPS ホストであれば取得します。機関モードでは、次の 2 つが追加されます。(1)すべての OA ソースで見つからなかった場合に、DOI のプレフィックスをもとに出版社側の PDF URL を組み立てる出版社への直接フォールバック(上記のステップ 6)。これにより、機関の IP/Cookie を使って取得を認証できます。(2)バッチジョブによって IP がスロットリングされたり、「体系的なダウンロード」を理由にアクセス禁止になったりしないようにする、1 秒あたり 1 リクエストのレートリミッター。
オプトインするには: export PAPER_FETCH_INSTITUTIONAL=1
機関モードでの変更点:
| 項目 | パブリック(デフォルト) | 機関 |
|---|---|---|
| ホストへの到達性 | SSRF 防御を通過した、あらゆる公開 HTTPS ホスト | 同じ |
| SSRF 防御 | 適用(プライベート IP/非 HTTP(S)/80・443 以外/クラウドメタデータをすべてブロック) | 適用 — 同じルール |
| 出版社への直接フォールバック | オフ | オン — DOI プレフィックスから出版社の PDF URL を構築し、すべての OA ソースで見つからなかった場合の最終手段として使用 |
| レート制限 | なし | 1 秒あたり 1 リクエストのトークンバケット(すべての送信リクエストが対象) |
meta.auth_mode | "public" | "institutional" |
変わらない点:
%PDFのマジックバイトチェックと 50 MB のサイズ上限(HTML のランディングページやサイズ超過のレスポンスが通過するのを防止します)- CAPTCHA の解除は一切行いません。出版社がチャレンジを表示した場合、レスポンスは
%PDFで始まらないため、paper-fetch は次のソースに進みます。 - 機関モード自体では、ブラウザー自動化、Playwright、ステルス機能を使用しません。出版社への直接 URL に対する通常の HTTP 取得です。(ブラウザー自動化は別途オプトインする機能で、上記の CloakBrowser フォールバックを
PAPER_FETCH_CLOAKで有効にします。) - エージェント自身ではオプトインできません。
PAPER_FETCH_INSTITUTIONALは、シェル環境で人間の運用担当者が設定する必要があります。これが信頼境界です。
パブリックモードで paper-fetch が OA 版を見つけられなかった場合、エラーエンベロープには suggest_institutional: true が含まれ、環境変数を設定するようユーザーに案内するヒントが表示されます。エージェントは、何も通知せずに失敗するのではなく、この内容をそのまま表示できます。
利用規約に関する注意: ほぼすべての出版社のサブスクリプションでは、「体系的なダウンロード」が禁止されています。1 秒あたり 1 リクエストのレート制限と既存のファイルごとの冪等性は、個人の研究利用が許容範囲内に収まるように設計されています。paper-fetch プロセスを多数並行して実行したり、レート制限を解除したりすると、出版社による機関全体への IP 禁止措置を招く可能性があります。絶対に行わないでください。
注意事項
- 認証は委任されます。 エージェントがログイン用サブコマンドを実行することはありません。人間またはオーケストレーターが環境内で
UNPAYWALL_EMAILを設定し、エージェントはそれを引き継ぎます。メールアドレスが設定されていない場合は、残りの 4 つのソースに自動的に切り替わります。 - 信頼の適用方向は決まっています。 CLI 引数はエントリーポイントで一度だけ検証されます。SSRF 防御、
%PDFのマジックバイトチェック、50 MB のサイズ上限は、エージェントのリクエスト時ではなく環境レイヤーで適用されます。エージェントがフラグを渡して安全性を緩めることはできません。機関モード(およびそのレート制限に伴うリスクプロファイル)のオプトインは、環境変数を使って運用担当者が行います。 - ダウンロードは自然に冪等になります。 同じ
--outを指定して再実行すると、すでに存在するファイルはスキップされます(決定的なファイル名:{first_author}_{year}_{journal_abbrev}_{short_title}.pdf。メタデータにジャーナル/開催地がない場合は、ジャーナルの部分が省略されます)。--idempotency-keyを併用すると、ネットワーク I/O を一切行わずに、同一のエンベロープを再現できます。 - デフォルトの出力ディレクトリ:
./pdfs/