20230420 TIL - Selenium

ohyujeong·2023년 4월 20일
0

TIL

목록 보기
9/27
post-thumbnail

📖 오늘의 학습

  • Selenium을 활용한 웹 스크래핑
  • 특강 : 좋은 코드 작성하기

Selenium 라이브러리

  • Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크
  • 동적 웹페이지를 웹 스크래핑할 수 있도록 해준다.
  • %pip install selenium 로 설치

Web driver

  • 웹 브라우저와 연동을 위해서는 Web driver 가 필요하다.
    Web driver를 관리하는 라이브러리인 webdriver-manager 의 설치가 필요하다.
  • webdriver-manager 의 역할 : 현재 나의 브라우저의 버전을 확인하고 그에 맞는 버전의 드라이버를 인스톨 해준다.

Selenium 시작하기

# selenium으로부터 webdriver 모듈을 불러온다

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

웹 브라우저 실행 및 요청 보내기

# 현재 사용하고 있는 크롬에 맞는 드라이버를 설치하고 service 파라미터로 전달
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("http://www.example.com")
print(driver.page_source)

위와 같이 실행할 경우 웹 브라우저 창이 계속 켜 있게 된다. 자동으로 닫히게 하기 위해 with절을 사용한다.

with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    print(driver.page_source)

Driver에서 특정 요소 추출하기

  • 요소 하나 찾기
    driver.find_element(by, target)

    • by : 대상을 찾는 기준 : ID, TAG_NAME, CLASS_NAME, ...
    • target : 대상의 속성
  • 요소 여러개 찾기
    driver.find_elements(by, target)

    • by : 대상을 찾는 기준 : ID, TAG_NAME, CLASS_NAME, ...
    • target : 대상의 속성

여기서 by에 해당하는 파라미터를 넘길 때 from selenium.webdriver.common.by import By 로 라이브러리를 가져와 가져오려는 기준을 명시하는데 사용한다.

from selenium.webdriver.common.by import By

# p 태그에 해당하는 요소 하나를 찾기
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    print(driver.find_element(By.TAG_NAME, "p").text)
    
# p 태그에 해당하는 요소 여러개 찾기
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    for element in driver.find_elements(By.TAG_NAME, "p"):
        print(element.text)

Wait and Call

  • 동적 웹 사이트는 비동기로 동작하기 때문에 데이터 처리가 완료되기 전에 데이터를 가져오게 되면 완전하지 못한 데이터를 가져온다.
  • 이를 보완하기 위해 Selenium은 암묵적 기다림(Implicit Wait)과 명시적 기다림(Explicit Wait)을 지원한다.
  • 암묵적 기다림(Implicit Wait) : 다 로딩이 될 때까지 지정한 시간 동안 기다림 (e.g. 로딩이 될 때까지 5초동안 기다려!)
  • 명시적 기다림(Explicit Wait): 특정 요소에 대한 제약을 통한 기다림 (e.g. 이 태그를 가져올 수 있을 때까지 기다려!)

암묵적 기다림(Implicit Wait)

  • 인자로 주는 시간을 기다리는 것이 아니라 대기할 최대 시간을 의미
  • 즉, 10초로 설정했을 때 10초 이전에 로딩이 완료되면 그대로 기다림이 끝난다.
# 10초동안 Implicit Wait을 진행
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("https://indistreet.com/live?sortOption=startDate%3AASC")
    driver.implicitly_wait(10)
    print(driver.find_element(By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[2]/div/a/div[2]/p[1]').text)

명시적 기다림(Explicit Wait)

  • WebDriverWait()과 아래 두 메서드를 활용해서 명시적 기다림을 적용한다.
  • until(): 인자의 조건이 만족될 때까지
    until_not() 인자의 조건이 만족되지 않을 때까지
  • WebDriverWaitexpected_conditions 를 활용하여 명시적 기다림을 적용한다.
  • expected_conditions 더 알아보기
from selenium.webdriver.support.ui import WebDriverWait
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")
    element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="__next"]/div/main/div[2]/div/div[4]/div[1]/div[2]/div/a/div[2]/p[1]')))
    
    print(element.text)

Wait을 활용한 복수 개의 요소의 내용 가져오기

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)

마우스 이벤트 처리하기

  • Selenium을 이용하여 여러가지 마우스 Event를 발동시킬 수 있다.
  • 마우스 입력의 과정
    1.입력하고자 하는 대상 요소를 찾는다. (find_element() 이용)
    2.입력하고자 하는 내용을 click을 통해 전달한다.
    3.perform()을 통해 동작한다.
  • ActionChains 라이브러리가 마우스 입력과 같은 액션들을 연쇄적으로 수행할 수 있게 한다.
  • 마우스 이벤트 더 알아보기
