셀레니움 웹드라이버

JOOYEUN SEO·2024년 10월 5일

100 Days of Python

목록 보기
48/76
post-thumbnail

Selenium Webdriver

  • 상급 수준의 웹 스크래핑 도구
  • 뷰티풀 수프로는 한계가 존재
    • 브라우저의 기능을 실제로 사용할 수 없음
    • 웹사이트가 자바스크립트나 앵귤러, 리액트로 렌더링되면 문제 발생
    • HTML 콘텐츠를 불러오는 데 오래 걸리거나 특정 조건 필요
  • 브라우저를 자동화하여 자동으로 작업을 처리하도록 만듦
    • 입력, 클릭, 스크롤 등 가능
    • 로봇을 만들어서 브라우저에서 해야 할 일을 지시하는 것과 같음
  • 반복적이고 지루한 일을 맡길 때 좋은 기능

✅ 셀레니움 설치&설정

  1. Chrome 브라우저 설치 (개발자 도구를 사용할 수 있기 때문에 추천)
  2. selenium 패키지 임포트 후 설치
  3. 설치한 셀레늄 패키지에서 webdrive 모듈 임포트
    a. 셀레늄 패키지에 브라우저와 상호작용할 수 있는 코드 존재
    b. 크롬 드라이버 : 셀레늄 코드가 크롬 브라우저와 소통하도록 연결하는 다리
    (다른 브라우저도 가능)
from selenium import webdriver

# 프로그램이 종료되어도 브라우저를 열린 상태로 유지하는 설정
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)

# (크롬) 드라이버 생성
driver = webdriver.Chrome()

# 드라이버를 사용하여 브라우저 오픈
driver.get("https://www.amazon.com")

# 드라이버를 사용하여 브라우저 종료
driver.close()	# 특정 페이지가 열려 있는 현재 활성 탭 하나를 종료
driver.quit()	# 브라우저 전체를 종료(추천)

❖ 셀레니움으로 웹사이트에서 요소를 찾고 선택

Locator strategies 에서 요소를 찾는 다양한 방법 참고 가능

  1. from selenium.webdriver.common.by import By 임포트
    (셀레니움 패키지의 웹드라이버 내부에 있는 common 폴더에 포함된 By 클래스)
  2.  driver.find_element( By.locator, value=str ) : 조건에 맞는 첫 번째 요소 하나를 반환하는 함수
     driver.find_elements( By.locator, value=str ) : 조건에 맞는 모든 요소를 리스트로 반환하는 함수

◇ 속성으로 요소 찾기

태그 안의 class name, id, name 등의 속성으로 요소 찾기

…
driver.get("https://www.python.org")

# name 속성의 값으로 요소 찾기 -> 셀레니움 요소 반환
search_bar = driver.find_element(By.NAME, value="q")

# 반환된 요소에서 태그 이름만 가져오기
print(search_bar.tag_name)

# 반환된 요소에서 특정 속성의 값만 가져오기
print(search_bar.get_attribute("placeholder"))
input
Search
…
driver.get("https://www.python.org")

# ID로 요소 찾기
button = driver.find_element(By.ID, value="submit")

# 가져온 요소의 사이즈 확인
print(button.size)
{'height': 40, 'width': 46}

◇ CSS 선택자로 요소 찾기

해당 태그에 속성이 따로 없을 경우 유용한 방법

…
driver.get("https://www.python.org")

# CSS 선택자로 원하는 요소 찾아가기
documentation_link = driver.find_element(By.CSS_SELECTOR,
                                         value=".small-widget.documentation-widget a")

# 찾아온 요소에서 텍스트 추출하기
print(documentation_link.text)
docs.python.org

◇ XPath로 요소 찾기

CSS 선택자로도 원하는 것을 찾기 힘들 경우 사용

  • HTML에서 경로를 통해 요소를 찾는 방법
  • 트리에서 특정 노드까지의 경로를 표현
  • ❗️XPath 안에는 큰따옴표가 포함되어 있으므로 value 설정 시 작은따옴표 사용하기
