셀레니움 웹 크롤링 봇 탐지 우회

셀레니움을 사용하다보면 어떤 웹사이트에서는 작동이 되지 않거나, 혹은 봇이 탐지되었다고 거부를 당하는 일이 있다. 셀레니움 웹 크롤링 봇 탐지 우회하는 방법을 찾아 이곳까지 왔다면 정말 제대로 찾아왔다. 웹사이트마다 접속자 Bot이라고 인식하는 방법은 여러가지가 있기 때문에 웹사이트마다 봇으로 인식되어 거부당하는 것을 하나로 설명할 수 없다. 다만! 봇으로 인식되지 않게 하는 해결책은 이 글 하나로 90%를 커버할 수 있다. 궁금한 점이나 설명이 부족한 부분을 댓글로 지적해주면 해당 부분에 대한 설명을 보충하여 글이 업데이트 됩니다.



 

셀레니움 웹 크롤링 봇탐지 우회

크롬 셀레니움을 구동해보면 우리는 이런 창을 자주 보게 된다.

셀레니움 웹 크롤링 봇 탐지 우회

Chrome이 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다. 저것은 그냥 우리의 눈으로만 보이는 것이 아니라, 저러한 정보가 웹사이트 서버에 사용자 정보로 전달이 된다. 쉽게 말해 웹사이트는 접속자가 셀레니움을 사용해서 접속했다는 것을 쉽게 알 수 있다는 의미이고 셀레니움으로 접속을 한 경우 접속을 거절하도록 설정해둘 수 있다.

그럼 우리는 어떡해야하나? 간단하다. 일반 크롬과 똑같은 설정으로 셀레니움을 접속시키면 된다. 이를 보통 디버거 크롬이라고 부른다. 자! 이 글에서는 당장의 문제를 해결하도록 소스를 통으로 보여주고 쓸데없는 말은 전부 제거하여 쓸데없는 공부의 고통을 줄이도록 하겠다.

드라이버 구동

일단 기본적으로 드라이버를 구동하는 방법은 셀레니움 설치와 크롬 드라이버 자동 처리 를 참조한다. 이 소스를 통으로 복붙해서 쓰는 습관을 가지면 정말 코딩과 디버깅이 편리해진다.

위 글의 핵심 코드를 다시 써보자면 아래와 같다.

from selenium import webdriver
import chromedriver_autoinstaller

chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]  #크롬드라이버 버전 확인

try:
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe')   
except:
    chromedriver_autoinstaller.install(True)
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe')

driver.implicitly_wait(10)

위 코드를 실행하면 자동화된 테스트 소프트웨어에 의해 제어되고 있습니다 라는 멘트가 나타나게 되는데 코드를 조금 수정해서 디버거 크롬으로 구동하는 방법을 알아보자

디버거 크롬

거두절미하고 소스부터 갖다 붙이자면 아래와 같다. 내용을 먼저 이해하고 사용하기 보다는 일단 소스를 복붙해서 무작정 실행부터 해보는 것이 좋다고 생각한다.


from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import chromedriver_autoinstaller
import subprocess

subprocess.Popen(r'C:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\chrometemp"') # 디버거 크롬 구동


option = Options()
option.add_experimental_option("debuggerAddress", "127.0.0.1:9222")

chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
try:
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
except:
    chromedriver_autoinstaller.install(True)
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
driver.implicitly_wait(10)

위 코드에 대해 설명을 하자면

subprocess.Popen 은 크롬 브라우저를 실행시키는 명령어이다. 일단 chrome.exe 파일이 내 컴퓨터 어디에 위치해 있는지 확인이 필요하다. 대부분의 컴퓨터는 아래 둘중의 한군데에 설치되어 있다.

C:\Program Files\Google\Chrome\Application\
C:\Program Files (x86)\Google\Chrome\Application\

두 경로 다 찾아보았는데 내 컴에는 없다면 크롬이 설치되어 있지 않거나 혹은 본인이 크롬을 설치할 때 다른 경로에 설치한 것이니 어디에 설치되어 있는지는 본인만이 알 수 있다. (참고로 윈도우 사용자를 기준으로 설명하였다.)

자 그럼 chrome.exe 파일을 실행시키되 명령어 뒤에 보면 옵션이 보인다. –remote-debugging-port=9222 는 구동하는 크롬의 포트를 알려주는 것이다. 포트가 무엇인지는 알필요 없다. 그냥 저 옵션을 넣어주면 된다고만 이해해도 충분하다. –user-data-dir=”C:\chrometemp” 는 크롬을 사용하여 웹상을 돌아다녔을 때 생기는 쿠키와 캐쉬파일을 저장하는 곳이다.

일반 셀레니움은 쿠키와 캐쉬파일을 저장하지 않는다. 저장할 필요가 없기 때문이다. 따라서 셀레니움 드라이버를 구동할때마다 쿠키와 캐쉬가 완전히 삭제된 새로운 브라우저창이 열린다. 그러나 우리가 지금 사용하려는 디버거 크롬은 일반 크롬과 동일하기 때문에 쿠키와 캐쉬를 저장한다. 만약 디버거 크롬을 사용할 때마다 자동으로 쿠키와 캐쉬파일을 삭제하도록 처리하고 싶다면 C:\chrometemp 폴더 이하 파일들을 삭제해주면 된다.