# 웹 스크래핑에 필요한 라이브러리 불러오기
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# 주어진 웹사이트를 누른 후, 로그인 버튼 요소를 찾은 후 마우스 이벤트를 실행
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://hashcode.co.kr/")
driver.implicitly_wait(0.5)

button = driver.find_element(By.XPATH, '//*[@id="main-app-gnb-header"]/div/div/div[1]/div/div[2]/div/div/div[1]/span[1]/a[1]')
ActionChains(driver).click(button).perform()

키보드 이벤트 처리하기

  • Selenium을 이용하여 여러가지 마우스 Event를 발동시킬 수 있다.
  • 키보드 입력의 과정
    1.입력하고자 하는 대상 요소를 찾는다. (find_element() 이용)
    2.입력하고자 하는 내용을 send_keys_to_element를 통해 전달한다.
    3.perform()을 통해 동작한다.
  • Keys import가 필요하다.
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver import Keys, ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

import time

# driver를 이용해 해당 사이트에 요청을 보낸다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://hashcode.co.kr")
time.sleep(1)
                          
# 내비게이션 바에서 "로그인" 버튼 찾고 클릭
button = driver.find_element(By.XPATH, '//*[@id="main-app-gnb-header"]/div/div/div[1]/div/div[2]/div/div/div[1]/span[1]/a[1]')
ActionChains(driver).click(button).perform()
time.sleep(1)

# "아이디" input 요소에 아이디(이메일) 입력
id_input = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/div[2]/input')
ActionChains(driver).send_keys_to_element(id_input, '로그인 할 이메일 입력').perform()
time.sleep(1)

# "패스워드" input 요소에 비밀번호를 입력
pw_input = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/div[4]/input')
ActionChains(driver).send_keys_to_element(pw_input, '로그인 비밀번호 입력').perform()
time.sleep(1)

# "로그인" 버튼을 클릭
login_button = driver.find_element(By.XPATH, '//*[@id="main-app-account"]/div/div[2]/div/div[2]/div[1]/div/div[2]/button')
ActionChains(driver).click(login_button).perform()
time.sleep(1)

📝 주요메모사항

좋은 코드란?

  • 읽고 이해하고 수정하기 쉬움
  • 명확한 이름, 일관된 형식, 의미있는 주석을 위한 convention이 잘 지켜짐
  • 함수는 하나의 일을 하도록 구현
  • 중복을 최대한 피한다
  • 오류를 일관성 있게 처리하고 디버깅이 용이한 로깅을 한다

어떻게 좋은 코드를 쓸 수 있을까?

  • 고급 문법을 이해하고 상황에 적합한 구문과 데이터 유형 선택
  • 오픈소스 코드를 보며 좋은 코드 익히기 (+로 소스에 기여하기)
  • chatGPT를 이용한 변수명을 만들 수도 있다.
  • DRY (Don't repeat yourself)
  • KISS (Keep it simple, stupid)
  • 구글 파이썬 스타일링 가이드 참고

코드리뷰를 왜 하는가?

  • 주니어 개발자에게 학습 기회를 줌
  • bug 발견으로 사고를 방지
  • 보안상 문제 가려내기
  • tracking
  • readable한 코드 유지하기

좋은 코드리뷰를 위해서는...

  • 받는 입장
    - 조금씩 자주 요청 (Unit test와 같이 요청시 best임)
    - 설명적인 주석 추가
    - 리뷰를 감정적으로 받아들이지 않기
  • 하는 입장
    - 코드 스타일보다는 코드 자체에 대해 이야기
    - 객관적이고 비판적이지 않은 어조로 리뷰
  • context를 아는 사람에게 코드리뷰 요청
  • 리뷰어가 꼭 senior일 필요는 없음
  • 가볍고 빠르게 리뷰
  • Readability가 가장 중요!

Unit test

  • 모듈의 특정 기능을 테스트
  • 특정 입력에 대해 원하는 값이 나오는가?
  • Test coverage : 모듈의 실행가능 경로 중 얼마나 cover하는지 나타낸다. 적어도 3개의 unit test가 필요하다 (if, else if, else)
  • Integration test : Unit test를 합쳐 테스트
  • Acceptance test : 부하를 줬을 때 견디는 지 테스트
  • UI test : Selenium을 사용하여 UI 액션을 테스트
  • Python의 경우 unittest라이브러리를 사용하여 Unit test를 한다.

😵 공부하면서 어려웠던 내용

profile
거친 돌이 다듬어져 조각이 되듯

0개의 댓글