🧬 연구 허브

Skill

paper-pdf-chrome-rules 코드 참조

페이월·Cloudflare 저널용 수집기. 출판사별 규칙 DB 를 스스로 학습한다.

기존 paper-pdf-downloader 가 Unpaywall/Crossref API 체인만 다루는 반면, 이 스킬은 undetected-chromedriver + 저널별 규칙 DB 로 paywall·Cloudflare 저널까지 처리한다. 성공한 규칙이 DB 에 누적되어 운영할수록 더 똑똑해진다.

언제 사용하나

  • Unpaywall/Crossref API 로는 못 받는 paywall 저널 (Wiley, Elsevier, AJR, Radiology, AAN …)
  • Cloudflare 방어가 있는 OA 저널 (MDPI, Frontiers 등 일부)
  • 기관 EZ-Proxy 세션을 활용해야 하는 학회지

5가지 URL 변형 패턴 (v2)

Pattern적용예시
transforms (독립 시도)단순 {doi} 치환["https://onlinelibrary.wiley.com/doi/pdfdirect/{doi}"]
sequence (방문 순서)첫 URL 방문 → 두 번째 URL nav (referer/세션)["/doi/epdf/{doi}", "/doi/pdf/{doi}?download=true"]
sequences (multi)여러 sequence 동시 시도[[seq1...], [seq2...]]
{current_url_html_to_pdf}페이지 URL 의 /html → /pdfAME 일부
{current_url_last_to_pdf}마지막 segment → /pdfAME (/article/view/14623/12290 → /article/view/14623/pdf)

6가지 method (priority 순)

method적용파라미터
unpaywallOA 저널 자동(없음)
url_transformpublisher 별 URL 패턴 (transforms / sequence / sequences)위 5가지 패턴
citation_pdf_urlGoogle Scholar 표준 meta 태그 (범용)(없음)
selectorCSS 셀렉터 + <button data-config> URL 추출{"selectors": ["button.js-ejp-login-btn"]}
ez_proxy기관 세션 경유{"proxyUrl": "..."}
browser_capture마지막 수단 (렌더 PDF)—

v2 새 기능

OneTrust, Quantcast, 일반 "Accept all cookies" 버튼 자동 클릭. iframe 포함, JS click fallback.

2) Cloudflare 자동 통과 (human-like)

  • "Just a moment..." challenge 페이지 자동 감지
  • ActionChains 마우스 천천히 다단계 이동 + 스크롤 + 자연 대기
  • 실패 시 logs/challenge-screenshots/ 에 스크린샷
  • 35초 내 자동통과 실패 시 → PdfHumanClickQueue 자동 적재 후 즉시 다음 논문으로 (Conv_home 통합).

사용자가 한가할 때 scripts/human-click-session.py 로 일괄 처리 (사람 클릭 1번 → publisher 별 cf_clearance 쿠키 영구 발급 → 같은 publisher 후속 논문 자동 통과). 상세는 Conv_home docs/pdf-human-click-queue.md.

3) Button data-config 추출 (LWW)

<button data-config='{"eventDetail":{"url":"..."}}'> 형태에서 PDF URL 자동 추출.

4) PDF 내용 검증 (가짜양성 방지)

다운로드 후 제목 키워드 매칭. 매치 < 40% 이면 자동 롤백 + 파일 삭제.

  • 단 'Letter to the editor', 'Correspondence', 'Corrigendum' 등은 통과

5) 좀비 Chrome 자동 정리

실행 시 pkill undetected_chromedriver + Singleton 파일 삭제.

6) 사람 수정 저자정보 보호

extract-authors 스크립트가 다음 중 하나라도 해당하면 무조건 skip:

  • ExtractedAuthorInfo.source = 'manual'
  • Activity 'paper.authorEdited' 로그 존재

검증된 Publisher 규칙 (2026-04-15 시드 기준)

# URL 변형이 통하는 저널들
wiley:
  doiPrefix: 10.1002
  method: url_transform
  params: {"transforms": ["https://onlinelibrary.wiley.com/doi/pdfdirect/{doi}"]}

ajr_arrs:
  doiPrefix: 10.2214
  method: url_transform
  params: {"transforms": ["https://www.ajronline.org/doi/pdf/{doi}"]}

mdpi:
  doiPrefix: 10.3390
  method: url_transform
  params: {"transforms": ["{current_url}/pdf"]}   # article_url → article_url/pdf

liebert_sage:
  doiPrefix: 10.1089
  method: url_transform
  # 2023년 Liebert → SAGE 인수로 URL 변경됨
  params: {"transforms": ["https://journals.sagepub.com/doi/pdf/{doi}"]}

# citation_pdf_url 이 통하는 OA 저널
plos: {doiPrefix: 10.1371, method: citation_pdf_url}
frontiers: {doiPrefix: 10.3389, method: citation_pdf_url}

사용법 (프로젝트 통합)

전제: 전용 Chrome 프로파일 셋업 (1회)

python3 scripts/chrome-driver-pdf.py --setup
# Chrome 창이 뜨면 기관 EZ-Proxy 로그인 → 창 닫지 말고 터미널 엔터

