Python Selenium 네이버 이미지 크롤링 — Selenium 4 변경점 정리
2020년 4월에 올렸던 네이버 이미지 크롤링 코드를 6년 만에 다시 펼쳐봤습니다. 그때는 코드 한 덩어리만 던지고 끝낸 글이었습니다. “크롤링을 할때는 항상 주의해주시구요”라는 한 줄이 주의사항의 전부였고요.
이 글은 두 가지를 함께 담습니다. 2020년 당시 코드는 그대로 보존하고, 지금 같은 작업을 다시 한다면 무엇이 달라지는지를 회고 형식으로 덧붙였습니다. 팩트(2020년 원본)와 분석(2026년 시점)은 섹션으로 분리해서 구별할 수 있도록 했습니다.
1) 그때 그 코드 — Selenium으로 네이버 이미지를 긁어오다
학습 동기는 단순했습니다. “어느 사이트든 HTML 구조만 잘 분석하면 크롤링은 가능하다”는 걸 직접 손으로 확인해보고 싶었습니다. 당시 참고한 건 유튜브 강의 한 편이었습니다(출처: https://www.youtube.com/watch?v=HJN28B7OSzw).
코드의 핵심 흐름을 짧게 정리하면 이렇습니다:
webdriver.Chrome('./chromedriver')로 Chrome 브라우저 드라이버를 기동한다.- 네이버 이미지 검색 URL(
https://search.naver.com/search.naver?where=image&sm=tab_jum&query={keyword})에 접속한다. body엘리먼트에Keys.PAGE_DOWN을 3회 입력해 lazy load를 트리거한다(스크롤하지 않으면 화면 밖 이미지는 아직 로드되지 않은 상태이기 때문에). 매 스크롤마다 1~3초 랜덤 슬립을 넣었다.img._imgCSS 셀렉터로 이미지 태그를 수집하고,src속성에'http'가 포함된 것만 추린다. 수집·저장 루프에는tqdm(터미널에 진행률을 표시해주는 라이브러리)을 써서 진행 상황을 눈으로 볼 수 있게 했다.images/{keyword}/폴더를 생성한 뒤urlretrieve로 한 장씩 저장한다.
지금 다시 보면 슬립을 1~3초 랜덤으로 넣은 건 나름 잘한 선택이었습니다. 서버에 과도한 트래픽 부하를 주지 않으려는 최소한의 배려이고, 일정 간격으로 반복 요청하면 봇으로 탐지될 확률도 높아지거든요.
위 흐름 그대로 짜인 코드가 아래입니다.
2) 원본 코드 (2020년 당시 그대로)
⚠️ 주의: 아래는 2020년 당시 그대로의 코드입니다. 현재 실행하려면 반드시 §3·§4의 Selenium 4 변경점을 반영해야 동작합니다.
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time, random, os
from urllib.request import urlretrieve
from tqdm import tqdm
def get_images(keyword) :
# 1~3 초 사이의 랜덤난수
randomsl = random.uniform(1,3)
print('Loading')
# 스크롤 다운을 해야 되서 chromedriver 사용
driver = webdriver.Chrome('./chromedriver')
driver.implicitly_wait(30) # 페이지 로딩 최대 30초 대기
url = 'https://search.naver.com/search.naver?where=image&sm=tab_jum&query={}'.format(keyword)
driver.get(url)
# 스크롤 다운을 하기 위한 select
body = driver.find_element_by_css_selector('body')
for i in range(3) :
body.send_keys(Keys.PAGE_DOWN)
time.sleep(randomsl) # 1~3초 사이 난수 적용
# 이미지 tag select
imgs = driver.find_elements_by_css_selector('img._img')
result=[]
for img in tqdm(imgs) :
if 'http' in img.get_attribute('src') :
result.append(img.get_attribute('src'))
driver.close()
print('Search End')
# images 폴더 아래에 크롤링 하는 사람의 폴더를 생성
path = 'images/'
if not os.path.isdir(path + './{}'.format(keyword)) :
os.mkdir(path + './{}'.format(keyword) + '/')
print('{}'.format(keyword) + 'make folders')
else :
print('{}'.format(keyword) + 'be folders')
# 이미지 다운로드
for index, link in tqdm(enumerate(result)) :
start = link.rfind('.')
end = link.rfind('&')
filetype = link[start : end]
urlretrieve(link, path + './{}/{}{}'.format(keyword, index, filetype))
time.sleep(randomsl) # 1~3초 사이 난수 적용
print('DownLoad End')
if __name__ == "__main__":
keyword = input('who Search: ')
get_images(keyword)
3) 지금이라면 무엇이 달라질까 — Selenium 4 변경점
이 코드를 지금 그대로 실행하면 첫 부분부터 막힙니다. find_element_by_css_selector 줄에서 바로 에러가 나거든요. 원본은 Selenium 3.x 기반으로 추정됩니다(작성 당시 버전을 별도로 기록하지 않았기 때문에 추정 표현입니다). 2021년 10월 Selenium 4가 정식 릴리즈되면서 기존 API가 크게 바뀌었습니다(Selenium 공식 changelog 기준).
Selenium 공식 마이그레이션 가이드(selenium.dev) 기준으로 직접 확인한 주요 변경점들입니다:
find_element_by_*계열 메서드는 Selenium 4에서 제거됐습니다. 원본 코드에서 두 군데(find_element_by_css_selector,find_elements_by_css_selector)에 쓴 이 메서드들은 지금 실행하면 바로 에러가 납니다.By클래스를 사용하는 방식으로 바뀌었는데, 핵심 변경은 이 두 줄입니다:
“python
# 변경 전 (Selenium 3)
body = driver.find_element_by_css_selector('body')
# 변경 후 (Selenium 4)
from selenium.webdriver.common.by import By
body = driver.find_element(By.CSS_SELECTOR, 'body')
“
- ChromeDriver 경로를 직접 문자열로 넘기는 방식도 달라졌습니다.
webdriver.Chrome('./chromedriver')처럼 경로를 직접 전달하던 방법 대신,Service객체를 분리해서 넘기는 구조가 권장됩니다. 공식 마이그레이션 가이드에서 이 부분을 별도로 안내합니다.
- 이후 Selenium 4.6.0 버전부터 Selenium Manager가 도입되어 ChromeDriver 수동 관리 부담이 줄었습니다. 브라우저 버전에 맞는 드라이버를 자동으로 처리해주는 기능이라, 예전처럼 버전을 맞춰 드라이버를 직접 내려받아야 하는 경우가 줄어들었습니다.
이 글에서는 검증하지 않은 새 코드를 별도로 작성하지 않겠습니다. Selenium 4 스타일로 재작성한 예제는 직접 테스트 없이 제시하기 어렵습니다. 위 변화 방향을 파악한 뒤, Selenium 공식 문서와 마이그레이션 가이드(selenium.dev)를 직접 참고해서 적용하시는 걸 권장합니다.
4) 셀렉터는 살아있을까 — 사이트 구조 변화
img._img는 2020년 당시 네이버 이미지 검색 결과 썸네일에 붙어있던 CSS 클래스명이었습니다. 6년이 지나는 동안 네이버 검색 결과 페이지는 여러 차례 개편됐습니다. 지금도 같은 클래스명이 유지되고 있는지는 직접 확인하지 않았기 때문에 단정할 수 없습니다.
Selenium 호환성 문제를 해결하더라도, 셀렉터부터 다시 잡아야 할 가능성이 높습니다.
셀렉터를 새로 조사하는 방법은 간단합니다. 브라우저에서 네이버 이미지 검색 페이지를 열고, 개발자 도구(F12)를 켠 뒤, 수집하려는 이미지 요소 위에서 우클릭 → ‘검사’를 하면 현재 클래스명과 HTML 구조를 직접 확인할 수 있습니다. 6년 전 클래스명은 참고로만 두고, 현재 페이지를 기준으로 새로 잡는 과정이 필수입니다.
이 글에서는 현재 클래스명을 직접 검사하지 않은 상태에서 새 셀렉터를 제시하지 않겠습니다.
5) 그리고 정말 중요한 한 가지 — 공식 API와 크롤링 윤리
6년 전에는 “크롤링을 할때는 항상 주의해주시구요” 한 줄로 끝냈습니다. 지금이라면 이 부분만큼은 꼭 확인하고 가겠습니다:
- robots.txt와 이용약관 확인이 먼저입니다. 어떤 사이트든 크롤러 접근 허용 여부는 해당 도메인의
robots.txt파일과 서비스 이용약관에 명시된 경우가 많습니다. 코드를 짜기 전에 이 두 가지를 먼저 확인하는 게 순서입니다.
- 1~3초 랜덤 슬립은 잘한 선택이었습니다. 반복 요청 간격이 지나치게 짧으면 서버에 트래픽 부하를 주거나 차단으로 이어질 수 있습니다. 원본 코드에서
random.uniform(1, 3)으로 슬립을 넣은 부분은 6년이 지난 지금도 기본에 충실한 접근입니다.
- 다운받은 이미지의 저작권은 별개 문제입니다. 기술적으로 수집이 가능하더라도 그 이미지를 자유롭게 활용할 수 있는 건 아닙니다. 각 이미지의 라이선스에 따라 사용 범위가 다를 수 있으니, 개인 학습 외의 용도라면 확인이 필요합니다.
- 공식 API가 있다면 그쪽부터 먼저 검토하는 게 좋습니다. 네이버는 검색 관련 Open API를 별도로 제공합니다. 구체적인 엔드포인트·파라미터·호출 한도 수치는 이 글에서 임의로 적지 않겠습니다. 공식 네이버 개발자 센터 문서에서 가능한 경로를 먼저 확인해보시길 권합니다.
6) 새 프로젝트 시작 전 한눈 체크리스트
지금 같은 코드를 처음부터 다시 시작한다면, 저는 이 순서로 보겠습니다:
- 공식 API가 있는지부터 확인합니다. 크롤링보다 안정적이고, 서비스가 허용하는 공식 경로부터가 맞습니다. (→ §5 참조)
- 없다면 robots.txt와 이용약관을 먼저 읽습니다. 허용 범위를 파악하는 게 코드보다 앞입니다. (→ §5 참조)
- Selenium 4 기준으로
By셀렉터 방식으로 재작성합니다.find_element_by_*계열이 아닌By클래스 방식입니다. 공식 마이그레이션 가이드(selenium.dev)를 참고해서요. (→ §3 참조) - 셀렉터는 현재 페이지를 직접 열어 새로 잡습니다. 6년 전 클래스명은 참고만 합니다. (→ §4 참조)
- 슬립·재시도·예외 처리는 그대로 유지합니다. 랜덤 슬립 구조는 지금도 유효한 접근입니다.
변화 요약 — 2020년 vs 2026년 시점
| 항목 | 2020년 원본 | 2026년 시점 메모 |
|---|---|---|
| Selenium 메서드 | find_element_by_css_selector | find_element(By.CSS_SELECTOR, ...) 권장 (Selenium 공식 마이그레이션 가이드 기준) |
| ChromeDriver 관리 | ./chromedriver 수동 경로 지정 | Selenium Manager(4.6.0+) 자동 관리 옵션 존재 |
| 네이버 이미지 셀렉터 | img._img | 페이지 개편 가능성, 재조사 필요 |
| 접근 경로 우선순위 | 검색 결과 페이지 직접 크롤링 | 공식 Open API 우선 검토 권장 |
마무리
돌아보면 원본 글이 많이 짧았습니다. 코드 한 덩어리와 인사말 네 줄이 전부였으니까요. 그래도 그때 던지듯 올렸던 코드 한 덩어리가 6년 뒤 회고의 출발점이 됐습니다. 나름 재밌는 일이기도 합니다.
원본 출처: 이 코드는 유튜브 강의(https://www.youtube.com/watch?v=HJN28B7OSzw)를 참고하여 2020년 4월 22일 작성된 학습 기록입니다.
Selenium 변경점 출처: Selenium 공식 마이그레이션 가이드 및 공식 changelog (selenium.dev) 기준.