🧬 연구 허브

Skill

browser-pdf-capture 코드 참조

웹페이지를 PDF 로 캡처한다. 봇 감지가 있으면 우회 브라우저를 쓴다.

웹페이지를 방문하고, 콘텐츠를 확장한 후 PDF로 저장합니다.

사용법

DOI 또는 URL을 인자로 전달하면 해당 페이지를 PDF로 캡처합니다.

/browser-pdf-capture 10.1016/j.phymed.2025.156973
/browser-pdf-capture https://www.sciencedirect.com/science/article/pii/S0944711325006117

두 가지 실행 방식

방식 A: Python 스크립트 (권장)

봇 감지 우회가 필요한 출판사 사이트에서 사용합니다. undetected-chromedriver를 사용하여 Cloudflare, Bot Detection 등을 우회합니다.

적합한 경우:

  • ScienceDirect, Wiley, Springer 등 출판사 사이트
  • Cloudflare 보호가 있는 사이트
  • "Please verify you are human" 메시지가 나오는 사이트

사용법:

python ~/projects/skills/scripts/browser-capture-pdf.py \
  --doi "10.1016/j.xxx" \
  --output "/path/to/output.pdf" \
  --job-id "abc123" \
  --content-id "content-id" \
  --db-path "/path/to/dev.db"

MetaPaper 모드 (메타분석):

python ~/projects/skills/scripts/browser-capture-pdf.py \
  --doi "10.1016/j.xxx" \
  --output "/path/to/output.pdf" \
  --job-id "meta-paperId" \
  --content-id "paperId" \
  --db-path "/path/to/dev.db" \
  --table "MetaPaper" \
  --pdf-url-prefix "/uploads/meta-papers"

방식 B: Playwright MCP (간단한 페이지)

봇 감지가 없는 일반 웹페이지에서 사용합니다.

적합한 경우:

  • 정적 웹페이지
  • 내부 시스템/인트라넷
  • 봇 감지가 없는 사이트

Python 스크립트 상세 (권장)

주요 기능

기능설명
봇 감지 우회undetected-chromedriver 사용
Chrome 버전 자동 감지시스템 Chrome 버전에 맞춰 자동 설정
브라우저 격리기존 Chrome과 독립된 임시 프로필로 실행
동시성 제어파일 락으로 동시 실행 방지
쿠키 배너 자동 처리14개 이상의 패턴 자동 클릭/숨김
콘텐츠 로딩 대기15개 셀렉터로 동적 확인 (최대 90초)
창 닫힘 감지10초 연속 접근 실패 시 조기 종료
데드락 방지PID 확인 + 타임스탬프 기반 자동 해제
좀비 프로세스 정리이전 실행 잔여 chromedriver 자동 종료
SSL 우회macOS Python 3.14 + LibreSSL 호환성 처리
MetaPaper 지원--table MetaPaper로 메타분석 논문 지원

브라우저 격리 (v2026-02)

기존 Chrome이 실행 중일 때 프로필 충돌을 방지합니다:

# 매 실행마다 고유한 임시 프로필 디렉토리 생성
user_data_dir = tempfile.mkdtemp(prefix='uc-browser-capture-')
driver = uc.Chrome(
    options=options,
    use_subprocess=True,
    version_main=chrome_version,
    user_data_dir=user_data_dir  # 기존 Chrome과 완전 격리
)
# 완료 후 임시 디렉토리 자동 삭제

해결하는 문제:

  • 기존 Chrome과 같은 user data directory 사용으로 인한 충돌
  • 새 Chrome 인스턴스가 기존 Chrome에 붙어서 시작되는 현상
  • Chrome 프로필 잠금(lock) 충돌

SSL 인증서 검증 우회 (v2026-02)

macOS Python 3.14 + LibreSSL 2.8.3 환경에서 SSL 오류를 우회합니다:

# undetected-chromedriver가 Chrome 릴리즈 번호 fetch 시 urllib.urlopen 사용
# → SSL: CERTIFICATE_VERIFY_FAILED 발생
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

