데이터 분석/웹 스크래핑

[Web Scrapping 실습] Selenium

eunnys 2023. 11. 1. 18:26

Selenium
- URL만으로 접근할 수 없는 홈페이지에 접근
- 크롤링이 목적이 아니라 만들어진 홈페이지를 테스트하고 웹 브라우저를 제어할 목적으로 사용
 
 
 관련 패키지 설치
- pip install selenium
- pip install webdriver_manager
 
 
## webdriver 실행하기 ##

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

# 웹 드라이버 동적 다운로드 방식
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

# 기 설치된 웹 드라이버 구동 방식
s = Service('c:\\DEV\\chromedriver\\chromedriver.exe') # 설치된 웹 드라이버의 경로를 지정
driver = webdriver.Chrome(service=s)

 
 
 
## find_element() 함수 ##

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

# 웹 드라이버 동적 다운로드 방식
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.daum.net')
ele = driver.find_element(by=By.LINK_TEXT, value='카페')
print(ele)
print(type(ele))
print(ele.text)

 

 
▶ 이벤트 제어하기
# click()

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By

# 웹 드라이버 동적 다운로드 방식
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.daum.net')
ele = driver.find_element(by=By.LINK_TEXT, value='카페') # 다음 홈페이지에 카페로 들어감
ele.click()

 
 
# send_keys()

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

# 웹 드라이버 동적 다운로드 방식
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.daum.net')
ele = driver.find_element(By.ID, 'q')
ele.send_keys('python') # 검색창에 'python' 검색
ele.send_keys(Keys.ENTER)

 
 
 
▶ selenium과 BeautifulSoup 연결하기
- 페이지의 내용을 자바스크립트를 이용해 동적으로 불러오는 경우에는 곧바로 BeautifulSoup을 이용할 경우 HTML 내용이 개발자 모드를 통해 보는 HTML 소소와 다를 수 있다.
- 이런 경우 selenium을 이용해서 페이지를 요청하고 sleep() 함수를 통해서 페이지가 로딩될 때까지 기다린 후 BeautifulSoup으로 스크래핑 한다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from bs4 import BeautifulSoup

# 웹 드라이버 동적 다운로드 방식
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.daum.net')
ele = driver.find_element(By.ID, 'q')
ele.send_keys('데이터 분석') 
ele.send_keys(Keys.ENTER)

bs = BeautifulSoup(driver.page_source, 'lxml') # 웹 드라이버를 통해서 얻은 소스 코드를 가져옴

 
 
 
[실습] 네이버 로그인하기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.naver.com')
ele = driver.find_element(By.CLASS_NAME, 'MyView-module__link_login___HpHMW')
ele.click() # 간혹 click() 이벤트가 적용되지 않는 경우에는 send_keys(Keys.ENTER)를 이용

id = 'myid'
pw = 'mypw'

# ele = driver.find_element(By.ID, 'id')
# ele.send_keys(id)
# ele = driver.find_element(By.ID, 'pw')
# ele.send_keys(pw)

# 로봇에 의해 클릭되지 못하도록 막았기 때문에 스크립트로 처리하여 로그인해야 된다

driver.execute_script(f'document.getElementById("id").value="{id}"')
driver.execute_script(f'document.getElementById("pw").value="{pw}"')

ele = driver.find_element(By.CLASS_NAME, 'btn_login')
ele.click()

 
 
 
[실습] 구글에서 이미지 검색 후 스크래핑 하기
[step-1] 구글에서 이미지 검색 후 검색 결과 6번 스크롤 하기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 페이지가 로드될 때까지 기다리는 시간
SCROLL_PAUSE_TIME = 1


driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.google.com')
ele = driver.find_element(By.CLASS_NAME, 'gLFyf')
ele.send_keys('python') # 검색창에 python 검색
ele.submit()

# 이미지 검색 결과로 이동
driver.find_element(By.LINK_TEXT, '이미지').click()
# 페이지가 로드될 때까지 기다림
time.sleep(SCROLL_PAUSE_TIME)

last_height = driver.execute_script('return document.body.scrollHeight') # 스크롤바의 높이
print('last height: ', last_height) # 3751
for i in range(6):
    # 윈도우 창의 스크롤바를 0에서부터 가장 밑(scrollHeight값)까지 이동
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    time.sleep(SCROLL_PAUSE_TIME)
    new_height = driver.execute_script('return document.body.scrollHeight')
    print('new height: ', new_height)
    print('-'*30)

# 더 이상 스크롤 될 페이지가 없을 경우 scrollHeight 값의 변화가 없다
last height:  3751
new height:  7874
------------------------------
new height:  15872
------------------------------
new height:  23386
------------------------------
new height:  30658
------------------------------
new height:  30739
------------------------------
new height:  30739
------------------------------

 
 
[step-2] 구글에서 이미지 검색 후 검색 결과 스크롤 한 후 '결과 더보기' 클릭하기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 페이지가 로드될 때까지 기다리는 시간
SCROLL_PAUSE_TIME = 1


driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.google.com')
ele = driver.find_element(By.CLASS_NAME, 'gLFyf')
ele.send_keys('python') # 검색창에 python 검색
ele.submit()

# 이미지 검색 결과로 이동
driver.find_element(By.LINK_TEXT, '이미지').click()
# 페이지가 로드될 때까지 기다림
time.sleep(SCROLL_PAUSE_TIME)

last_height = driver.execute_script('return document.body.scrollHeight') # 스크롤바의 높이

# 스크롤 횟수
scroll_cnt = 0

while True:
    # 윈도우 창의 스크롤바를 0에서부터 가장 밑(scrollHeight값)까지 이동
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    
    scroll_cnt += 1
    
    time.sleep(SCROLL_PAUSE_TIME)
    new_height = driver.execute_script('return document.body.scrollHeight')
    print(f'last height:{last_height}, new height:{new_height}, scroll count:{scroll_cnt}')

    if last_height != new_height: # 계속헤서 new_height 값이 변경 된다면...
        last_height = new_height
    else: # 더 이상 스크롤 될 페이지가 없으면...
        ele = driver.find_element(By.CSS_SELECTOR, '#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input')
        # 해당 element와 상호작용이 불가능한 상태면 ElementNotInteractableException 예외가 발생
        try:
            ele.click()
            print('결과 더보기 클릭')
        except:
            break

 
 
[step-3] 구글에서 이미지 검색 후 검색 결과 스크롤 한 후 검색 결과 이미지의 src값(실제 이미지가 저장되어 있는 주소값) 수집하기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from bs4 import BeautifulSoup


# 페이지가 로드될 때까지 기다리는 시간
SCROLL_PAUSE_TIME = 1
# 이미지 추출 개수
IMAGE_EXTRACT_NUM = 20
# 찾고싶은 키워드
SEARCH_KEYWORD = 'python'


driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.google.com')
ele = driver.find_element(By.CLASS_NAME, 'gLFyf')
ele.send_keys('python') # 검색창에 python 검색
ele.submit()

# 이미지 검색 결과로 이동
driver.find_element(By.LINK_TEXT, '이미지').click()

# 페이지가 로드될 때까지 기다림
time.sleep(SCROLL_PAUSE_TIME)

# 스크롤바의 높이
last_height = driver.execute_script('return document.body.scrollHeight') 

# 스크롤 횟수
scroll_cnt = 0

while True:
    # 윈도우 창의 스크롤바를 0에서부터 가장 밑(scrollHeight값)까지 이동
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    
    scroll_cnt += 1
    
    time.sleep(SCROLL_PAUSE_TIME)
    new_height = driver.execute_script('return document.body.scrollHeight')
    print(f'last height:{last_height}, new height:{new_height}, scroll count:{scroll_cnt}')

    if last_height != new_height: # 계속헤서 new_height 값이 변경 된다면...
        last_height = new_height
    else: # 더 이상 스크롤 될 페이지가 없으면...
        ele = driver.find_element(By.CSS_SELECTOR, '#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input')
        # 해당 element와 상호작용이 불가능한 상태면 ElementNotInteractableException 예외가 발생
        try:
            ele.click()
            print('결과 더보기 클릭')
        except:
            break

####### 이미지를 선택하고 해당 이미지의 src를 저장 #######
# 검색된 첫 번째 이미지 선택 : CSS_SELECTOR 값을 복사

imgs = driver.find_elements(By.CSS_SELECTOR, '#islrg > div.islrc > div a.FRuiCf.islib.nfEiy')
# div = 일종의 영역을 나타내는 태그 
# a = 링크 태그 (원본이미지에 대한 접근이 가능)

print('이미지 개수: ', len(imgs))

img_cnt = 0 
# 수집된 이미지의 src값을 리스트에 저장
img_src_list = []

for img in imgs:
    img.send_keys(Keys.ENTER)  # 또는 img.click()
    # 검색 이미지 클릭 후 오른쪽에 크게 보여지는 원본 이미지의 CSS_SELECTOR 값을 수집
    try: # try~except를 사용해서 CSS_SELECTOR 값이 다르면 넘어감
        img_src = driver.find_element(By.CSS_SELECTOR, '#Sva75c > div.A8mJGd.NDuZHe.CMiV2d.OGftbe-N7Eqid-H9tDt > div.dFMRD > div.AQyBn > div.tvh9oe.BIB1wf.hVa2Fd > c-wiz > div > div > div > div.v6bUne > div.p7sI2.PUxBg > a > img').get_attribute('src')  # src 속성값을 가져옴
    except:
        continue
        
    if not img_src.startswith('http'): # 가지고 온 주소가 http로 시작하지 않으면 건너띔
        continue
    img_cnt += 1
    print(f'[{img_cnt}].{img_src}')
    img_src_list.append(img_src)
    if img_cnt == IMAGE_EXTRACT_NUM: break  # 20개가 되면 멈춤

 
 
[step-4] 구글에서 이미지 검색 후 검색 결과 스크롤 한 후 검색 결과 이미지의 src값(실제 이미지가 저장되어 있는 주소값) 수집후 이미지 파일로 저장하기

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
from bs4 import BeautifulSoup