코드로 처리하자면 아래와 같이 하면 되겠다.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import chromedriver_autoinstaller
import subprocess
import shutil

try:
    shutil.rmtree(r"c:\chrometemp")  #쿠키 / 캐쉬파일 삭제
except FileNotFoundError:
    pass

subprocess.Popen(r'C:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\chrometemp"') # 디버거 크롬 구동


option = Options()
option.add_experimental_option("debuggerAddress", "127.0.0.1:9222")

chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
try:
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
except:
    chromedriver_autoinstaller.install(True)
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
driver.implicitly_wait(10)

실행 원리에 관한 보다 자세한 정보를 알고 싶다면 열려있는 chrome에서 크롤링하기 를 참조하면 좋다. 좋은 정보를 제공해주신 작성자님께 감사의 말씀 전합니다.

마치며

위 코드로 실행하면 거의 대부분의 봇탐지를 우회할 수 있다. 브라우저 설정이 일반 크롬과 동일하기 때문이다. 그럼에도 불구하고 봇탐지 걸리는 경우가 있다. 예를 들자면 네이버/구글 로그인이 그렇다. 웹사이트에서 접속자가 사람이 아닌 봇으로 인지하는 경우는 크게 두가지이다. 하나는 위와같은 브라우저 설정을 보는 것이고 하나는 웹페이지에서 자바스크립트가 실행되었는지로 따지는 것이다.

우리는 보통 셀레니움으로 엘리먼트를 찾아서 클릭하고 텍스트등을 적고 전송한다. 거의 대부분의 작업이 클릭, 스트링전송, 텍스트 읽어보기 등일 것이다. 셀레니움에서 이러한 작업은 우리가 보통 element.click() / element.send_keys(‘~~~’) 와 같은 메소드등을 사용하는데 이 메소드가 바로 자바스크립트를 사용해서 웹페이지상에 버튼을 클릭하거나 스트링을 넣을 수 있도록 하는 것이다. 일반인 사용자는 웹페이지에서 직접 키보드로 타이핑을 하거나 마우스를 클릭하지 자바스크립트로 무언가를 실행하지 않는다.

어느 정도 감이 왔는가? 결국 위 함수를 사용하지 않고 웹페이지를 사용하도록 하는 것이다. 매크로를 이용하던 (autohotkey 추천) 다른 방법을 사용해도 좋다. 자바스크립트가 실행되지 않고 스트링을 입력하더나 무언가를 클릭하게 하면 된다.

후자에 관한 부분은 나중에 따로 포스팅하도록 하겠다.

셀레니움 웹 크롤링 봇 탐지 우회 2탄 네이버 로그인 캡차 피하기

궁금한점이나 설명이 부족한 부분을 지적해주면 해당 부분의 설명을 보강하여 글을 업데이트 됩니다.

2021.3.5. updated

2021.4.3. updated

