행인로 개인 작업실

수집 0건은 한산한 하루와 똑같이 생겼다

돌려두는 가격 수집기가 어느 날부터 아무것도 가져오지 않았다. 죽은 게 아니라 수집을 못 했다. 서비스는 전부 떠 있었고, 스케줄러는 제시간에 돌았고, 데이터베이스는 쓰기를 받고 있었고, 연속 실패 카운터는 0이었다. 잘못된 것은 단 하나, 두 워커 모두 전날 오전 11시 31분 이후 모든 회차에서 0행을 반환했다는 것 — 대략 685회 연속이었다.

0은 합법적인 답이다. 어떤 분류에 오늘 새로 올라온 게 정말 없을 수도 있다. 그래서 수집기는 “결과 0건”을 경고로만 보고했고, 경고는 아무도 깨우지 않았다. 오류도 없고 데이터도 없는 시스템은, 밖에서 보면 한산한 일주일과 정확히 똑같이 생겼다.

사이트는 나를 막은 게 아니라 이사를 갔다

대상 사이트가 목록 페이지를 다른 서브도메인의 새 프런트엔드로 통째로 교체하면서, 옛 주소를 새 주소로 넘기고 있었다. 차단도, 캡차도, 요청 제한도 없었다. 내 수집기는 넘김을 따라가 완벽하게 정상인 페이지를 받아왔고, 자기가 아는 선택자(상품 카드·상세 링크·페이지 넘김)가 하나도 맞지 않는다는 것을 발견했다. 파싱된 카드 0개, 발생한 오류 0건, 기록된 행 0건, 그리고 초록색 대시보드.

여기서 고칠 것이 둘 나왔고, 그중 이 사이트에 관한 건 하나뿐이다. 좁은 쪽은 새 파서다. 넓은 쪽은 0이 일정 횟수 이상 이어지면 오류로 승격시킨 것이다. 데이터가 없다는 사실도 데이터다.

다음 사이트를 싸게 만들기

선택자만 그 자리에서 기워 넣는 대신, 전체를 재사용 가능한 층으로 끌어냈다 — 가져오기, 어댑터, 정규화, 품질 점검, 검증, 중복 제거, 저장. 사이트마다 다른 지식은 이제 어댑터 파일 하나와 저장해둔 페이지 표본 몇 개에만 산다. 다음 사이트는 또 하나의 맞춤 수집기가 아니라, 어댑터 하나와 표본 몇 개가 된다.

살아 있는 사이트 대신 저장해둔 페이지로 시험한 것이, 이 세션의 나머지를 가능하게 했다. 같은 상품 카드 150장을 서로 다른 HTML 파서 셋에 통과시킬 수 있었고, 옛 파이프라인과 새 파이프라인을 동일한 입력으로 견줄 수 있었고, 이번 재작성과 아무 상관 없는 차이까지 볼 수 있었다.

그 비교가 끄집어낸 것

150장 중 30장이 새 규칙에서 다른 가격으로 잡혔다. 전부 한 분류에 몰려 있었고, 전부 옛 코드가 카드사 할인 가격을 그 상품의 가격으로 조용히 집어 오던 경우였다 — 보통 진짜 가격보다 5~10% 낮게. 몇 달째 운영 중이던 데이터 품질 버그이고, 아무것도 실패한 적이 없어서 보이지 않았다.

16개 항목은 데이터베이스의 무엇과도 매치되지 않았는데, 이건 수집 문제가 아니라 스키마 문제였다. 상품 테이블이 행을 이름으로 식별하고 안정적인 ID 칼럼이 없어서, 판매자가 상품명을 바꾸면 새 행이 생기고 나중에 그게 같은 물건임을 증명할 방법이 없다. 15개는 진짜 신규였고, 1개는 읽어봐야만 확인되는 개명이었다.

마음에 든 세부가 하나 더 있다. 재작성 후 실제 데이터베이스를 상대로 쓰기 없이 한 번 돌렸더니, 그 분류에서 이미 알고 있던 54개 항목을 전부 다시 찾아냈고 잘못된 레코드는 0건이었다. 재작성을 믿을 수 있었던 이유는, 그것을 자기 자신이 아닌 무언가에 대조할 방법이 있었기 때문이다.

남길 부분

수집기가 깨진 것은 흥미로운 사건이 아니다. 사이트가 개편되면 수집기는 깨지고, 그건 막을 수 있는 버그가 아니다. 흥미로운 사건은 그것이 내 감시가 무시하도록 만들어진 모양으로 깨졌다는 것이다. 내가 지켜보던 것은 전부 정상 동작했다. 내가 지켜보지 않던 것은 685회 동안 변하지 않은 숫자 하나였다.