본문 바로가기
풀스택 개발 학습 과정/데이터

[데이터 수집] 셀레니움 - 2 (구글 이미지 실습)

by 육츠 2023. 11. 2.
Contents 접기

[실습] 구글에서 이미지 검색 후 스크래핑 하기

 

[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')
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)


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('-'*20)

# 더이상 스크롤 될때까지 페이지가 없을 경우 scrollHeight 값의 변화가 없다

#결과
# last height: 3509
# new_height: 7632
# --------------------
# new_height: 15630
# --------------------
# new_height: 22902
# --------------------
# new_height: 30658
# --------------------
# new_height: 30739
# --------------------
# new_height: 30739
# --------------------

구글 이미지에서의 스크롤은 사용자가 마우스 휠을 조작하여 페이지를 내리면 자동적으로 이미지가 더 생성 됨을 알 수 있다.
그 횟수가 6번임. (코드를 통해 확인)
return document.body.scrollHeight : 현재 스크롤된 페이지의 좌표
range를 6으로 설정해서 6번 for문을 돌리게 한다. -> 결과를 통해 5번째의 new_height 와 6번째 new_height 가 같음을 알 수 있다.

 

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

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: # 더이상 new_height 값에 변동이 없다면 
        ele = driver.find_element(By.CSS_SELECTOR, '#islmp > div > div > div > div > div.C5Hr4 > div.K414Oe > div.FAGjZe > input')
        # 해당 엘리먼트와 상호 작용이 불가한 상태면 ElementNotInteractableException 발생된다.
        try:
            ele.click()
            print('결과 더보기 클릭')
        except:
        # except ElementNotInteractableException:
            break
   
# ...
# last height:96576, new_height: 96576, scroll count: 6
# 결과 더보기 클릭
# last height:96576, new_height: 120702, scroll count: 7
# last height:120702, new_height: 126594, scroll count: 8
# last height:126594, new_height: 126594, scroll count: 9

처음 측정한 마우스의 높이 값과 나중에 측정한 마우스의 높이 값이 같아지기 전 까지 무한 반복을 진행한다.
step-2 에서는 마지막까지 움직이는 마우스 스크롤의 횟수만 측정한다. 

 

[step-3] 구글에서 이미지 검색 후 검색 결과 스크롤한 후 검색 결과 이미지의 src값 수집하기

####### 이미지 선택 & 해당 이미지 src 저장
# 검색된 첫 번째 이미지 선택 : CSS_SELECTOR값 복사
# imgs = driver.find_elements(By.CSS_SELECTOR,'#islrg > div.islrc > div:nth-child(2) > a.FRuiCf.islib.nfEiy') 
imgs = driver.find_elements(By.CSS_SELECTOR,'#islrg > div.islrc > div a.FRuiCf.islib.nfEiy') 
# div 밑에 a 태그 에 class 를 찾아라
print('이미지 개수:' , len(imgs))

img_cnt = 0
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 가져옴
    #img_src = driver.find_element(By.XPATH,'//*[@id="Sva75c"]/div[2]/div[2]/div[2]/div[2]/c-wiz/div/div/div/div[3]/div[1]/a/img').get_attribute('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
    

# last height:38167, new_height: 40187, scroll count: 8
# last height:40187, new_height: 40187, scroll count: 9
# 이미지 개수: 522
# [1].https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR_T42J85DGNpIirviSA8tsYzFUDTUiE67nGQ&usqp=CAU
# [2].https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRvfuMlFb2C-OyEJLFe7cyIY0d0CgVq1PwlPA&usqp=CAU
# [3].https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQJofIm8qsVZLaSM8PSQEX80V6EWS9O9cBg-A&usqp=CAU
# ...

스텝-3은 이미지를 다운 받아오기 전 정확하게 이미지가 선택되는지 확인하는 과정이다.
이 과정에서 구글 이미지의 이미지 속성값이 계속 에러가 났었다. 해당 에러는 구글의 크롤링 방지를 위해, 이미지마다 다른 CSS 속성값을 가져 반복문을 도는 과정에 에러가 난 것 이었다.
try ~ except: 구문을 통해 같은 이미지 속성 값을 가지는 이미지만 20개를 받아온 것을 알 수 있다.
또한 http 가 포함된 링크를 가진 것을 받아왔다.

 

[step-4] 구글에서 이미지 검색 후 검색 결과 스크롤한 후 검색 결과 이미지 저장

import os
import requests

# 이미지마다 파일 확장자가 다르다
# 시스템의 시간을 가지고 폴더에 동적으로저장

now = time.localtime()
path = '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:
    extension = url.split('.')[-1]
    ext = ''
    if extension in ['jpg','JPG','jpeg','png','PNG','gif','GIF']:
        ext = '.'+extension
    else:
        ext = '.jpg' # 확장자가 없다면 일괄적으로 jpg를 붙임
    file_name = str(file_no)+'-'+SEARCH_KEYWORD+ext

    file_no += 1
    res = requests.get(url)
    with open(file_name,'wb') as f:
        f.write(res.content)

driver.close()

Temp 라는 임시의 폴더를 생성 후 폴더이름은 크롤링 진행 중에 내용이 엉키지 않도록 크롤링한 년.월.일.시.분.초 를 적어준다. 
또한 다운받을 이미지에는 확장자가 다양하고, 예외로 확장자가 없는 파일도 존재하므로 확장자명을 검색하고,
확장자명이 없다면 일괄적으로 .jpg 를 부여한다.