41 thoughts on “셀레니움 웹 크롤링 봇 탐지 우회”

  1. 안녕하세요! 🙂
    포스트 잘 읽었습니다.
    위와 같이 따라해서 크롬창을 여는 부분까지는 성공을 하였는데
    연 크롬창에 텍스트를 넣고 검색을 하는부분(엘리멘트를 사용하는 경우)은 어떤 명령어를 사용해야 하나요?

    응답
      • 실행해본 결과 크롬드라이버에 의해 제어되는 창이 우회하여 열린 창 앞에 뜹니다. 앞의 창을 닫고서 뒤에 있는 창을 자동화시키고 싶은데 직접적으로 어떻게 해야하나요? 창의 우선순위를 바꾸려고 window handles 메소드도 써 봤습니다. pyautogui를 쓰면 굳이 앞의 방법을 쓰지 않고도 직접적으로 자동화를 할 수 있기 때문에 물어봅니다.

        응답
  2. 마지막 탭을 닫고 종료하고싶은데 어떻게 해야할까요?
    창을 못닫아서 주피터노트북에서 셀이 계속 실행중이네요..
    driver.close()로 마지막창을 닫으면 오류뜨면서 셀이 뻗어주긴 하는데요.. 정상적으로 종료시키고싶어서요

    응답
    • 네 당연히 가능합니다. 사이트에 접속할때 모바일 주소로 접속하거나 혹은 크롬에서 UA 를 모바일 값으로 수정하는 방법으로 접근할 수 있겠습니다.

      응답
      • 저도 그렇게 시도하였으나
        개발자 도구를 중지하면 바로 데스크탑 모드로 돌아오네요.
        그래서 아예 셀레니움에서 우회된 모바일로 동작하게끔 하고 싶은데
        가능하시면 해당 코드도 같이 언급해주실 수가 있으신가요?

        응답
        • 개발자도구에서 UA 를 바꿔주는것이 아니라 크롬작동시에 옵션을 넣어주어야 합니다. 구동옵션에 -user-agent “Mozilla/5.0 (Linux; U; Android 2.3.3; ko-kr; SHW-M250S Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1” 이런 것등을 추가해보세요. 좀 더 자세한 정보는 “크롬 ua 변경 옵션”으로 구글링하시면 나올겁니다.

          응답
  3. 셀레니움이 느리다고 해서 requests 모듈을 이용해서 크롤링하는 코드를 만들었는데
    이 경우엔 어떤 식으로 우회 할 수 있을까요?
    cloudflare로 차단해 버리니까 방법이 없네요ㅠ

    응답
  4. 셀레니움 코드를 헤로쿠 같은 가상 서버에서 돌리려고 하는데 이 방법을 쓸 수 있을까요?
    그럼
    subprocess.Popen의 크롬 설치 경로나
    –remote-debugging-port=9222의 크롬 포트,
    –user-data-dir=”C:\chrometemp” 쿠키/캐쉬 저장 폴더 지정은 어떻게 해줘야 할까요?

    응답
    • 네 최근에 네이버 로그인 로직이 바뀌어서 현재는 위 글 절차대로는 안되실거에요. 위 글은 참고만 하시고 응용하셔서 도전해보시기 바랍니다.

      응답
  5. 안녕하세요. 혹시 jupyter로 여러 chrome창을 동시에 크롤링을 작업하고 싶은데 port = 9222로 고정이 되어 여러 크롬창을 컨트롤 하기가 안 되더라구요… 해결법이 있을까요?

    응답
  6. 포스팅 너무나 감사합니다!
    마지막까지 이해는 하였는데
    첫번째 try 문에서 퍼미션 에러가 나네요
    파이썬을 접한지 1주일 밖에 안된 파린이라 기초적인 문제에도 허덕이고 있습니다.ㅠ

    어떤 방법으로 해야할까요.ㅠ

    아래는 콘솔창 전문 입니다.

    환경은 윈도우에서 파이참으로 테스트 하고 있습니다.

    C:\pythonProject\DebugChrome\venv\Scripts\python.exe “C:\Program Files\JetBrains\PyCharm Community Edition 2021.3.1\plugins\python-ce\helpers\pydev\pydevd.py” –multiproc –qt-support=auto –client 127.0.0.1 –port 7741 –file C:/pythonProject/DebugChrome/main.py
    Connected to pydev debugger (build 213.6461.77)
    Debug Chrome Test Program
    Traceback (most recent call last):
    File “C:/pythonProject/DebugChrome/main.py”, line 14, in init_ChromeDriver
    shutil.rmtree(r”c:\chrometemp”) # 쿠키 / 캐쉬파일 삭제
    File “C:\Python310\lib\shutil.py”, line 739, in rmtree
    return _rmtree_unsafe(path, onerror)
    File “C:\Python310\lib\shutil.py”, line 612, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
    File “C:\Python310\lib\shutil.py”, line 617, in _rmtree_unsafe
    onerror(os.unlink, fullname, sys.exc_info())
    File “C:\Python310\lib\shutil.py”, line 615, in _rmtree_unsafe
    os.unlink(fullname)
    PermissionError: [WinError 5] 액세스가 거부되었습니다: ‘c:\\chrometemp\\BrowserMetrics\\BrowserMetrics-61F4B21B-26E0C.pma’
    python-BaseException

    Process finished with exit code 1

    응답
  7. popen하고 terminate() 종료하였는데 다음실행시 크롬 이전 복구창이 뜹니다

    quit시에 chromedrvier가 오류가나서 chromedrvier 를 강제로 종료시켰는데

    혹시 이게 영향이 있나요?

    응답
  8. 좋은 정보 감사합니다.
    브라우저 설정에 관해서는 잘 이해하였으나

    “””웹페이지에서 자바스크립트가 실행되었는지로 따지는 것이다.”””

    이 문구에서 궁금한 점이 있는데요 자바스크립트 실행여부를 서버에서 체크할 수 있는 방법으로 어떤게 있을까요?? 제가 아는 지식으로는 자바스크립트는 클라이언트단에서 실행되는걸로 알고 있는데 일반 마우스 클릭과 스크립트 실행을 통한 클릭을 어떻게 탐지되는지가 궁금합니다. 결국 마우스 클릭도 해당 버튼의 자바스크립트를 실행하는 것이 아닌지요

    응답
  9. 안녕하세요! 혹시 리눅스 서버에서도 사용이 가능한가요?
    디버그 크롬 경로만 수정해서 해보려고 했는데 안되네요ㅜ.ㅜ
    리눅스 서버의 경우 display가 없다고 하는데 xclip설치했는데도 안돼서요.!

    응답
  10. 안녕하세요. 한가지 문의 드리고 싶은게 있습니다~
    디버그 크롬을 사용하게되면 quit()로는 크롬 종료를 할 수가 없나요?
    일반 셀레니움에서 잘 작동하고 디버그 크롬에서 close()는 작동을 잘하는데 유독 quit()만 작동을 하지 않습니다. ㅠㅠ

    응답

Leave a Comment