“한 번 성공하는 코드”가 아니라 “여러 페이지를 안정적으로 수집하는 코드”를 만드는 핵심 단계
크롤링을 처음 배울 때는 보통 이런 흐름으로 시작합니다.
- 페이지 열기
- 요소 찾기
- 텍스트 추출하기
- 리스트에 저장하기
이 정도만 되면 일단 “크롤링이 된다”는 느낌을 받습니다.
하지만 실제 프로젝트나 실무 환경에서는 여기서부터가 진짜 시작입니다.
왜냐하면 실제 웹페이지는 항상 깔끔하게 동작하지 않기 때문입니다.
예를 들어 이런 일이 아주 흔합니다.
- 페이지가 생각보다 늦게 뜬다
- 버튼을 눌렀는데 다음 요소가 바로 안 생긴다
- 어떤 매물은 가격이 비어 있다
- 어떤 카드만 구조가 조금 다르다
- 몇 페이지는 정상인데 중간 페이지에서 갑자기 에러가 난다
- 사이트가 요청을 너무 많이 보내는 것으로 판단해 차단한다
- 크롤링 도중 인터넷이 잠깐 끊기거나 응답이 늦어진다
즉, 크롤링에서 중요한 것은
단순히 데이터를 뽑는 문법만이 아니라,
예상치 못한 상황에서도 전체 수집이 끝까지 돌아가게 만드는 안정성입니다.
이 파트에서는 바로 그 부분을 다룹니다.
- time.sleep()은 왜 필요한가
- Selenium의 implicit wait / explicit wait는 무엇이 다른가
- 왜 “무조건 기다리기”보다 “조건을 기다리기”가 좋은가
- try-except는 크롤링에서 왜 거의 필수인가
- 어떤 예외는 건너뛰고, 어떤 예외는 멈춰야 하는가
- 로그를 남기는 습관은 왜 중요한가
- 크롤링 매너는 왜 단순 예절이 아니라 실전 전략인가
- 서버 부하와 차단을 줄이려면 무엇을 고려해야 하는가
즉, 이 파트의 핵심은
크롤링을 성공시키는 것이 아니라, 크롤링을 안정적으로 지속시키는 방법을 배우는 것입니다.
1. 왜 “안정화”가 따로 필요한가?
많은 초보자는 이렇게 생각하기 쉽습니다.
- 내가 원하는 요소를 찾았으니 끝
- 코드가 한 번 돌아갔으니 성공
- 일단 결과가 나오니까 문제 없음
하지만 실제 크롤링은 “한 번 되는 것”과 “계속 잘 되는 것”이 다릅니다.
예를 들어 이런 코드는 종종 처음엔 잘 됩니다.
그런데 실제로는:
- 페이지 로딩이 조금만 늦어도 실패할 수 있고
- 구조가 조금만 바뀌어도 실패할 수 있고
- 특정 카드만 예외 구조면 실패할 수 있습니다
즉, 크롤링은 본질적으로 불안정한 환경에서 돌아가는 코드라고 생각하는 것이 맞습니다.
왜냐하면 크롤링 대상 사이트는:
- 내가 통제할 수 없는 외부 시스템이고
- 언제든 구조가 바뀔 수 있고
- 속도도 일정하지 않고
- 요청 제한 정책도 있을 수 있기 때문입니다
그래서 안정화는 선택이 아니라 필수입니다.
2. 크롤링에서 자주 발생하는 문제 유형
안정화 전략을 이해하려면 먼저 어떤 문제가 자주 발생하는지 알아야 합니다.
2-1. 로딩 타이밍 문제
예:
- 페이지는 열렸지만 원하는 요소는 아직 안 나타남
- 버튼 클릭 후 DOM이 늦게 갱신됨
- 스크롤 후 추가 데이터가 늦게 붙음
이건 Selenium에서 가장 흔한 문제 중 하나입니다.
2-2. 구조 불일치 문제
예:
- 대부분 카드에는 가격이 있는데 일부 카드만 없음
- 어떤 페이지는 배너가 끼어 있어서 구조가 다름
- 일부 항목은 옵션 정보가 빠져 있음
즉, 모든 데이터가 동일한 형태라고 가정하면 위험합니다.
2-3. 네트워크 / 응답 문제
예:
- 요청이 느림
- 타임아웃
- 일시적인 500 에러
- 연결 불안정
이건 requests 기반 크롤링에서 특히 자주 만납니다.
2-4. 차단 / 접근 제한 문제
예:
- 너무 빠른 요청
- 비정상적인 반복 접근
- 헤더가 너무 비어 있음
- 짧은 시간에 과도한 클릭/스크롤
이런 경우 사이트가 봇처럼 판단할 수 있습니다.
2-5. 코드 자체 문제
예:
- 잘못된 selector
- 리스트 길이 불일치
- None 처리 누락
- 반복 중간에 예외 발생
즉, 안정화는 웹사이트 문제뿐 아니라
내 코드가 예외 상황을 제대로 처리하느냐와도 깊게 연결됩니다.
3. time.sleep()은 무엇인가?
3-1. 정의
time.sleep()은
파이썬 코드 실행을 일정 시간 멈추는 함수입니다.
예:
time.sleep(2)
이 코드는 2초 동안 프로그램을 멈춥니다.
3-2. 왜 크롤링에서 자주 쓰일까?
크롤링에서는 너무 빠르게 다음 단계로 넘어가면 문제가 생길 수 있습니다.
예:
- 페이지 이동 직후 아직 요소가 안 뜸
- 버튼 클릭 후 결과 목록이 안 바뀜
- 스크롤 직후 추가 로딩이 덜 끝남
이때 잠깐 기다리는 용도로 자주 씁니다.
3-3. 가장 쉬운 대기 방법
time.sleep()의 장점은 단순하다는 것입니다.
예:
time.sleep(3)
이렇게 하면 일단 3초 기다리므로
페이지가 어느 정도 뜨는 시간을 벌 수 있습니다.
즉, 초반 실습에서는 가장 이해하기 쉬운 대기 방식입니다.
4. time.sleep()의 한계
4-1. 너무 많이 기다릴 수 있다
페이지가 0.5초 만에 로딩돼도 무조건 3초 기다립니다.
즉, 전체 크롤링 속도가 느려집니다.
4-2. 너무 적게 기다릴 수 있다
반대로 어떤 페이지는 5초 걸리는데 2초만 기다리면 실패합니다.
즉, 고정된 시간 대기는 환경 변화에 약합니다.
4-3. 왜 실무에서는 불완전한가?
실제 웹페이지 속도는 항상 일정하지 않습니다.
그래서 sleep(2)가 오늘은 되고 내일은 안 될 수 있습니다.
즉, time.sleep()은 단순하고 편하지만
정확히 무엇을 기다리는지 모른 채 그냥 시간을 버는 방식입니다.
그래서 Selenium에서는 더 나은 대기 방식이 등장합니다.
5. Selenium의 대기(wait)란 무엇인가?
Selenium에서는 단순 시간 지연보다
“특정 조건이 만족될 때까지 기다리는 방식” 이 중요합니다.
이걸 보통 wait라고 부릅니다.
크게 보면 다음 두 개가 자주 언급됩니다.
- Implicit Wait
- Explicit Wait
6. Implicit Wait란 무엇인가?
6-1. 정의
Implicit Wait는
Selenium이 요소를 찾을 때, 바로 없다고 실패하지 말고
일정 시간 동안 기다려 보라는 설정입니다.
예:
이렇게 하면 Selenium이 요소를 찾을 때 최대 5초 정도 기다릴 수 있습니다.
6-2. 어떻게 이해하면 좋을까?
이건 브라우저에게 이렇게 말하는 것과 비슷합니다.
“요소를 바로 못 찾더라도, 조금 기다렸다가 다시 찾아봐.”
즉, 기본적인 완충 장치입니다.
6-3. 장점
- 설정이 간단하다
- 코드가 짧다
- 초보자에게 이해가 쉽다
6-4. 한계
- “무슨 조건을 기다리는지” 세밀하게 지정할 수 없다
- 모든 탐색에 공통 적용되므로 비효율적일 수 있다
- 복잡한 동적 페이지에서는 부족할 수 있다
즉, Implicit Wait는 기본 안전장치 정도로 이해하면 좋고,
실전에서는 Explicit Wait가 더 강력합니다.
7. Explicit Wait란 무엇인가?
7-1. 정의
Explicit Wait는
특정 조건이 만족될 때까지 기다리는 방식입니다.
예:
- 요소가 나타날 때까지 기다리기
- 버튼이 클릭 가능할 때까지 기다리기
- 특정 텍스트가 보일 때까지 기다리기
즉, 단순히 “몇 초 쉬어라”가 아니라
“원하는 상태가 될 때까지 기다려라” 입니다.
7-2. 왜 더 좋은가?
이 방식은:
- 너무 일찍 넘어가는 문제를 줄이고
- 필요 이상 오래 기다리지 않고
- 실제 동작 조건과 연결되기 때문에
더 안정적이고 효율적입니다.
즉, 실전 Selenium에서는 Explicit Wait가 매우 중요합니다.
8. Explicit Wait 기본 형태
예:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "price"))
)
이 코드는 다음 의미를 가집니다.
- 최대 10초 동안 기다리되
- class="price" 요소가 나타나면
- 그 즉시 반환하라
즉, “시간”이 아니라 “조건”을 기다리는 방식입니다.
9. 자주 쓰는 Explicit Wait 조건들
9-1. presence_of_element_located
요소가 DOM에 존재할 때까지 기다립니다.
예:
주의할 점은 “존재”만 보는 것이지,
반드시 화면에 보여야 한다는 뜻은 아닐 수 있습니다.
9-2. visibility_of_element_located
요소가 실제로 보일 때까지 기다립니다.
예:
이건 사용자 화면에서 볼 수 있는 상태를 기다릴 때 유용합니다.
9-3. element_to_be_clickable
요소가 클릭 가능한 상태가 될 때까지 기다립니다.
예:
버튼 클릭 전에 아주 자주 씁니다.
9-4. presence_of_all_elements_located
여러 요소가 나타날 때까지 기다립니다.
예:
목록형 구조에서 자주 유용합니다.
10. 언제 sleep을 쓰고, 언제 wait를 쓸까?
이건 실전에서 매우 중요합니다.
time.sleep()
- 간단한 테스트용
- 아주 짧은 완충용
- 구조 파악 초반 임시 확인용
Explicit Wait
- 요소 등장을 기다릴 때
- 클릭 가능 상태를 기다릴 때
- 동적 로딩 대응할 때
- 반복 수집 코드 안정화할 때
즉, 처음 실습에서는 sleep이 쉬울 수 있지만,
실전 안정화 코드로 갈수록 Explicit Wait 비중이 높아지는 것이 자연스럽습니다.
11. try-except는 왜 크롤링에서 거의 필수인가?
크롤링은 외부 환경을 다루기 때문에
예외가 정말 자주 발생합니다.
예:
- 요소를 못 찾음
- 텍스트 변환 실패
- 링크 누락
- 특정 카드 구조 이상
- 페이지 전환 실패
이때 try-except가 없으면
중간에 한 번 에러가 나서 전체 수집이 멈출 수 있습니다.
즉, try-except는 단순 문법이 아니라
전체 크롤링을 끝까지 살아남게 하는 안전장치입니다.
12. 어떤 부분에 try-except를 둘까?
12-1. 카드 하나 처리 단위
가장 흔한 패턴은
반복문 안에서 각 아이템 하나를 처리할 때 예외를 잡는 것입니다.
예:
try:
brand = item.find_element(By.CSS_SELECTOR, ".brand").text
price = item.find_element(By.CSS_SELECTOR, ".price").text
cars.append({"brand": brand, "price": price})
except Exception as e:
print("매물 하나 처리 실패:", e)
continue
이렇게 하면 한 카드가 실패해도
다음 카드로 넘어갈 수 있습니다.
12-2. 페이지 단위
페이지 번호를 넘기며 수집할 때는
페이지 단위로 예외를 잡을 수도 있습니다.
예:
- 1페이지는 성공
- 2페이지는 실패
- 3페이지는 계속 진행
이런 흐름이 가능해집니다.
12-3. 네트워크 요청 단위
requests 기반이라면
요청 자체를 try-except로 감쌀 수 있습니다.
즉, 예외 범위를 어디까지 둘지는
“어디까지 실패해도 전체를 계속 돌릴 것인가”와 연결됩니다.
13. 모든 예외를 무조건 무시하면 안 되는 이유
초보자는 종종 이렇게 쓰기도 합니다.
...
except:
pass
이건 매우 위험할 수 있습니다.
왜냐하면:
- 무슨 오류가 났는지 전혀 모르고
- 데이터가 빠져도 눈치채기 어렵고
- 구조가 깨졌는데 계속 진행해버릴 수 있기 때문입니다
즉, 예외를 잡더라도
최소한 무엇이 실패했는지는 알 수 있게 해야 합니다.
예:
...
except Exception as e:
print("오류 발생:", e)
이렇게라도 남겨야 디버깅이 가능합니다.
14. continue와 break는 어떻게 다르게 쓸까?
예외 처리와 함께 자주 나오는 개념입니다.
continue
이번 반복만 건너뛰고 다음으로 넘어갑니다.
예:
try:
...
except Exception as e:
print("건너뜀:", e)
continue
즉, 하나 실패해도 전체는 계속 갑니다.
break
반복 자체를 종료합니다.
예:
try:
...
except Exception as e:
print("치명적 오류:", e)
break
즉, 더 진행하면 의미 없다고 판단할 때 멈춥니다.
어떻게 판단하면 좋을까?
- 카드 하나 누락 → continue가 자연스러울 수 있음
- 페이지 전체 구조가 무너짐 → break가 더 적절할 수 있음
- 로그인 만료 / 차단 감지 → 멈추는 것이 나을 수 있음
즉, 예외를 만났을 때
무조건 계속 갈지, 멈출지의 판단도 중요합니다.
15. 로깅(logging) 감각은 왜 중요한가?
크롤링이 길어질수록
단순 print()만으로는 흐름을 파악하기 어려울 수 있습니다.
예를 들어 이런 정보는 남겨두는 것이 좋습니다.
- 몇 페이지째 처리 중인지
- 몇 개 매물을 수집했는지
- 어떤 URL에서 실패했는지
- 어떤 항목이 누락됐는지
- 언제 시작했고 언제 끝났는지
예:
print(f"현재까지 수집 개수: {len(cars)}")
print(f"실패 URL: {url}")
이런 로그는 디버깅뿐 아니라
실행 상태를 추적하는 데도 매우 중요합니다.
즉, 안정적인 크롤링은
단순히 결과만 저장하는 것이 아니라
과정도 추적 가능해야 합니다.
16. 재시도(retry) 개념은 왜 중요할까?
실패가 항상 영구적인 것은 아닙니다.
어떤 실패는 잠깐 네트워크가 느렸거나, 로딩이 늦어서 생길 수 있습니다.
즉:
- 한 번 실패했지만
- 다시 시도하면 될 수도 있습니다
이런 상황에서 재시도 개념이 중요합니다.
예를 들어:
- 요청 타임아웃
- 일시적 응답 실패
- 버튼 클릭 직전 로딩 지연
은 다시 시도해서 해결될 수 있습니다.
처음 단계에서는 복잡한 retry 라이브러리까지 몰라도 괜찮지만,
“모든 실패가 영구 실패는 아니다” 라는 감각은 꼭 필요합니다.
17. 크롤링 매너란 무엇인가?
이건 단순 예절 문제가 아닙니다.
실전 전략과도 깊게 연결됩니다.
17-1. 의미
크롤링 매너는
대상 사이트에 과도한 부담을 주지 않고,
불필요한 차단이나 문제를 일으키지 않도록 조심하는 태도와 방법을 뜻합니다.
17-2. 왜 중요한가?
웹사이트는 사람이 보는 서비스를 위해 운영됩니다.
내 크롤러가 너무 공격적으로 요청을 보내면:
- 서버에 부담을 주고
- 서비스에 악영향을 줄 수 있고
- 차단되거나 법적/운영적 문제가 생길 수 있습니다
즉, 크롤링 매너는 단순한 예절이 아니라
지속 가능한 수집을 위한 기본 조건입니다.
18. 서버 부하를 줄이는 기본 방법
18-1. 요청 사이 간격 두기
너무 빠르게 연속 요청하지 않도록 간격을 둡니다.
예:
time.sleep(1)
18-2. 불필요한 요청 줄이기
같은 페이지를 계속 새로 열지 않기
이미 수집한 데이터는 다시 요청하지 않기
필요한 페이지만 요청하기
18-3. 전체 규모를 조절하기
처음부터 수만 건을 긁지 말고:
- 작은 범위로 테스트
- 구조 확인 후 점진적 확장
이 더 안전합니다.
19. 너무 규칙적인 요청도 문제일 수 있다
사이트는 봇 패턴을 감지할 수 있습니다.
예:
- 정확히 0.1초마다 요청
- 동일한 패턴 반복
- 너무 일정한 클릭 흐름
- 너무 빠른 페이지 넘김
즉, 사람처럼 보이지 않는 패턴은 차단 위험이 커질 수 있습니다.
그래서 실제로는
너무 기계적인 속도를 피하고,
적절한 간격과 완충을 두는 것이 중요합니다.
20. 헤드리스(headless) 실행은 무엇인가?
Selenium에서는 브라우저 창을 실제로 띄우지 않고
백그라운드처럼 실행하는 방식을 headless 라고 합니다.
예:
- 화면 없이 브라우저 자동 실행
- 서버 환경에서 사용
- 배포 환경에서 자주 사용
이건 매우 유용하지만,
일부 사이트에서는 일반 브라우저보다 더 쉽게 탐지하거나 다르게 반응할 수도 있습니다.
즉, headless는 성능과 편의성에 장점이 있지만
항상 만능은 아니라는 정도는 알아두면 좋습니다.
21. 크롤링 안정화에서 “요소가 없을 수 있음”을 항상 생각해야 한다
실전 크롤링에서 가장 중요한 태도 중 하나입니다.
예:
- 가격 없음
- 주행거리 없음
- 링크 없음
- 이미지 없음
- 상세 스펙 없음
이건 이상한 일이 아니라 흔한 일입니다.
그래서 코드는 보통 이렇게 생각해야 합니다.
- “이 값이 반드시 있다”가 아니라
- “없을 수도 있다”
즉, 안정적인 크롤링은
완벽한 데이터만 상정하지 않는 코드에서 시작합니다.
22. requests 기반 크롤링에서도 안정화는 중요하다
안정화는 Selenium 전용 개념이 아닙니다.
requests를 쓸 때도 마찬가지입니다.
예:
- 상태 코드 확인
- timeout 설정
- 응답 없을 때 재시도
- 인코딩 문제 확인
- 너무 빠른 요청 방지
즉, 안정화는
웹 수집 전반의 공통 주제입니다.
23. 범용적으로 꼭 익혀야 하는 안정화 사고방식
23-1. 외부 시스템은 항상 불안정할 수 있다
대상 사이트는 내 코드처럼 내가 통제하는 시스템이 아닙니다.
따라서:
- 느릴 수도 있고
- 구조가 바뀔 수도 있고
- 일시적으로 실패할 수도 있습니다
이걸 기본 전제로 깔아야 합니다.
23-2. 예외는 특별한 일이 아니라 기본 시나리오다
크롤링에서는 예외가 생기는 것이 자연스럽습니다.
중요한 건 “예외가 없게 만들기”보다
예외가 생겨도 전체 수집이 망가지지 않게 하는 것입니다.
23-3. 기다림은 시간보다 조건 중심이 좋다
무조건 sleep(5)보다
“이 요소가 나올 때까지” 기다리는 방식이 더 안정적이고 효율적입니다.
23-4. 과정이 보여야 나중에 고칠 수 있다
로그를 남기지 않으면:
- 어디서 실패했는지
- 얼마나 수집됐는지
- 어느 페이지부터 꼬였는지
알 수 없습니다.
즉, 안정화는 디버깅 가능성과도 연결됩니다.
23-5. 크롤링 매너는 결국 내 코드도 보호한다
너무 공격적으로 수집하면 사이트뿐 아니라
내 수집 코드도 차단당하거나 실패하게 됩니다.
즉, 매너는 윤리 문제이기도 하지만
동시에 성공률을 높이는 전략이기도 합니다.
'AI > Python' 카테고리의 다른 글
| Selenium 기초: 동적 페이지 제어 (0) | 2025.05.30 |
|---|---|
| 웹 크롤링 기초: requests + BeautifulSoup 관점 (0) | 2025.05.29 |
| 웹의 구조 이해: HTML / CSS / DOM (0) | 2025.05.28 |
| 데이터 저장 관점과 CSV / JSON / Excel 구조 이해 (0) | 2025.05.26 |
| 파이썬 파일 입출력과 모듈 구조 (0) | 2025.05.21 |