언제 쓰나
검색식 하나로 여러 DB에서 논문 코퍼스를 만들 때. DB마다 제약이 달라서 한 방식으로 셋을 다 긁을 수 없다.
정본 코드
~/Documents/혜연/BTC_KOL_PoC/
collect_pubmed.py 194줄 esearch → efetch 배치
collect_openalex.py 198줄 OR 필터 배치 + 캐시
collect_koreamed.py 426줄 API 없음 — 폼 재전송으로 긁는다
topics/ 검색식 정의
out/ 수집 결과 JSONDB별 제약 — 여기가 전부다
| DB | 인증 | 한도 | 페이지네이션 | 응답 |
|---|---|---|---|---|
| PubMed E-utilities | 키 선택 | 무키 3 req/s | retmax 최대 10000, efetch는 100건씩 | XML |
| OpenAlex | 키 없음 | mailto= 넣으면 10/s · 10만/일 | OR 필터 한 번에 50개 | JSON |
| KoreaMed | 없음 | 명시 없음 | 아래 함정 참조 | XML(요청해야) |
PubMed — 2단계
esearch로 PMID 목록을 받고(retmax: 10000), efetch로 100건씩 상세를 받는다.
def efetch(pmids, batch=100, delay=1.0):
...
time.sleep(delay) # NCBI 부하 배려. 무키 제한은 3req/s 이지만 넉넉히 둔다한도보다 여유 있게 잡는다. 3 req/s가 상한이라고 1초에 3번 때리면 간헐적으로 막힌다. 재시도는 time.sleep(3 * (i + 1))로 점점 늘린다.
OpenAlex — 키가 아예 없다
API 키는 존재하지 않는다. mailto= 만 붙이면 polite pool(초당 10건 / 일 10만건).
mailto는 인증이 아니라 예의다. 안 넣으면 공용 풀에서 훨씬 느리다.
BATCH = 50 — OpenAlex OR 필터 상한이다. 그 이상 넣으면 조용히 잘린다. select= 로 필요한 필드만 받는다(ids,doi,authorships,cited_by_count,publication_year). 전체를 받으면 응답이 수십 배 커진다.
결과는 openalex_cache.json에 쌓는다. 병합 단계를 여러 번 다시 돌리게 되므로 캐시가 없으면 매번 다시 긁는다.
KoreaMed — 공식 API가 없다
이 파일 426줄의 대부분이 여기서 나온 함정 대응이다.
s_display_type=xml → PubMed 스타일 XML (저자별 소속 포함). 안 주면 HTML 파싱해야 한다
s_num_per_page → 최대 500가장 큰 함정 — 2페이지 이후:
2페이지 이후는search_type=page_search+ 결과페이지의sub_search_frm폼 전체 재전송이라야 나온다.sub_search로s_direct_page만 바꾸면 조용히 1페이지가 다시 온다 (실측 중복 500/500).
에러가 아니라 같은 데이터가 성공처럼 돌아온다. 중복 제거를 안 하면 500건을 N번 받고도 모른다. 그래서 form_fields(body)로 결과 페이지의 input/select를 전부 긁어 되보낸다.
검색식이 길면 나눈다. QUERY_BUDGET = 900자 어림으로 최상위 OR 기준 분할(split_top_level_or), 실패하면 자동 이분할. 주석에 "정확할 필요 없다"고 적힌 이유다 — 실패가 분할을 유도한다.
공통 규칙
- 검색식을 코드에 박지 마라.
topics/에 두고load_topic(topic_id)로 읽는다. 검색식은 계속 바뀌고, 어떤 식으로 뽑았는지가 논문에 들어간다. - 재시도는 지수 백오프(
3 * (i+1)). 4~5회. - 수집 결과는 원본 그대로
out/에 남긴다. 가공은 병합 단계에서.
연관 skill
paper-merge-provenance(다음 단계), biblio-normalize, lit-relevance-classify