# 페이지가 로드될 때까지 기다리는 시간
SCROLL_PAUSE_TIME = 1
# 이미지 추출 개수
IMAGE_EXTRACT_NUM = 20
# 찾고싶은 키워드
SEARCH_KEYWORD = 'python'


driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=webdriver.ChromeOptions())

driver.get('https://www.google.com')
ele = driver.find_element(By.CLASS_NAME, 'gLFyf')
ele.send_keys('python') # 검색창에 python 검색
ele.submit()

# 이미지 검색 결과로 이동
driver.find_element(By.LINK_TEXT, '이미지').click()

# 페이지가 로드될 때까지 기다림
time.sleep(SCROLL_PAUSE_TIME)

# 스크롤바의 높이
last_height = driver.execute_script('return document.body.scrollHeight') 

# 스크롤 횟수
scroll_cnt = 0

while True:
    # 윈도우 창의 스크롤바를 0에서부터 가장 밑(scrollHeight값)까지 이동
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    
    scroll_cnt += 1
    
    time.sleep(SCROLL_PAUSE_TIME)
    new_height = driver.execute_script('return document.body.scrollHeight')
    print(f'last height:{last_height}, new height:{new_height}, scroll count:{scroll_cnt}')

    if last_height != new_height: # 계속헤서 new_height 값이 변경 된다면...
        last_height = new_height
    else: # 더 이상 스크롤 될 페이지가 없으면...
        ele = driver.find_element(By.CSS_SELECTOR, '#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input')
        # 해당 element와 상호작용이 불가능한 상태면 ElementNotInteractableException 예외가 발생
        try:
            ele.click()
            print('결과 더보기 클릭')
        except:
            break

####### 이미지를 선택하고 해당 이미지의 src를 저장 #######
# 검색된 첫 번째 이미지 선택 : CSS_SELECTOR 값을 복사

imgs = driver.find_elements(By.CSS_SELECTOR, '#islrg > div.islrc > div a.FRuiCf.islib.nfEiy')
# div = 일종의 영역을 나타내는 태그 
# a = 링크 태그 (원본이미지에 대한 접근이 가능)

print('이미지 개수: ', len(imgs))

img_cnt = 0 
# 수집된 이미지의 src값을 리스트에 저장
img_src_list = []

for img in imgs:
    img.send_keys(Keys.ENTER)  # 또는 img.click()
    # 검색 이미지 클릭 후 오른쪽에 크게 보여지는 원본 이미지의 CSS_SELECTOR 값을 수집
    try:
        img_src = driver.find_element(By.CSS_SELECTOR, '#Sva75c > div.A8mJGd.NDuZHe.CMiV2d.OGftbe-N7Eqid-H9tDt > div.dFMRD > div.AQyBn > div.tvh9oe.BIB1wf.hVa2Fd > c-wiz > div > div > div > div.v6bUne > div.p7sI2.PUxBg > a > img').get_attribute('src')  # src 속성값을 가져옴
    except:
        continue
        
    if not img_src.startswith('http'): # 가지고 온 주소가 http로 시작하지 않으면 건너띔
        continue
    img_cnt += 1
    print(f'[{img_cnt}].{img_src}')
    img_src_list.append(img_src)
    if img_cnt == IMAGE_EXTRACT_NUM: break  # 20개가 되면 멈춤


####### 검색 이미지 파일로 저장 #######
import os
import requests


now = time.localtime() # 현재 시스템의 시간
path = 'c:/Temp/' # c드라이브 밑에 Temp 폴더를 만들어서 저장
folder_name = f'{now.tm_year}.{now.tm_mon}.{now.tm_mday}.{now.tm_hour}.{now.tm_min}.{now.tm_sec}'
directory = SEARCH_KEYWORD+'/'+folder_name+'/'   # 검색 키워드마다 폴더가 만들어짐
os.chdir(path) # 경로 이동
if not os.path.exists(directory): # 폴더가 존재하지 않으면 새로 만들어줌
    os.makedirs(directory)

file_no = 1 # 파일 번호
os.chdir(path+directory)

# 원본 이미지의 확장자를 먼저 확인, 없으면 임의로 붙여줌
for url in img_src_list: # 리스트에 있는 원본 url을 하나씩 가져옴
    extension = url.split('.')[-1] # .으로 구분하고 가장 마지막에 있는 걸 가져옴 (확장자)
    ext = ''
    if extension in ['jpg', 'JPG', 'jpeg', 'JPEG', 'png', 'PNG', 'gif', 'GIF']:
        ext = '.'+extension
    else:
        ext = '.jpg'

    file_name = str(file_no)+'-'+SEARCH_KEYWORD+ext  # 파일 번호는 정수형이기 때문에 문자열로 바꿔줌

    file_no += 1
    res = requests.get(url)  # 원본 이미지의 url 요청
    with open(file_name, 'wb') as f:
        f.write(res.content)

driver.close() # 열려있는 웹브라우저를 닫아줌