[python] 크롤링 implicit wait vs time.sleep

[python] 크롤링 implicit wait vs time.sleep

반응형

크롤링 하다보면, 잠시 코드 실행을 멈춰야 할 필요가 생긴다.

주된 요인은 2가지가 있을 것 같은데,

첫번쨰, 코드의 실행속도가 너무 빨라, 웹 서버에 데이터가 전송이 느려서, 실제 사이트가 열리지도 않았는데 코드가 먼저 실행되는 경우이다. 이런 경우에는 데이터가 씹히게 된다.

두번째, 반복적인 크롤링 작업은 서버에 많은 부하가 걸리는 것을 유발하는 큰 요인이다. 그러므로, 자칫하면 우리의 ip가 잠깐 정지 당할 수도 있다. 크롤링 작업을 진행하다가 갑자기 ip가 막혀버리면, 이보다 심한 경우는 없을 것이다. 그러므로 적절한 쉬어감의 미학이 필요한 것이다.

본격적으로 time.sleep 문과 implicit wait 문을 비교하면, 확실히 time.sleep 보다 implicit wait 이 좋은 것 같다. 아니다.

포스팅을 하다보니, 3가지 explicit wait 까지 전부 제대로 알고, 적절히 섞어서 쓰는 것이 성능면이나 안정도면이나 가장 best 인 것 같다.

전제조건은 ip 부하에 상관없이, 빠르게 크롤링할 수 있는 사이트를 기준으로 한다.

만약에 좀 사이트가 헤비하고, 이러한 차단 능력이 있다면, time.sleep문을 통해 강제로 코드가 실행되는 것을 멈출 수 있다.

그 전에 기본적인 time.sleep 과 implicit wait의 차이는 다음과 같다.

time.sleep

import time time.sleep(2)

그래도 데이터 양이 많아지면, 가장 많이 사용해야 될 것같다.

- 일부 ip에서는 동일한 시간을 쉬고 접근하는 것도 막을수도 있으니, 가장 best는 random 함수를 쓰는 것이다

- random 함수는 random 모듈을 불러와야한다.

import time import random for url in url_list: html = requests.get(url).text time.sleep( random.uniform(1,4) ) # 1~4초 사이 랜덤한 시간으로 쉬어줘 # 또는 time.sleep(random.randint(1,4)) # 1~4초 사이 랜덤한 시간으로 쉬어줘 # 입력 파라미터 a부터 b까지의 범위 중 # 임의의 정수를 반환 (a <= N <= b) # 또는 time.sleep(random.random()) # 반 열린 구간(semi-open range) # [0.0, 1.0) 무작위 float 숫자를 반환

implicit wait

보통은 explicitly wait 도 같이 있는데, 나는 그것보다 이게 더 중요하다고 생각한다.

우선, 무엇보다 코드를 입력하게 되면, 서버랑 통신하기 때문에, 시간차라는 것이 생기게 된다.

예를들어, driver.get(‘https://naver.com’) 명령어를 쓰면,

서버로부터 데이터를 받아오기도 전에, 브라우저 화면에 있는 것을 어떻게 할까?

에 대한 나의 다음 명령어가 실행되면,

컴퓨터는 당연히 에러를 발생시키게 된다.

여기서 해결방법은 화면이 뜰때까지 기다리는 것인데, 셀레니움에서 기다리는 방법이 2가지가 존재한다.

바로 implicitly wait (웹페이지 전체가 넘어올때까지 기다리는 방법) 와 explicitly wait (웹페이지의 일부분이 나타날때까지 기다리기) 이다.

이게 좋은게, 바로 10초로 입력을 해놔도 해당 코드가 실행되고, 사이트가 접속하는데 1초만에 잘 연결이 되었다면, 10초가 지나지 않아도 다음 명령어를 실행할 수 있고, 접속이 원할하지 않고 웹페이지가 넘어오지 않을때는, 기다려보다가 10초 이후에 그냥 다음 명령어를 실행하게 된다.

여기서 큰 문제가 하나 있다.

어떤 사이트는 접속을 할때 한번에 모든 페이지가 다 나오는 경우도 있긴 하지만, 전체 페이지의 일부분만 먼저 나오거나 늦게 나오는 경우가 있을 수도 있다.

이것을 동적DOM 이라고 부르는데 어려운 용어이긴 하다. 심플하게, 한번에 웹페이지가 표시되지 않는 경우도 있다는 점에 주목하라.

이런 경우에 implicitly wait 를 8초로 설정하고, 1초만에 넘어왔는데, 웹페이지의 일부분이 자바스크립트로 구현되어 있어, 그 일부분이 화면상에서 렌더링 되느라 늦게 표시되었다면 어떻게할까?

예를들어, 티스토리 블로그에 접속했는데, 내용은 뜨지만, 구글 에드센스 광고의 경우, 로고만 뜨는 경우이다.

이떄 문제는, 웹페이지 단에서 정보가 넘어왔으나, 일부 js의 내용이 렌더링 되기 전에 그 다음 명령어가 작동이 되는 것이다.

이런 경우를 막기위해, explicitly wait 를 사용한다. 바로 내가 찾고자 하는, 크롤링하고자 하는 부분이 표시될 떄까지 기다리는 것을 의무화해놓는 것이다.

기본적으로 import 해야하는 모듈이 많으니, 참고하자.

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome('chromedriver.exe') driver.get("https://naver.com") element = WebDriverWait(driver, 8).until( EC.presence_of_element_located(By.ID, "idname") ) )

1)

대략적인 설명으로, element 변수를 생성 ->

2)

WebDriverWait(driver, 8) 구동한 driver는 셀레니움으로 구동한 드라이버로서,

8초까지 그 부분이 필요할때까지 기다려보겠다는 의미 [(물론 8초 이전에 그 부분이 나타나면 8초까지 다 기다리지 않는것!!)]

3)

-> 어떤 부분에 대한 것인지? -> 엘레먼트가 나타나면 해당 엘레먼트를 element 변수가 가리킴

4)

EC.presence_of_element_located( ) 괄호 안의 요소가 나올떄까지 기다리라는 의미로서,

보통 ID 값이나, 클래스이름, xpath, name, css_selector, partial_link_text(일부 텍스만 일치), link_text(전부 일치)

기존의 find_by_element_class_name 등과 같이 find_by~ 구문과 비슷하다고 생각되며,

전체 괄호의 개수가 마지막에 3개가 온다는 것을 잊지말자.

(By.ID, ‘아이디이름’)

(By.CLASS_NAME, ‘클래스명’)

(By.XPATH, ‘xpath경로’)

(By.NAME, ‘네임명’)

(By.CSS_SELECTOR, ‘CSS셀렉터’)

(By.PARTIAL_LINK_TEXT, ‘링크텍스트일부분’)

(By.LINK_TEXT, ‘링크텍스트(전부일치)’)

EC.element_to_be_clickable((By.ID, 'up') EC.element_to_be_clickable((By.CLASS_NAME, 'car_class') EC.element_to_be_clickable((By.NAME, 'KimJ')

반응형

from http://ongbike.tistory.com/470 by ccl(A) rewrite - 2021-11-25 01:02:00