창 닫힘 조기 감지 (v2026-02)

일부 사이트(예: AJR)가 봇 감지로 Chrome 창을 강제로 닫는 경우:

consecutive_window_errors = 0
MAX_WINDOW_ERRORS = 10  # 10초 연속 창 접근 실패 시 조기 종료
# 90초 대기 대신 10초 만에 포기 → 리소스 절약

좀비 프로세스 정리 (v2026-02)

def kill_stale_chromedriver():
    """이전 실행에서 남은 chromedriver 프로세스 종료"""
    # pgrep -f 'chromedriver.*--enable-chrome-logs'

def cleanup_old_temp_dirs():
    """1시간 이상 된 uc-browser-capture-* 임시 디렉토리 삭제"""

동시성 제어

락 파일: /tmp/browser-capture-pdf.lock
정보 파일: /tmp/browser-capture-pdf.lock.info

최대 대기 시간: 5분 (300초)
락 유효 시간: 10분 (600초) - 초과 시 강제 해제
데드락 방지: 프로세스 생존 여부(PID) + 타임스탬프 확인

3단계 전략으로 쿠키/동의 배너를 자동 처리합니다:

1단계: 사이트별 전용 셀렉터 (25+)

플랫폼셀렉터
OneTrust (ScienceDirect, Wiley, Nature, Springer, IEEE)#onetrust-accept-btn-handler
Cookiebot#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll
Osano.osano-cm-accept-all
Quantcast/TCF.qc-cmp2-summary-buttons button[mode="primary"]
Didomi#didomi-notice-agree-button
TrustArc#truste-consent-button
Taylor & Francis#cookie-accept-all-btn
Oxford Academic#cookie-policy-accept
bioRxiv/medRxiv.hw-cookie-btn-accept
Frontiers#cookies-accept
PLOS#consent-accept
일반 패턴.cc-accept, .cc-allow, [data-action="accept-cookies"] 등

2단계: 다국어 버튼 텍스트 매칭 (100+)

  • English: Accept, Accept all, Allow all, I agree, OK, Got it, Consent, Continue...
  • Korean: 동의, 모두 동의, 모두 허용, 쿠키 허용, 수락, 확인, 닫기...
  • Japanese: 同意する, すべて許可, 許可...
  • Chinese: 同意, 全部接受, 接受, 确定, 好的...
  • German: Alle akzeptieren, Zustimmen...
  • French: Tout accepter, J'accepte...
  • Spanish: Aceptar todo, De acuerdo...
  • Italian: Accetta tutto, Accetto...
  • Portuguese: Aceitar tudo, Concordo...

3단계: 강제 DOM 제거 (클릭 실패 시)

  • 쿠키/동의/GDPR/개인정보 관련 배너 선택 + 제거
  • 고정 위치(fixed/sticky) 배너 중 쿠키 관련 텍스트 포함된 것 제거
  • 오버레이/backdrop 제거 + body overflow 복원

콘텐츠 로딩 감지 셀렉터 (15개+)

#abstracts, .abstract, [id*="abstract"], [class*="abstract"]
.article-content, .article-body, .article__body
article, .JournalAbstract, .ArticleAbstract
.content-box, .paper-content, .fulltext
main article, main .content, [role="main"]
.doi-link, .citation-info, .article-header
h1.article-title, .author-info, .authors-list

PDF 생성 방식

Chrome DevTools Protocol (CDP) 사용:

pdf_params = {
    'landscape': False,
    'displayHeaderFooter': False,
    'printBackground': True,
    'preferCSSPageSize': False,
    'paperWidth': 8.27,   # A4
    'paperHeight': 11.69, # A4
    'marginTop': 0.4,
    'marginBottom': 0.4,
    'marginLeft': 0.4,
    'marginRight': 0.4,
}
result = driver.execute_cdp_cmd('Page.printToPDF', pdf_params)

Playwright MCP 방식 (간단한 페이지용)

실행 단계

$ARGUMENTS 값을 사용하여 다음 단계를 수행합니다:

1. URL 결정

  • DOI 형식(예: 10.1016/j.xxx)이면 https://doi.org/ 접두사 추가
  • 이미 URL이면 그대로 사용

2. 브라우저로 페이지 접속

mcp__plugin_playwright_playwright__browser_navigate로 URL 접속

3. 페이지 로딩 대기

mcp__plugin_playwright_playwright__browser_wait_for로 2-3초 대기

4. 콘텐츠 확장 (Show more, Affiliations 등)

페이지 스냅샷을 확인하고 다음 버튼들을 순서대로 클릭:

a) Show more 버튼 클릭 (있는 경우) b) Affiliations/Authors 확장 버튼 클릭 (있는 경우) c) References 제외: 참고문헌 목록을 펼치면 PDF가 너무 길어지므로 클릭하지 않음

5. PDF 생성

mcp__plugin_playwright_playwright__browser_run_code 사용:

async (page) => {
  const pdfPath = './cell/{DOI_파일명}.pdf';  // 프로젝트 루트 기준

  await page.pdf({
    path: pdfPath,
    format: 'A4',
    printBackground: true,
    margin: {
      top: '15mm',
      bottom: '15mm',
      left: '10mm',
      right: '10mm'
    }
  });

  return `PDF saved to: ${pdfPath}`;
}

6. 브라우저 종료

mcp__plugin_playwright_playwright__browser_close로 브라우저 닫기

파일명 규칙

DOI를 파일명으로 변환:

  • / → _
  • 기타 특수문자 유지
  • 예: 10.1016/j.phymed.2025.156973 → 10.1016_j.phymed.2025.156973_browser.pdf

출력 디렉토리

프로젝트 cell/ 디렉토리 (기본값)

또는 --output 인자로 지정


로깅

로그 파일 위치

모드로그 파일
Content (일반 논문)logs/browser-pdf-{jobId}.log
MetaPaper (메타분석)logs/meta-browser-pdf-{paperId}.log

로그 내용 예시

[UC Chrome] 락 획득 시도...
[Lock] 락 획득 성공 (PID: 25605)
[UC Chrome] DOI URL: https://doi.org/10.2214/AJR.14.13810
[UC Chrome] 격리된 프로필 디렉토리: /var/folders/.../uc-browser-capture-seya3ozg
[UC Chrome] 감지된 Chrome 버전: 145
[UC Chrome] 브라우저 시작 중...
[UC Chrome] 브라우저 안정화 대기 (2초)...
[UC Chrome] 페이지 이동 중...
[UC Chrome] 리다이렉트 대기 (3초)...
[UC Chrome] 콘텐츠 로딩 대기 중...
[UC Chrome] 대기 중... (1/90초) - 제목: Article Title...
[UC Chrome] ✅ 콘텐츠 로딩 완료 (1초) - 셀렉터: #abstracts
[UC Chrome] 추가 안정화 대기 (5초)...
[UC Chrome] 현재 URL: https://www.ajronline.org/doi/10.2214/AJR.14.13810
[UC Chrome] 쿠키 배너 없음 또는 이미 처리됨
[UC Chrome] 'Show more' 버튼 1개 클릭 (JS)
[UC Chrome] 팝업/툴팁 5개 숨김
[UC Chrome] PDF 생성 중: /path/to/output.pdf
[UC Chrome] ✅ PDF 생성 완료 (355474 bytes)
[UC Chrome] 임시 프로필 디렉토리 정리 완료
[UC Chrome] MetaPaper 업데이트 완료: pdfFile=/uploads/meta-papers/filename.pdf
{"success": true, "source": "browser-capture", "fileSize": 355474, ...}
[Lock] 락 해제 완료

실패 시 로그 패턴

# 창 닫힘 (봇 감지/페이월)
[UC Chrome] 창 접근 오류 (1/90초, 연속 1회): no such window: target window already closed
[UC Chrome] ❌ 브라우저 창이 10초 이상 닫힌 상태 - 캡처 불가