단일 DOI 다운로드 (DB 규칙 사용)

python3 scripts/chrome-driver-pdf.py \
    --doi 10.1148/radiol.2018181197 \
    --output /tmp/test.pdf \
    --db-path prisma/prod.db

Publisher discovery (대표 1건 테스트)

# 미해결 publisher 목록
python3 scripts/discover-publisher.py --list

# 특정 prefix 테스트 (headful Chrome 창 뜸)
python3 scripts/discover-publisher.py --prefix 10.2214

Publisher 일괄 재다운로드 (페이싱 + DOI dedupe 자동)

python3 scripts/batch-redownload-publisher.py --prefix 10.2214
# --dedupe-by-doi 기본 ON: 같은 DOI 는 1번만 다운, N 레코드에 공유
# 페이싱: 건 사이 15~35s 랜덤, 5건마다 60~120s 휴식

저자 재추출 (DOI 캐시)

DATABASE_URL="file:./prisma/prod.db" \
  npx tsx scripts/extract-authors-for-updated-papers.ts --skip-existing

Publisher 규칙 추가하는 법

자동 (discovery 가 성공한 경우)

python3 scripts/discover-publisher.py --prefix 10.XXXX
# 성공 시 chrome-driver-pdf.py 가 successCount++ 해서 자동 학습

수동 (새 URL 패턴 발견 시)

  1. 브라우저에서 https://doi.org/{sample DOI} 열어 PDF 링크 확인
  2. /admin/pdf-rules 에서 "규칙 추가":
    • publisherKey: 식별 키 (예: new_journal)
    • doiPrefix: 해당 저널 DOI prefix
    • domainPattern: URL substring (선택)
    • method: url_transform
    • params: {"transforms": ["발견한 패턴"]}
    • priority: 1 (최우선)
    • confirmedByHuman: ✅ (자동 재조정에서 제외)
  3. 다시 discover-publisher.py --prefix 로 재검증
  4. 성공하면 batch-redownload-publisher.py --prefix 로 일괄 처리

페이싱 (차단 방지 — 필수)

구분기본값
건 사이15~35초 랜덤
5건 연속 후60~120초 휴식
Publisher 전환60초+
차단 지표(429/captcha) 감지즉시 중단

절대 --no-pace 쓰지 말 것. publisher 에 차단되면 해당 IP/세션이 최소 수시간 블록됨.

DOI 단위 dedupe (중요)

DB 에 같은 DOI 가 여러 Content 레코드(교수 n명 등록)로 존재하는 경우가 있다. --dedupe-by-doi (기본 ON) 이 작동하면:

  • 다운로드: 1번만 수행 → pdfFile 경로를 모든 sibling 레코드에 공유
  • 저자 추출: WOS/PubMed 쿼리도 1번만 → ExtractedAuthorInfo 만 각자 저장
  • 결과: publisher 트래픽 ↓, 저장 공간 ↓, 차단 위험 ↓

실패 패턴과 대응

실패 원인해결
headless 에서 Access Deniedheadful 로 실행 (기본값)
403 / Cloudflare기관 EZ-Proxy 세션 필요, --setup 다시
가짜 양성 (엉뚱한 PDF)selector 규칙 비활성, url_transform 으로 교체
Publisher URL 변경브라우저 직접 확인 → 어드민 UI 수정
신규 publisherdiscovery CLI 실행 → 성공/수동 rule

성공률 자동 관리

  • 성공 시 successCount++, 실패 시 failureCount++
  • 주기적으로 /admin/pdf-rules 의 "성공률 집계 + priority 재정렬" 클릭
  • confirmedByHuman=true 규칙은 건드리지 않음 (수동 확정 보호)

제약 & 주의

  • macOS: chrome-driver headful 은 GUI 세션 필요. PM2 서비스(프로덕션 서버) 에서 직접 실행 불가.
  • 자동화 한계: cron 으로 돌리려면 사용자가 로그인된 GUI 세션 안에서 실행해야 함.
  • 검증 권장: 다운로드 성공 후 pdftotext -l 1 file.pdf 로 첫 페이지 텍스트 확인.
  • 차단 지표: 429/blocked/captcha 감지 시 즉시 break.

관련 스킬·문서

  • paper-pdf-downloader (API-only 구 스킬, 폴백 1단계)
  • paper-author-extractor-wos / paper-author-extractor-pubmed (저자 추출)
  • 프로젝트 내: docs/pdf-download-pipeline.md, docs/pdf-download-learning-workflow.md

설계 철학

  1. 저널 = 방법 1:1 — 같은 저널 내 여러 논문마다 발견할 필요 없음
  2. 누적 학습 — 한 번 성공한 규칙이 DB 에 쌓여 다음에 자동 재사용
  3. DOI 단위 dedupe — publisher 에 부담 최소, 저장·시간 절약
  4. 페이싱 필수 — 속도보다 안정성. 차단되면 복구 어려움
  5. 수동·자동 혼합 — 인간 확정(confirmedByHuman) 과 자동 재조정이 공존