크롤링 본문에 섞인 네비게이션·푸터 노이즈 제거
목차
크롤러 본문에서 네비게이션 / 헤더 / 푸터 덩어리가 그대로 살아남아 있다는 걸 뒤늦게 발견했다.
배경: 왜 nav/header/footer가 문제였나
K-STARTUP이나 BIZINFO 같은 공공 계열 웹사이트는 구조가 꽤 전형적이다. 메인 콘텐츠 영역 밖에 전역 헤더, GNB(Global Navigation Bar), 사이드 메뉴, 푸터 고지문이 큼직하게 자리 잡는다. 문제는 이 요소들이 HTML 상에서 <main> 이나 <article> 같은 시맨틱 태그 안에 깔끔하게 격리되어 있지 않고, <div> 범벅 속에 콘텐츠와 뒤섞여 있는 경우가 많다는 점이다.
단순히 BeautifulSoup으로 텍스트를 긁어오면 "사업공고 상세내용"과 함께 "사이트맵", "개인정보처리방침 안내", "Copyright ©..." 같은 문자열이 통째로 딸려온다. 이 상태로 저장된 본문은 이후 텍스트 인덱싱, 요약 생성, 키워드 추출 어느 단계에서도 노이즈로 작동한다. 팀에서 확인해보니 일부 레코드는 본문의 30~40%가 nav/footer 텍스트였다.
작업 내용
이번에 손댄 파일은 네 개다.
| 파일 | 역할 | 이번 변경 |
|---|---|---|
bizinfo_crawler.py |
BIZINFO 사이트 크롤링 메인 로직 | 해당 사이트 전용 nav/header/footer 셀렉터 제거 로직 추가 |
kstartup_crawler.py |
K-STARTUP 사이트 크롤링 메인 로직 | 동일. 사이트별 마크업 차이 반영 |
crawler_common.py |
크롤러 공통 유틸 | 공통 제거 함수 분리/정의 |
migrate_content_absolutize.py |
기존 저장 콘텐츠 마이그레이션 | 이미 저장된 레코드 소급 정제 |
사이트마다 nav/footer의 CSS 클래스나 id가 달라서 공통 함수 하나로 전부 처리하기는 어려웠다. crawler_common.py에 공통 인터페이스를 만들고, 각 사이트 크롤러에서 제거 대상 셀렉터 목록을 넘기는 방식으로 정리했다.
# crawler_common.py
def strip_unwanted_elements(soup, selectors: list[str]) -> None:
"""
nav/header/footer 등 불필요한 DOM 요소를 in-place로 제거.
selectors: CSS selector 문자열 목록
"""
for selector in selectors:
for tag in soup.select(selector):
tag.decompose()
# kstartup_crawler.py (예시)
STRIP_SELECTORS = [
"#header",
"#gnb",
"#footer",
".site-map",
"#skipNav",
]
strip_unwanted_elements(soup, STRIP_SELECTORS)
body_text = soup.get_text(separator="\n", strip=True)
decompose()를 쓰는 게 포인트다. extract()는 제거된 태그 객체를 반환하면서 메모리에 남기지만, decompose()는 트리에서 완전히 파괴한다. 크롤러처럼 대량으로 반복 처리하는 환경에서는 이 차이가 누적 메모리에 영향을 준다.
migrate_content_absolutize.py는 기존에 저장된 본문 레코드를 소급 정제하는 스크립트다. 새 크롤링 로직을 붙인다고 기존 오염 데이터가 자동으로 정리되진 않는다. 마이그레이션 스크립트가 별도로 필요한 이유가 여기 있다. "앞으로 들어오는 데이터"만 고치면 된다고 생각하면 절반만 한 거다. 팀에 이 점을 공유하면서 마이그레이션 적용 순서와 롤백 조건도 같이 정리해뒀다.
회고
이런 류의 버그는 처음 크롤러를 붙일 때 "일단 텍스트 뽑히면 OK" 하고 넘어가는 순간 심어진다. QA 단계에서 본문 샘플을 육안으로 확인하면 보통 잡히는데, 크롤러 특성상 수백 개 URL을 자동으로 처리하다 보니 리뷰 커버리지가 낮아지기 쉽다.
코드리뷰에서 내가 자주 얘기하는 것 중 하나가 "크롤러는 파싱 결과 샘플을 반드시 로그로 남겨라"는 거다. 전체 본문을 다 찍을 필요는 없고, 첫 200자 정도만 남겨도 nav 텍스트가 섞여 들어오는지 바로 보인다. 이번 케이스도 결국 그 로그를 뒤지다가 발견했다.
- 크롤러 결과는 "뭔가 나온다"와 "올바르게 나온다"를 따로 검증해야 한다
- 사이트별로 마크업이 다르기 때문에 셀렉터 목록은 파일마다 명시적으로 관리하는 게 낫다
- 신규 데이터 픽스와 기존 데이터 마이그레이션은 항상 세트다
decompose()vsextract()— 반복 처리 환경에서는 전자가 기본값
데이터 파이프라인 초반 품질 관리가 얼마나 중요한지 다시 한 번 체감한 작업이었다. 오염된 본문이 하류 시스템(검색 인덱스, 요약 모델 등)까지 전파되기 전에 잡아낸 게 다행이다.
끝.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.