PMC / OA 文献 PDF 取得
適用前提
開放アクセス(OA) 文献(CC BY / CC BY-NC / CC BY-NC-ND など)のみに使用すること。有料購読論文のペイウォール回避目的での使用は禁止——これは著作権保護の迂回に該当する。OA状態の判定:Unpaywall が is_oa: true を返す、または EuropePMC の isOpenAccess: Y を確認。
機関購読シナリオ(重要な補足)
ユーザーの所属大学・研究機関が出版社データベースを購読している場合、手動での単一論文ダウンロードは認可範囲内で完全に合法だが、以下の点に注意:
- キャンパスネットワーク / 機関IP範囲内:出版社はIPアドレスを認証に使用するため、フォールバックチェーンの第5段階(出版社直リンク)は通常200を返し、Cloudflareによるブロックも発生しない。この場合、本スキルの既存パイプラインは購読コンテンツを直接取得可能で、ログイン処理は不要。
- 外部アクセス:可能な限り所属機関が提供するシステムレベルVPN(スクリプトに対して透過的で、一度設定すればOK)を優先使用すること。WebVPN(URL書き換え型プロキシ)は機関ごとの書き換えルールが異なり、セッションCookieが必要なため、本スキルではサポートしない。
- Shibboleth / OpenAthens の自動ログインは記述しないこと:統一認証情報をローカルに保存する必要があり、セキュリティリスクとなる。また各機関のIdPフローは多様で、汎用的な実装は不可能。
- バルクダウンロードの禁止:ほぼすべての出版社の機関ライセンスは、「手動での単一論文ダウンロード」と「組織的/バルクダウンロード」を区別している。購読がある場合でも、スクリプトによるバッチ取得はライセンス違反となり、所属機関の全IPがブロックされる可能性がある。大規模な全文テキストマイニングが必要な場合は、出版社のTDM(テキストマイニング)API(例:Elsevier TDM API、Springer Nature Meta API)を申請し、公式のクォータルートを利用すること。本スキルをバッチループに使用しないこと。
フォールバックチェーン(厳密な順序で)
| 優先度 | 経路 | コマンド / エンドポイント | 説明 |
|---|---|---|---|
| 1 | EuropePMC 構造化全文 | https://www.ebi.ac.uk/europepmc/webservices/rest/{PMCID}/fullTextXML | 最も安定。セクション見出しレベルまで正確な全文構造を抽出するために使用。JATS XMLを返す。 |
| 2 | Unpaywall によるOA場所の特定 | https://api.unpaywall.org/v2/{DOI}[email protected] | oa_locations[].url_for_pdf を読み、PDF直リンクを直接取得。 |
| 3 | PMC PoW 解決ダウンロード | scripts/pmc_pdf.py <PMCID> <out.pdf> | 下記参照。本文PDF取得の主力手法。 |
| 4 | EuropePMC 補足資料 | https://www.ebi.ac.uk/europepmc/webservices/rest/{PMCID}/supplementaryFiles | ZIPを返す:元画像(gr1… = Figure 1…)+ 補足資料PDF(mmc1.pdf)。 |
| 5 | 出版社直リンク | 各出版社 .../article-pdf/... | 通常403(Cloudflare)。最後の手段として試すこと。 |
動作確認済みの無効なエンドポイント(時間を無駄にしないこと)
…/rest/{PMCID}/fullTextPDF→ 404(廃止)…/rest/{PMCID}/fulltextRepo→ 403https://europepmc.org/articles/{PMCID}?pdf=render→ 403https://europepmc.org/backend/ptpmcrender.fcgi?accid=…&blobtype=pdf→ 403https://www.ncbi.nlm.nih.gov/pmc/utils/oa/oa.fcgi?id=…→ 404(OAサービス移転)ashpublications.orgの記事ページは、ヘッドレスブラウザで Cloudflare の「お待ちください…」チャレンジ画面で停止するため使用不可
★ 前提条件:PMC の PDF ファイル名は固定ではないため、事前に解決する必要がある
https://pmc.ncbi.nlm.nih.gov/articles/{PMCID}/pdf/<ファイル名>.pdf の <ファイル名> はジャーナルと発行年によって変化する。main.pdf とハードコードすると、多くの場合404が返る。
| 実測ジャーナル | 実際のファイル名 |
|---|---|
| Am J Hematol | main.pdf |
| Blood Advances | main.pdf |
| HemaSphere | HEM3-10-e70338.pdf |
| Am J Hematol | AJH-101-1790.pdf |
| Liver International | LIV-46-0.pdf |
| Haematologica | 111300.pdf(巻111 + 開始ページ300、ジャーナル略称なし) |
| Blood(NIH著者原稿) | nihms-2192199.pdf |
解析方法(優先順位順;実測の複数例で全て1番目にヒット)
- 記事ページの
<meta name="citation_pdf_url" content="https://pmc.ncbi.nlm.nih.gov/articles/{PMCID}/pdf/XXX.pdf">← 最も信頼性が高い grep -oE 'href="[^"]*?/pdf/[^"]+?\.pdf"'で最初のヒットを取得- フォールバック:
pdf/main.pdf
命名規則の参考(信頼はしないこと。解析が正しいかどうかの手動確認のみに使用):Wiley系OAでは <ジャーナル略称>-<印刷巻>-0.pdf(例 LIV-46-0.pdf)が一般的。採番記事の場合は <ジャーナル略称>-<巻>-<記事番号>.pdf(例 HEM3-10-e70338.pdf)。同じジャーナル内では <略称>-<巻>-<開始ページ>.pdf(例 AJH-101-1790.pdf)が多い。また「巻+開始ページでジャーナル略称なし」のバリエーションもある(Haematologica の 111300.pdf = 巻111 + 開始ページ300)。
記事ページ取得時は必ずCookieを破棄すること:独立した、Cookieを保持しないリクエストで記事ページを読み、PDFリクエストは新しいCookieセッションから開始する必要がある。記事ページで生成された
ncbi_sid/ncbi_pingerをPDFリクエストに持ち越すと、直接reCAPTCHAに昇格される(実測)。scripts/pmc_pdf.pyはこの実装になっている。
核心的手法:PMC Proof-of-Workのオフライン解決
原理
https://pmc.ncbi.nlm.nih.gov/articles/{PMCID}/pdf/<ファイル名>.pdf は、リスクポリシーに基づいて2種類のチャレンジページを切り替える:
- PoWページ(1817バイト、タイトル
Preparing to download ...)→ オフラインで解決可能。これが目的のチャネル。 - reCAPTCHAページ(約20KB、タイトルに
reCAPTCHAを含む)→ オフラインでは解決不可。
PoWをトリガーしreCAPTCHAを回避する鍵:シンプルなUser-Agentを使用し、PDFをリクエストする前に記事ページにアクセスしないこと。
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
逆効果(reCAPTCHAに昇格される、実測):
- 完全なChrome UA(
…AppleWebKit/537.36 … Chrome/1xx.0.0.0 Safari/537.36) - 先に
GETで記事ページにアクセス(ncbi_sid/ncbi_pingerを保持)してからPDFをリクエスト
PoW アルゴリズム(pow-*.js からリバースエンジニアリング済み、確認済み)
PoWページには4つの定数が埋め込まれている:POW_CHALLENGE / POW_DIFFICULTY / POW_COOKIE_NAME / POW_COOKIE_PATH。
- 最小の非負整数
nonceを求め、sha256(challenge + str(nonce)).hexdigest()の先頭が'0' * difficultyと一致すること。 - Cookieを設定:
<POW_COOKIE_NAME> = challenge + "," + nonce(パスはPOW_COOKIE_PATH)。 - そのCookieと
Referer: https://pmc.ncbi.nlm.nih.gov/articles/{PMCID}/を付けてPDF直リンクに再リクエスト →200とContent-Type: application/pdfが返るはず。
典型的な difficulty = 4(期待されるハッシュ数は約65536回、ローカルではサブ秒で完了)。
ワンクリックスクリプト
# 推奨:実際のPDFファイル名を自動解析(citation_pdf_url経由)、その後PoWをオフライン解決
python3 scripts/pmc_pdf.py PMC13575452 ./out.pdf
# 自分でファイル名を解析済みの場合、直接指定して記事ページリクエストをスキップ
python3 scripts/pmc_pdf.py PMC13575452 ./out.pdf --pdf-name LIV-46-0.pdf
# サイレントモード(結果行のみ出力)
python3 scripts/pmc_pdf.py PMC13575452 ./out.pdf --quiet
スクリプトの動作:まずシンプルなUAで、Cookieを破棄して記事ページにアクセスし、実際のPDF直リンクを解析 → 次に新しいCookieセッションでそのPDFにコールドリクエスト → 直接PDFが得られれば保存、そうでなければPoW定数を解析、解決、Cookieを埋め戻して再試行。チャレンジを強制的に再取得したい場合は再実行する(スクリプトはCookieをディスクに保存せず、毎回新しいセッションになる)。
★★ 絶対に覚えておくべき2つの厳格なルール(実測による血の教訓)
1. 2番目の位置引数は出力ファイルのフルパスであり、ディレクトリではない。 ディレクトリを渡すと、PDFを取得した後に
IsADirectoryError: [Errno 21] Is a directoryがスローされ、それまでのPoW解決は無駄になり、取得したPDFも失われる。 ❌pmc_pdf.py PMC13482240 --outdir /path/to/dir✅pmc_pdf.py PMC13482240 /path/to/dir/xxx.pdf2. PMCのPDFチャネルには「一度きり」の特性がある。 初回のコールドリクエストはほぼ確実にPoWを通過し、実際のPDFを取得できる。その後、同じPDFに対して再度リクエストすると、ほぼ確実にreCAPTCHAに昇格される(「UAを変えればOK」という通常のレート制限とは異なる)。 したがって最初から出力パスを正しく指定し、決して「試しに実行してみる」ことをしてはならない。 実測:初回
status=200 type=application/pdf bytes=23970819、数秒後に同じURLを再実行するとtype=text/html bytes=21329(reCAPTCHAチャレンジページ)に変化。 一度この機会を無駄にすると、次のセクションの「出版社公式サイトへのフォールバックルート」に直接進むこと。PMCで繰り返しリトライしてはならない。
トラブルシューティング
| 現象 | 原因 | 対応 |
|---|---|---|
HTMLが返り、POW_CHALLENGE を含む | PoWチャネルに入った(正常) | スクリプトで解決 |
HTMLが返り、recaptcha を含む | reCAPTCHAに昇格された | シンプルなUAに変更、セッションCookieをクリア、少し待ってから再試行。それでも失敗する場合は経路4/5を使用 |
| 404 / 「Article not found」 | PDFファイル名が間違っている | 記事ページに戻って citation_pdf_url を解析すること。main.pdf をハードコードしないこと |
nonce がなかなか収束しない | difficulty が引き上げられた | difficulty を表示して判断。>5の場合は非同期またはマルチプロセスでの解決を検討 |
| 解決成功したがPDFがHTMLのまま | Cookieのドメイン/パスが一致しない | Cookieのドメインが pmc.ncbi.nlm.nih.gov、パスが POW_COOKIE_PATH であることを確認 |
IsADirectoryError: Is a directory | 出力引数をディレクトリとして扱っている | 第2引数は出力ファイルのフルパス。 このエラーが出るときは、実はPDFは取得できているが、ディスクに書き込まれていない——すぐに再実行しないこと(reCAPTCHAを誘発する)。まずパスを修正し、すでに昇格している場合は出版社公式サイトルートに進む。 |
| 初回成功、2回目で即reCAPTCHA | PMCが同一PDFパスに対して「一度きり」のレート制限をかけている | 上記の厳格ルール2を参照。PMCを諦め、出版社公式サイトへのフォールバックルートに進む。 |
citation_pdf_url が -mmc1.pdf を返す | 当該論文のPDFがアーカイブされておらず、補足資料が解析された | Blood などのジャーナルでは「JATS全文はあるが本文PDFはアーカイブされていない」ことがある。すぐにPMCルートを諦め、補足資料を本文の代わりに使用しないこと(実測)。 |
★ IPレベルのレート制限が存在する(実測) 短時間にPMCに対してPDFを繰り返しリクエストする(例:連続して複数の論文をダウンロードする)と、正しいシンプルなUAを使用していても、直接reCAPTCHAに昇格される。45秒待っても回復しない。症状:「初回成功、その後すべて失敗」。 対応: ① バッチダウンロード時はリクエスト間に十分な間隔を空ける。② 一旦reCAPTCHA状態になったら、繰り返しリトライしない(ブロックが長期化するだけ)。代わりに他の作業を先に進めるか、経路4(
supplementaryFiles)と出版社直リンクを使用する。③ どうしても複数論文が必要な場合は、事前にバッチで準備するのではなく、必要なときに都度ダウンロードすることを優先する。
★★ フォールバックルート:PMCがreCAPTCHAで使えない場合、出版社公式サイト経由に切り替える(実測で有効)
PMC直リンクが2回連続でreCAPTCHAに昇格された場合(IPレベルのレート制限、待機後も回復しない)、対象ジャーナルが完全OAジャーナルであれば、PMCをリトライし続けるよりも出版社公式サイト経由の方が信頼性が高い:
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
# 1) DOIを解決 → 最終的にどの出版社の記事ページに着地するかを確認
curl -s -o /dev/null -w "final: %{url_effective}\nHTTP: %{http_code}\n" -A "$UA" -L "https://doi.org/<DOI>"
# 2) 記事ページで実際のPDF直リンクを解析
curl -s -A "$UA" "https://<publisher-article-page>" | grep -oiE '<meta name="citation_pdf_url"[^>]*>|href="[^"]*\.pdf[^"]*"' | sort -u | head
# 3) Refererを付けて直接取得(出版社はRefererをほとんどチェックしないが、付けておくとより安定)
curl -sL --retry 3 -A "$UA" -e "$ARTICLE_PAGE" -o "$OUT" "$PDF_URL"
★★ 出版社ページがCloudflareで完全にブロックされている場合:ブラウザの「PDFに印刷」を使用する(実測で有効)
ashpublications.org(ASH系ジャーナル)は今回、Cloudflareによる完全なブロックが発生した。以前は有効だった「10秒待ってからopen」という経験則は再現できなかった。有効な代替経路:
- PMCの全文ページ(PDFではない)を開く。出版社サイトを迂回する。
- ブラウザツールでそのページをPDFに印刷する:
agent-browser pdf - reCAPTCHAが最初に表示された場合は、openをもう一度実行すれば通過できる。
判定のポイント:対象ジャーナルがASH / ScienceDirectのような強力なCloudflare防御を採用している場合、curlで繰り返しリトライしても成功確率は低く、ブロックを悪化させるだけ。ブラウザチャネルに直接切り替えること。
- 動作確認済み:Haematologica / Ferrata Storti ——
doi.org/10.3324/haematol.2025.287698→haematologica.org/article/view/12187→haematologica.org/article/download/12187/78653→ 2.91 MB / 14ページ。 - 不可:Cloudflare系チャレンジでブロックされる出版社(ASH
ashpublications.org、Wileyonlinelibrary.wiley.com/doi/pdfdirect/...、De Gruyter、Thieme)——これらはPMCチャネルまたはブラウザセッションに戻るしかない。 - 事前判断:完全OA / Gold OA ジャーナルのみこのルートを試す価値がある(購読ジャーナルはペイウォールに当たり、迂回は禁止)。
★ 完全性チェック:大ファイルのダウンロードが中断されると xref が破損する
curl でPDFを取得中にSIGTERM / タイムアウトで中断された場合、ファイルヘッダは正常な %PDF-1.6 で、ファイルサイズも一見妥当だが、pdfinfo は Invalid XRef entry を報告する。したがってダウンロード後は、ファイルヘッダとファイルサイズだけを見て判断してはならず、以下の3つすべてが揃っていなければならない:
head -c 8 "$OUT" # %PDF-…
tail -c 32 "$OUT" | od -c | tail -3 # startxref … %%EOF が表示されるはず
pdfinfo "$OUT" | grep -E "Pages|Page size" # ページ数が「巻号ページ」から推定される数と一致するはず
この3つが全て揃って初めて完全とみなす。不足している場合は完全に再ダウンロードする(--retry 3 を追加)。破損したファイルを成果物として渡してはならない。
補足資料と画像
本文PDFが取得できない場合でも、supplementaryFiles エンドポイントは通常使用可能で、その内容はしばしば論文に付随する画像と補足方法である:
curl -sL -o sup.zip "https://www.ebi.ac.uk/europepmc/webservices/rest/PMC13049628/supplementaryFiles"
unzip -o sup.zip -d sup/
命名規則(Wiley / ASH系):*-gr1…*-grN は Figure 1…N に対応、*-ga1 はグラフィカルアブストラクト、*-mmc1.pdf は補足資料。
ブラウザツール使用時
クリック、ログイン、JSレンダリングが必要な場合にのみ agent-browser を使用すること。純粋なダウンロードシナリオではブラウザを起動しない——PoWはオフラインで解決可能であり、Chromiumを起動すると数百MBのオーバーヘッドと失敗の可能性が増えるだけ。どうしてもブラウザが必要な場合:
- 使用可能なnodeがPATHに含まれていることを事前に確認する(そうしないとdaemonの起動がタイムアウトする)。
- タスク終了後は必ず
agent-browser closeを実行し、ゾンビdaemonを残さない。
確認リスト
- ダウンロード前に目的の文献がOAであることを確認する(または現在機関購読のIP範囲内にいることを確認する——この場合、出版社直リンクが直接使用できるが、それでも手動での単一ダウンロードに限り、バッチは禁止)。
- 記事ページの
citation_pdf_urlから実際のPDFファイル名を解析する(main.pdfをハードコードしない)。 - 保存後、ファイルヘッダが
%PDFであることを確認する。 - 末尾で
startxref/%%EOFを確認する(大ファイルが中断されると、ヘッダは正常だがxrefが破損した半製品ができる。pdfinfoで識別可能)。 - ページ数を検証する:
re.findall(rb'/Count\s+(\d+)', data)または/Type /Pageのカウントを使用する。file out.pdfのページ数は信頼しない(macOSでは