…
driver.get("https://www.python.org")

# XPath로 원하는 요소 찾기 (복사한 값 붙여넣기)
bug_link = driver.find_element(By.XPATH, value='//*[@id="site-map"]/div[2]/div/ul/li[3]/a')

# 찾아온 요소에서 텍스트 추출하기
print(bug_link.text)
Submit Website Bug

❖ 스크래핑한 데이터를 딕셔너리로 만들기

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

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)
driver.get("https://www.python.org")

# 이벤트 시간과 이름 추출
event_times = driver.find_elements(By.CSS_SELECTOR, value=".event-widget time")
event_names = driver.find_elements(By.CSS_SELECTOR, value=".event-widget li a")

# 딕셔너리 생성
events = {}
for n in range(len(event_times)):
    # 이벤트의 키는 숫자 n(0에서 4까지), 값은 시간 키와 이름 키를 가진 딕셔너리
    events[n] = {
        "time": event_times[n].text,
        "name": event_names[n].text
    }

pprint.pp(events)
{0: {'time': '2024-10-04', 'name': 'PyCon ES 2024'},
 1: {'time': '2024-10-04', 'name': 'Django Day Copenhagen 2024'},
 2: {'time': '2024-10-05', 'name': 'Django Girls Ecuador 2024'},
 3: {'time': '2024-10-07', 'name': 'Creating Python Communities - Buea'},
 4: {'time': '2024-10-09', 'name': 'PyCon Uganda 2024'}}

❖ 셀레니움으로 양식 작성 및 버튼 클릭을 자동화

from selenium import webdriver
from selenium.webdriver.common.by import By
# 키보드의 키를 누르려면 Keys 클래스 임포트
from selenium.webdriver.common.keys import Keys

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)
driver.get("http://secure-retreat-92358.herokuapp.com/")

# First name, Last name, Email 주소를 입력하는 창 찾기
first_name = driver.find_element(By.NAME, value="fName")
last_name = driver.find_element(By.NAME, value="lName")
email = driver.find_element(By.NAME, value="email")

# 가입 버튼 찾기
button = driver.find_element(By.CSS_SELECTOR, value="form button")

# 입력창에 자동으로 문자열 입력하기
first_name.send_keys("jy")
last_name.send_keys("Seo")
email.send_keys("example@gmail.com")

# 두 가지 방법 중 하나 선택
## 1. 입력창에서 자동으로 엔터 키 누르기
email.send_keys(Keys.ENTER)

## 2. 버튼을 자동으로 클릭하기
button.click()

🗂️ 아마존 가격 추적기 코드 단축

Day47 프로젝트: 아마존 가격 추적기 코드를 셀레니움으로 훨씬 간단하게 변경

⌨️ main.py

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

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)
driver.get("https://www.amazon.com/Aeropress-Clear-Coffee-Press-bitterness/dp/B0C3KDC3FJ/?_encoding=UTF8&pd_rd_w=jAf2Q&content-id=amzn1.sym.0e739659-7c9d-4d82-868c-90015618ffcc&pf_rd_p=0e739659-7c9d-4d82-868c-90015618ffcc&pf_rd_r=JPF0B5X1M7759S2ZZV83&pd_rd_wg=6hyiv&pd_rd_r=6dfbe5fb-5543-4062-800b-b8e50b6af1a4&ref_=pd_hp_d_btf_exports_top_sellers_unrec")

# 클래스 이름으로 요소찾기
price_dollar = driver.find_element(By.CLASS_NAME, value="a-price-whole")
price_cents = driver.find_element(By.CLASS_NAME, value="a-price-fraction")

# 찾은 요소에서 텍스트만 추출
print(f"The price is {price_dollar.text}.{price_cents.text}")

driver.quit()
The price is 49.95

🗂️ Day48 프로젝트: 쿠키 클리커 자동플레이 봇

