Selenium - Explicit Wait과 Implicit Wait
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
3-3. Wait and Call¶
- Wait을 이용해서 동적 웹 사이트를 성공적으로 스크래핑해봅시다.
Implicit / Explicit Wait¶
Selenium은 동적 웹 사이트에 대한 지원을 진행하기 위해 명시적 기다림(Explicit Wait) 과 암묵적 기다림(Implicit Wait) 을 지원합니다.
- Explicit Wait: 다 로딩이 될 때까지 지정한 시간 동안 기다림 (e.g. 다 로딩이 될 때까지 5초동안 기다려!)
- Implicit Wait: 특정 요소에 대한 제약을 통한 기다림 (e.g. 이 태그를 가져올 수 있을 때까지 기다려!)
Target: IndieStreet 이벤트 스크래핑¶
다음 사이트에 있는 행사의 이름들을 스크래핑 해봅시다 : https://indistreet.com/live?sortOption=startDate%3AASC
해당 웹 페이지는 공연 리스트가 담긴 웹 페이지입니다.
# 스크래핑에 필요한 라이브러리를 불러와봅시다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
XPath?¶
위 사이트를 확인해보니 class 이름이 조금 특이했습니다.
이는 스크래핑을 방지할 목적으로 랜덤하게 class 이름을 생성하기 때문인데요,
이러한 경우에서 쓸 수 있는 방법이 여러가지가 있는데,
그 중 한가지 방법인, 위치를 활용한 방법을 알아보도록 하겠습니다.
XPath
는 XML, HTML 문서 등의 요소의 위치를 경로로 표현하는 것을 의미합니다.
마치 데스크탑/폴더1/폴더2/음악.mp3
와 같이 말이죠!
이를 적용해서 데이터를 온전히 가져와봅시다.
Hint: 저희가 원하는 요소의 XPath는 //*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]
입니다.
# 예시 사이트에 요청을 진행하고, 예시 사이트의 첫 번째 이벤트의 제목을 가져와봅시다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]'.text)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[13], line 5 3 driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) 4 driver.get("https://indistreet.com/live?sortOption=startDate%3AASC") ----> 5 driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]'.text) AttributeError: 'str' object has no attribute 'text'
오류가 발생합니다! 왜 그럴까요?
이 웹 페이지는 동적 웹페이지이기 때문에 그렇습니다.
이 페이지를 성공적으로 스크래핑하기 위해서는 Wait
을 사용해야합니다.
2가지 Wait 방법을 활용해서 주어진 내용을 스크래핑 해봅시다.
Implicit Wait¶
.implicitly_wait()
을 활용해서 암시적 기다림을 적용할 수 있습니다.
주의하실 점으론, 반드시 해당 시간을 기다리는 것이 아니라, 로딩이 다 될때까지의 한계 시간의 의미를 가집니다.
# 10초동안 Implicit Wait을 진행하도록 해서 스크래핑이 잘 이루어지도록 수정해봅시다.
from selenium.webdriver.support.ui import WebDriverWait
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(10)
driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]').text
'Knock Live Rock band'
Explicit Wait¶
WebDriverWait()
과 두 메서드를 활용해서 명시적 기다림을 적용할 수 있습니다.
until()
: 인자의 조건이 만족될 때까지
until_not()
인자의 조건이 만족되지 않을 때까지
예를 들어, id가 target
인 요소가 존재할 때까지 기다린 후 다음 명령을 진행합니다.
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "target")))
이때, EC는 expected_conditions
로, selenium에서 정의된 조건들입니다 (~가 존재하면, ...)
이곳에서 더 자세한 정보를 확인할 수 있습니다.
주어진 XPath가 등장할 때 까지 Wait을 진행해봅시다. (EC.presence_of_element_located()
활용)
# Explicit Wait를 활용해서 스크래핑이 잘 이루어지도록 코드를 작성해봅시다.
from selenium.webdriver.support import expected_conditions as EC
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
# explicit wait 방식
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]')))
element.text
'Knock Live Rock band'
여러 공연 제목 가져오기¶
여러 공연의 제목들을 가져오기 위해 XPath를 관찰해봅시다.
이 페이지의 공연 제목들에 해당하는 XPath는 다음과 같습니다:
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[1]/div/a/div[2]/p[1]
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[2]/div/a/div[2]/p[1]
//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[3]/div/a/div[2]/p[1]
...
이를 일반화해서 가장 먼저 등장하는 10개의 이름을 추출하는 코드를 작성해봅시다.
# 여러 공연의 제목을 스크래핑하는 코드를 작성해봅시다.
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
driver.implicitly_wait(10)
for i in range(1, 11):
print(driver.find_element(By.XPATH, f'//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[{i}]/div/a/div[2]/p[1]').text)
Knock Live Rock band Sign up 이디어츠 1st EP 발매기념 공연 도라페스트 4탄 NO PASARAN! Vol.1 LIVE in NOV 2019 LIVE in DEC 2019 오롯한 라이브와 함께 LIVE in FEB 2020 PUNK Marathon
# 포맷의 다양한 형태
a=10
b=20
print(f'포맷: {a}, {b}')
print('포맷: {}, {}'.format(a, b))
포맷: 10, 20 포맷: 10, 20