웹페이지를 방문하고, 콘텐츠를 확장한 후 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) + 타임스탬프 확인쿠키 배너 자동 처리 (_handle_cookie_consent)
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-listPDF 생성 방식
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 selenium5. 락 파일이 남아있는 경우
비정상 종료로 락 파일이 남아있으면:
rm -f /tmp/browser-capture-pdf.lock /tmp/browser-capture-pdf.lock.info6. 기관 인증
ScienceDirect 등은 기관 IP에서 접속해야 전문 접근 가능
7. PDF 크기가 너무 작은 경우 (< 50KB)
로딩 페이지만 캡처된 경우입니다. 다음을 확인하세요:
- 봇 감지로 차단되었는지
- 페이지 로딩이 완료되었는지
- 기관 인증이 필요한지
8. 구독제 저널 (페이월)
일부 구독제 저널(AJR, Radiology 등)은 Chrome 창을 강제로 닫습니다. 이 경우 브라우저 캡처로는 PDF를 얻을 수 없으며, 기관 접근 또는 OA 버전이 필요합니다.
의존성
Python 스크립트
pip install --break-system-packages undetected-chromedriver seleniumPlaywright 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 버전