본문 바로가기
Python , Pip/Python 자동화, Macro

Selenium Web scraping 방법 정리 # Python implicit Explicit wait

by Developer88 2022. 5. 6.
반응형

오늘은 Python으로 Selenium 을 이용한 WebScraping 에 대해서 정리해 보도록 하겠습니다.

 

1. 필요한 모듈 설치

1-1.  Selenium 모듈설치

가장 먼저해야할 것은 selenium 모듈을 설치하는 일 입니다.

프로젝트 폴더에서 아래코드로 Selenium 모듈을 설치해 줍니다.

 

python -m pip install selenium

 

1-2.  Chromedriver 설치

드라이버를 설치하면서 주의할 점이 있는데요.

바로 자신의 크롬버전과 같은 버전을 설치해야 한다는 점 입니다.

 

따라서 제일 먼저 할 것은 Chrome버전을 확인하는 일 입니다.

 

 

제 맥에는 아래버전의 크롬이 설치가 되어 있네요.

 

 

 

이제 chromedriver 사이트 다운로드사이트(https://chromedriver.chromium.org/downloads)에 들어가서,

자신의 브라우저에 해당하는 버전을 찾아주어야 합니다.

저는 97버전이므로 아래와 같이 해당하는 버전을 찾아서 클릭해줍니다.

공식문서에 따르면 메이저버전에 맞는 드라이버를 다운로드하라고 나옵니다.

 

 

그럼 아래와 같이 파일디렉토리가 나오는데요.

해당하는 운영체제의 드라이버를 설치해주면 됩니다.

저는 Mac의 m1칩이므로 mac64_m1.zip파일을 선택할 수 있겠네요.

 

 

이렇게 다운로드 받은 파일은 파이썬 프로젝트가 있는 폴더로 옮겨주시면 됩니다.

파일하나로 되어있으므로 옮기거나 하는데 불편함은 없습니다.

 

예상하실 수 있으시겠지만,

크롬브라우저를 업데이트 할 때마다 이것을 재설치 해 주어야 합니다.

 

2. 브라우저 열고 닫기

아래와 같이 파이썬 파일을 작성후 실행시키면, 브라우저를 열어 사이트에 접속할 수 있는데요.

 

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome(<크롬드라이버 경로>)
driver.get("https://www.oasis.co.kr")

 

 

위에서 크롬드라이버 경로라고 적혀있는 곳에는 실제 크롬드라이버의 위치를 적어주셔야 합니다.

경로는 VisualStudioCode에서 크롬드라이버파일을 선택후 우측마우스버튼을 누른다음 패스를 복사해서 적어넣어주면 됩니다.

그런데, 한번도 실행하지 않은 경우에는 아래와 같은 경고가 나올수 있는데요.

 

 

우측마우스버튼으로 파일을 선택후 Terminal로 한번 실행시켜 주면 이런문제는 해결됩니다.

 

 

이제 다시한번 파이썬 파일을 실행시키면 브라우저가 열리는 것을 볼 수 있습니다.

 

간혹 실행을 했는데 아래와 같이 에러메시지가 나오는 경우가 있습니다.

버전이 않맞는다는 것 인데요.

이미 자동업데이트다운로드를 통해서 100버전이 다운로드 되었는데,

아직 크롬이 업데이트실행이 않되어서 예전버전으로 실행되는 경우가 있습니다.

저는 노트북을 사용하기에 컴퓨터의 브라우저는 완전히 close하는 일이 드물어서 자주 일어납니다.

크롬드라이버를 아래 설명에 맞는 최신버전으로 다운로드 해 주면 됩니다.

 

 

3.  implicit wait vs explicity wait

3-1. implicit wait

웹사이트에서 원하는 엘리먼트가 로딩이 되기전에 접근하려고 하는 경우, 에러가 발생할 수 있습니다.

python의 time.sleep()함수를 생각하시는 분들도 있을텐데요.

Selenium에서도 이러한 기능을 하는 API를 제공해주는데 implicitly_wait() 함수가 그것입니다.

implicit wait는 절대적인 시간을 기준으로 몇초 기다리게 할 것인지를 정해주는 역할을 합니다.

 

driver.implicitly_wait(기다리게 할 초)

 

3-2. Explicit wait

explicit wait은 특정조건을 만족할 때까지 코드를 멈추는 것을 의미하는데요.

이 때의 조건은 다음과 같은 경우들입니다.

  • alert is present
  • element exists
  • element is visible
  • title contains
  • title is
  • element staleness
  • visible text

 

원하는 요소가 로딩될때까지 기다리는, element exists나 element is visible의 경우가 많이 쓰이게 되겠지요.

ExplicitWait은 아래와 같이 사용을 하게 되는데요.

API를 이용하기 위해서 WebDriverWait 와 expected_conditions를 import 해 주어야 합니다.

사용방법은 

 

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("http://google.com")
try:
 element = WebDriverWait(driver, 8)\
  .until(EC.presence_of_element_located(By.CLASS_NAME, "test")
finally:
 driver.quit()

 

 

4. HTML 엘리먼트 찾기

4-1. find_element

find_element를 통해서 class명이나 id명을 통해서 쉽게 찾을 수 있구요.

두번째의 예처럼 HTML태그의 트리를 따라서 내려갈수도 있습니다.

 

vegetable = driver.find_element(By.CLASS_NAME, "tomatoes")

fruits = driver.find_element(By.ID, "fruits")
fruit = fruits.find_elements_by_id("tomatoes")

 

속성중 클래스명은 다른 엘리먼트들과 공통적으로 적용되는 경우가 많은데요.

클래스를 이용해서 엘리먼트를 찾을 때는 다른곳에도 해당 클래스가 많이 사용되지는 않았는지 살펴보아야 합니다.

그렇지 않으면 해당클래스가 적용된 여러개의 엘리먼트 중 가장 먼저 나오는 것만 찾아서 리턴해 줍니다.

 

Elements탭에서 'Cmd+F'를 누르면, 요소내에서 검색을 할 수 있는데,

해당 클래스가 얼마나 많이 있는지 이를 통해서 확인해 볼 수 있습니다.

 

아래와 같이 찾고자 하는 엘리먼트를 체이닝할수도 있습니다.

찾은 엘리먼트의 tag_name을 아래와 같이 print()해서 볼 수도 있구요.

 

 

 

특히, 여러개의 Element에 적용된 class로 특정 Element를 찾을 때,

먼저 해당 Element의 부모 Element를 찾은다음,

체이닝해서 해당 Class를 찾으면,

같은 Class가 중복으로 적용된 여러개 중 원하는 Element를 찾는데 도움이 됩니다.

 

4-2. Name Attribute찾기

selenium에서는 편하게 Name attribute도 쉽게 찾을 수 있습니다.

첫번째 인자로 (By.NAME)을 넣어주기만 하면 됩니다.

로그인을 할 때등은 이것이 있으면 매우 편하게 할 수 있습니다.

cssSelector로 ''input[name="userId"]로 찾았던 

 

driver.find_element(By.NAME, "userId").send_keys("슈퍼아이디")

 

4-3. CSS Selector 이용하기

css셀렉터를 이용해서 엘리먼트를 찾을수도 있습니다.

 

user_id = driver.find_element(By.CSS_SELECTOR, 'input[name="userId"]')
user_id = driver.find_element(By.CSS_SELECTOR, '.parent_class .child_class')

 

4-4. 여러개의 Element 한번에 찾기

검색결과나 비슷한 요소를 반복해서 나타낼 때는 동일한 클래스를 적용해서 표현하는데요.

이러한 엘리먼트들을 한번에 list로 가져올 수 있습니다.

아래와 같이 find_element뒤에 s만 붙여주면 됩니다.

 

fruit = driver.find_elements(By.CSS_SELECTOR, "#fruits .tomatoes")  
plants = driver.find_elements(By.TAG_NAME, "li")

for i in plants:
    print(i.text)

 

 

4-5. text 가져오기

아래와 같이 쉽게 text를 가져올 수 있습니다.

 

text = driver.find_element(By.CSS_SELECTOR, "h1").text

 

4-6. 엘리먼트에 대한 정보 

is_displayed(), is_selected(), tag_name 등을 이용해서,

아래와 같이 엘리먼트에 대한 정보를 가져올수도 있습니다.

 

# Get boolean value for is element display
visible = driver.find_element(By.CSS_SELECTOR, "[name='login']").is_displayed()
print(visible)

selected = driver.find_element(By.CSS_SELECTOR, "input[type='checkbox']:first-of-type").is_selected()
print(selected)

tag_name = driver.find_element(By.CSS_SELECTOR, "h1").tag_name
print(tag_name)

 

4-7. Attribute가져오기

위에서 Name attribute을 가져오는 방법에 대해서 정리해 보았는데요.

특정한 attribute을 다음과 같은 방법으로 가져올 수도 있습니다.

 

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.google.com")
driver.find_element(By.CSS_SELECTOR, '[name="q"]').send_keys("webElement")

attr = driver.switch_to.active_element.get_attribute("title")
print(attr)

 

 

5. HTML엘리먼트 상대적 경로 찾기

5-1.  Above 와 Below

사실 이름만으로도 어떤 기능을 할지 쉽게 예측할 수 있는데요.

바로 위에있는 엘리먼트와 아래에 있는 엘리먼트를 찾기 위해 사용합니다.

 

email_locator = locate_with(By.TAG_NAME, "input").above({By.ID: "password"})
password_locator = locate_with(By.TAG_NAME, "input").below({By.ID: "email"})

 

5-2. to_left_of 와 to_right_of

바로 옆에 있는 엘리먼트를 찾도록 해 줍니다.

 

cancel_locator = locate_with(By.TAG_NAME, "button").to_left_of({By.ID: "submit"})

submit_locator = locate_with(By.TAG_NAME, "button").to_right_of({By.ID: "cancel"})

 

5-3. Near

50px근처에 있는 엘리먼트를 가져올 수 있습니다.

 

email_locator = locate_with(By.TAG_NAME, "input").near({By.ID: "lbl-email"})

 

5-4. Chaining

체이닝을 통해서 쉽게 위치를 찾도록 도와줍니다.

 

submit_locator = locate_with(By.TAG_NAME, "button").below({By.ID: "email"}).to_right_of({By.ID: "cancel"})

 

6. 인터랙션

웹사이트와 인터랙션 하기위해서는 클릭하거나 텍스트필드에 텍스트를 보내는 것이 필요한데요.

이런 인터랙션을 하는 방법에 대해서 정리해 보겠습니다.

 

6-1. sendKeys() 와 click()

텍스트를 직접 하드코딩해서 아래와 같이 보낼수도 있습니다.

값을 보낼때는 send_keys()라는 함수를 이용해주면 됩니다.

텍스트키로 보낼때는 아래와 같이 send_keys()안에 텍스트를 넣어주면 됩니다.

 

driver.get("http://www.google.com")
driver.find_element(By.NAME, "userId").send_keys("SeleniumId")

 

 

텍스트 뿐만 아니라 enter키를 입력할수도 있는데요.

이 때는 아래오 같이 Keys를 import 해 주어야 합니다.

Enter키는 Keys.ENTER로 입력해주면 됩니다.

 

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

driver.get("http://www.google.com")
driver.find_element(By.NAME, "userId").send_keys("userId" + Keys.ENTER)

 

Enter이외에도 숫자버튼같이 다양한 입력이 제공되고 있습니다.

하지만, 실제로는 대부분 ENTER키 정도면 될 것 같네요.

 

 

참고로 보통 클릭할 수 있는 Element의 경우, click()함수로도 되지만, send_keys(Keys.ENTER)로 클릭이 되는 경우가 많습니다.

 

추가적으로, 간혹 원하는 페이지에 갑자기 팝업이 나타나는 경우가 있습니다.

이럴 때는 if문을 이용해서 해당 popup이 존재하는지 확인해 보고, 있다면,

닫기버튼 click()함수를 이용해 클릭해주어야 합니다.

 

6-2. clear()

텍스트를 비워야 하는 경우도 있는데요. 이때는  clear()함수를 해주면 됩니다.

 

from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()

    # Navigate to url
driver.get("http://www.google.com")
    # Store 'SearchInput' element
SearchInput = driver.find_element(By.NAME, "q")
SearchInput.send_keys("selenium")
    # Clears the entered text
SearchInput.clear()

 

7. 페이지 이동

페이지를 이동하는 것은 사실 제일 처음 한 것인데요.

바로 get()을 이용하는 것 입니다.

 

driver.get("https://selenium.dev")

 

refresh()를 할 경우도 있을텐데요.

아래 명령어로 쉽게 구현할 수 있습니다.

 

driver.refresh()

 

8. Javascript 실행하기

8-1. 기본적인 사용법

Selenium을 이용해서 Javscript코드를 실행해  볼 수도 있습니다.

아래 코드에 포함된 JS에서 arguments는 인자로 전달된 값을 저장한 배열과 비슷하게 사용할 수 있는 변수입니다.

JS에서는 인자를 정의하지 않아도, 이 변수에 배열형태로 함수안에서 접근할 수 있게 됩니다.(엄청나게 유연하지요)

그래서 arguments[0]은 첫번째 인자로 전달된 값을 의미합니다.

 

Selenium에서의 실행방법은 execute_script()함수를 이용해서,

두번째 인자로 실행할 element를 넣어주구요.

첫번째인자에는 arguemnts[0]으로 정해준 element에 접근해서 실행할 코드를 넣어주면 됩니다.

 

# Stores the header element
header = driver.find_element(By.CSS_SELECTOR, "h1")

# Executing JavaScript to capture innerText of header element
driver.execute_script('return arguments[0].innerText', header)

 

8-2. Click()

JS코드로 클릭하고자 한다면 아래와 같이 할 수 있겠지요.

이 방법은 Selenium으로 click()함수가 실행되지 않을 때 대안으로 사용할 수도 있습니다.

 

driver.execute_script('arguments[0].click()', <클릭할 element>)

 

이상으로 Python 으로 Selenium Web scraping 방법에 대해서 정리해 보았습니다.

728x90

댓글