웹 기반 게임 쿠키 클리커를 자동으로 플레이하는 프로그램

🔍 유의 사항

  • 클래식 버전으로 플레이 : https://orteil.dashnet.org/experiments/cookie/)
  • 셀레니움과 파이썬으로 최대한 빨리 쿠키를 클릭하는 봇 만들기
  • 우측 창에서 구매 가능한 업그레이드 아이템이 있는지 5초마다 확인
    • 업그레이드 비용과 가진 쿠키를 비교
    • 구매 가능할 경우 가장 비싼 아이템을 구매
  • 5분 간 플레이 후 봇을 멈추고 초당 쿠키 생산 개수를 출력
  • 알고리즘을 수정하면 더 효율적으로 작동하는 봇을 만들 수 있음
  • 주의점
    • 업그레이드 아이템의 id는 9개가 있지만, 가격은 8개까지만 존재
    • split("-"), strip() 사용 필요
    • 가격에 있는 쉼표를 replace(",", "") 로 제거하고 int형으로 바꿔야 함

⌨️ main.py

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

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)
driver.get("http://orteil.dashnet.org/experiments/cookie/")

# 브라우저 크기 축소(효율 높이기)
driver.set_window_size(800, 600)

# 쿠키 element (클릭)
cookie = driver.find_element(By.ID, value="cookie")

# 아이템 elements의 id 리스트 (클릭)
items = driver.find_elements(By.CSS_SELECTOR, value="#store div")
item_ids = [item.get_attribute("id") for item in items]

# 아이템 elements의 가격 리스트
all_prices = driver.find_elements(By.CSS_SELECTOR, value="#store b")
item_prices = []
for price in all_prices:
    element_text = price.text
    if element_text != "":
        price = int(element_text.split("-")[1].strip().replace(",", ""))
        item_prices.append(price)

# 키가 가격, 값이 id인 딕셔너리 생성
upgrades = {}
for n in range(len(item_prices)):
    upgrades[item_prices[n]] = item_ids[n]

timeout = time.time() + float(5)            # 현재 시간에서 5초 후 시간
five_mins = time.time() + float(60 * 1)     # 현재 시간으로부터 5분 후 시간 (5분 = 60초 * 5)

while True:
    cookie.click()

    # 매 5초마다 구매 가능한 아이템이 있는지 확인
    if time.time() > timeout:

        # 1. 현재 쿠키 개수(돈) 체크
        money = int(driver.find_element(by=By.ID, value="money").text.replace(",", ""))

        # 2. 현재 구매 가능한 아이템을 딕셔너리로 만들기
        affordable_upgrades = {}
        for price, id in upgrades.items():
            if money > price:
                affordable_upgrades[price] = id

        # 3. 구매 가능한 아이템이 있다면 그 중 가장 비싼 것 사기
        if len(affordable_upgrades) != 0:
            highest_price_affordable_upgrade = max(affordable_upgrades)
            to_purchase_id = affordable_upgrades[highest_price_affordable_upgrade]

            driver.find_element(By.ID, value=to_purchase_id).click()

        # 4. timeout을 현재 시간에서 5초 후로 변경
        timeout = time.time() + float(5)

    # 5분 후 프로그램 종료
    if time.time() > five_mins:
        break

# 초당 쿠키 생산량 확인하기
cookies_per_second = driver.find_element(By.ID, value="cps").text.split(":")[1].strip()
print(f"cookies/second is {cookies_per_second}")
cookies/second is 89.8

현재 업그레이드 가능한 아이템 중 가장 비싼 것을 사기 때문에 가장 싼 커서는 아예 사지 않게 됨
→ 업그레이드 확인 시간을 5초 이하로 낮추거나 구매 가능한 아이템을 전부 사는 방식으로 변경해보기




▷ Angela Yu, [Python 부트캠프 : 100개의 프로젝트로 Python 개발 완전 정복], Udemy, https://www.udemy.com/course/best-100-days-python/?couponCode=ST3MT72524

0개의 댓글