# SSL 오류 (ssl 우회 미적용 시)
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]

# 모듈 미설치
ModuleNotFoundError: No module named 'undetected_chromedriver'

트러블슈팅

1. 기존 Chrome 실행 중 캡처 실패

원인: undetected-chromedriver가 기존 Chrome과 프로필 디렉토리를 공유하여 충돌 해결: v2026-02에서 user_data_dir 격리 적용 (자동). 수동 확인:

# 좀비 chromedriver 프로세스 정리
pkill -f 'chromedriver.*--enable-chrome-logs'

# 오래된 임시 프로필 정리
rm -rf /tmp/uc-browser-capture-*

2. SSL 인증서 검증 실패 (macOS Python 3.14)

원인: Python 3.14 + LibreSSL 2.8.3의 SSL 인증서 검증 비호환 해결: 스크립트에 ssl._create_default_https_context = ssl._create_unverified_context 포함됨 (자동)

# Python 버전 확인
python3 --version
python3 -c "import ssl; print(ssl.OPENSSL_VERSION)"

3. Chrome 세션 충돌 (Playwright MCP)

기존 Chrome이 실행 중이면 Playwright MCP 캐시 디렉토리 삭제:

rm -rf ~/Library/Caches/ms-playwright/mcp-chrome-*

4. undetected-chromedriver 설치/업데이트

pip install --break-system-packages undetected-chromedriver selenium

5. 락 파일이 남아있는 경우

비정상 종료로 락 파일이 남아있으면:

rm -f /tmp/browser-capture-pdf.lock /tmp/browser-capture-pdf.lock.info

6. 기관 인증

ScienceDirect 등은 기관 IP에서 접속해야 전문 접근 가능

7. PDF 크기가 너무 작은 경우 (< 50KB)

로딩 페이지만 캡처된 경우입니다. 다음을 확인하세요:

  • 봇 감지로 차단되었는지
  • 페이지 로딩이 완료되었는지
  • 기관 인증이 필요한지

8. 구독제 저널 (페이월)

일부 구독제 저널(AJR, Radiology 등)은 Chrome 창을 강제로 닫습니다. 이 경우 브라우저 캡처로는 PDF를 얻을 수 없으며, 기관 접근 또는 OA 버전이 필요합니다.


의존성

Python 스크립트

pip install --break-system-packages undetected-chromedriver selenium

Playwright MCP

Playwright MCP 서버가 Claude Code에 설정되어 있어야 합니다.


예시 실행 흐름 (Playwright MCP)

1. browser_navigate → https://doi.org/10.1016/j.phymed.2025.156973
2. browser_wait_for → 3초 대기
3. browser_snapshot → 페이지 구조 확인
4. browser_click → "Show more" 버튼 (ref=e155)
5. browser_wait_for → 1초 대기
6. browser_run_code → PDF 생성
7. browser_close → 브라우저 종료
8. ls -la → 파일 확인 (2.4MB)

관련 파일

파일경로설명
Python 스크립트~/projects/skills/scripts/browser-capture-pdf.py봇 우회 버전
Content 래퍼프로젝트 src/app/api/papers/[id]/browser-pdf/route.ts일반 논문 API
MetaPaper 래퍼프로젝트 src/app/api/meta-analysis/[id]/papers/[paperId]/browser-pdf/route.ts메타분석 API
Skill 정의~/projects/skills/skills/browser-pdf-capture/SKILL.md이 문서

업데이트 이력

  • 2026-03-06: 쿠키 동의 처리 대폭 강화 (_handle_cookie_consent): 3단계 전략(사이트별 셀렉터 25+, 다국어 텍스트 매칭 100+, 강제 DOM 제거), SSL 우회를 import 전으로 이동
  • 2026-02-13: 브라우저 격리(user_data_dir), SSL 우회, 창 닫힘 조기 감지, 좀비 정리, 로깅 문서화
  • 2025-02-03: Python 스크립트 기반 봇 우회 기능 문서화
  • 2025-01: 초기 Playwright MCP 버전