ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Selenium 기초 및 활용 하기 04 - 구글 이미지 스크래핑
    빅데이터/Selenium 2022. 1. 11. 20:31

    유투버 '이수안컴퓨터연구소' 강의 참조

     

     

     

    • 구글에서 이미지를 크롤링 할 예정. 보통 이러한 페이지에서 이미지를 바로 다운로드하면 원본이 아닌 작은 사이즈로 받아 진다. 

     

    • 원본 이미지를 받으려면 원하는 이미지를 클릭 한 후 열리는 창에서 다시 우클릭으로 받아야 받아진다. 

     

    • 또한 구글 이미지 같은 경우는 페이지로 나뉘는게아니라 스크롤을 계속 내리면서 업데이트 되는 형식이다. 스크롤을 일정 이상 내리면 더보기라는 버튼이 뜨는데 이 또한 클릭을 해줘야 계속해서 진행이 가능 하다

    계속 내리다보면 결과가 없다고 나온다.

     

     

    * 기본 세팅

    import selenium
    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.common.by import By
    import time
    import pandas as pd
    #셀레니엄 예외처리용
    from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, ElementNotInteractableException  #(클릭시 없을때, 엘리멘트 자체가 없을떄, 엘리멘트가 상호작용을 못할때 )
    import os #이미지 파일 다운로드 제어 용
    import socket #소켓 에러 방지용
    from urllib.request import urlretrieve #이미지 다운로드 라이브러리
    from urllib.error import HTTPError, URLError #각종 에러 방지 라이브러리 (HTTP/URL 에러 방지)
    from PIL import Image # 이미지를 사용가능하도록 처리 하는 라이브러리
    
    #웹브라우저를 띄우지 않고 진행하기 위한 설정
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    • PIL가 되지 않으면 따로 설치를 해줘야 한다.
      pip install Pillow

     

     

     

     

    01 스크래핑 시작전 세팅

    • 소켓통신을 통하여 이미지 스크래핑을 진행 할 예정인데 너무 오래 걸릴시에는 정지하도록 세팅
    socket.setdefaulttimeout(30)
    • 이미지 스크래핑 전/후로 저장 공간이 필요하므로 위치를 잡아 줘야 한다. 
    socket.setdefaulttimeout(30)#소켓 통신 시간 제어
    
    wd = webdriver.Chrome('chromedriver', options=chrome_options)
    
    scraped_count = 0 #이미지 스크래핑 개수 카운트 용
    
    path = './' #현재 사용하는 위치에서 검색어 폴더를 만들고 이미지 다운로드 용
    query = input('검색어 입력: ')
    
    dir_name = path + query
    os.makedirs(dir_name) # 'dir_name' 으로 폴더 생성
    
    print(f"[{dir_name} 디렉토리 생성]")
    
    scraping(dir_name, query) #실행 될 함수

     

     

     

    02 페이지 훑어 보기

    도구를 눌러 이미지 사이즈는 큰것만 나오도록 하고 그 url을 쓴다

     

    ▶ URL 주소를 수정하여 파라미터로 넘어오는값으로 검색이 되도록 한다.

    https://www.google.com/search?q=sea&tbm=isch&hl=en&tbs=isz:l&rlz=1C1CHZN_koKR971KR971&sa=X&ved=0CAIQpwVqFwoTCIC5mMe9qfUCFQAAAAAdAAAAABAC&biw=942&bih=941
    
    - 위의 주소를 보면 'sea'가 주소에 들어가고 이 'sea'가 검색이 되는것을 볼 수 있다. 이를 파라미터로 받자
    
    url = f'https://www.google.com/search?q={query}&tbm=isch&hl=en&tbs=isz:l&rlz=1C1CHZN_koKR971KR971&sa=X&ved=0CAIQpwVqFwoTCIC5mMe9qfUCFQAAAAAdAAAAABAC&biw=942&bih=941'

     

     

    ▶ 구글 이미지 페이지 특성상 전체 화면이 아니면 스크롤이 늘어나는 형태이다. 즉 항상 전체화면에서 스크래핑이 되도록 세팅

    wd.get(url)
    wd.maximize_window() #전체화면으로 스크래핑

     

    ▶ 스크롤 제어

    def scraping(dir_name, query):
        global scraped_count
        
        url = f'https://www.google.com/search?q={query}&tbm=isch&hl=en&tbs=isz:l&rlz=1C1CHZN_koKR971KR971&sa=X&ved=0CAIQpwVqFwoTCIC5mMe9qfUCFQAAAAAdAAAAABAC&biw=942&bih=941'
        
        wd.get(url)
        wd.maximize_window() #전체화면으로 스크래핑
        
        scroll_down()
    def scroll_down():
        scroll_count = 0
        pritn('[스크롤 다운 함수 시작!]')
        
        #스크롤을 내릴려면 위치값이 필요하다. 'execute_script()' 를 통해 스크롤 위치값(Height)을 가져 올 수 있음
        last_height = wd.execute_script("return document.body.scrollHeight")
        after_click = False #스크롤을 계속내리다가 '더 보기'가 나오는지 체크용
        
        
        while True:
            print(f"[스크롤 다운 중: {scroll_count}]")
            # 'scrollTo()' 함수를 써서 0부터 최대 위치값까지 스크롤을 함
            wd.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            scroll_count += 1 #스크롤이 최대로 갈때마다 카운트
            time.sleep(1) #JS 액션이 실행되고 반응이 될때까지 기다릴 시간
            
            new_height = wd.execute_script("return document.body.scrollHeight") # 최대값에 도달하면 스크롤이 다시 생성되니 거기서 다시 최대값 구함
            
            if last_height == new_height: #스크롤이 더이상 되지 않다면
                if after_click is True: #'더 보기'가 나온다면..
                    break
                else:
                    try:
                        more_button = wd.find_element(By.XPATH, '//*[@id="islmp"]/div/div/div/div[1]/div[2]/div[2]/input')
                        if more_button.is_displayed(): # '더 보기' 버튼이 나오면
                            more_button.click()
                            time.sleep(1)
                            after_click = True
                    except NoSuchElementException as e:
                        print(e)
                        break
                        
            last_height = new_height

     

     

     

    03 1차 시도

    모든 이미지가 <div> 태그에 담겨 있다. XPATH로 가져 온 후 각각의 이미지를 담아 보자
    각각의 이미지 태그 부분 서치 후 'Copy Selector' 

    def scraping(dir_name, query):
        global scraped_count
        
        url = f'https://www.google.com/search?q={query}&tbm=isch&hl=en&tbs=isz:l&rlz=1C1CHZN_koKR971KR971&sa=X&ved=0CAIQpwVqFwoTCIC5mMe9qfUCFQAAAAAdAAAAABAC&biw=942&bih=941'
        
        wd.get(url)
        wd.maximize_window() #전체화면으로 스크래핑
        
        scroll_down()
        
        div = wd.find_element(By.XPATH, '//*[@id="islrg"]/div[1]')
        img_list = div.find_elements(By.CSS_SELECTOR, '.rg_i Q4LuWd')
        
        for index, img in enumerate(img_list):
            try:
                #이 함수로 저장 이름, 인덱스, 이미지, 이미지 총 개수를 파라미터로 넘긴다
                click_and_save(dir_name, index, img, len(img_list))
            except

    ▶ 위에서 언급했듯이 1차로 클릭하고 그 다음 저장이 이루어 진다. 이에 대한 함수가 필요

     

    열린 이미지 창에서 다시 검사를 해서 이 원본 이미지 src를 들고 와야 한다. Xpath로 접근 하자

    def click_and_save(dir_name, index, img, img_list_length):
        global scraped_count
        
        try:
            img.click() #이미지를 클릭
            wd.implicitly_wait(3) #클릭 후 로드 시간이 필요하므로 대기
            src = wd.find_element(By.XPATH, '//*[@id="Sva75c"]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[2]/div/a/img').get_attribute('src')
            if src.split('.')[-1] == 'png':
                urlretrieve(src, dir_name + '/' + str(scraped_count + 1) + '.png')
                print(f' {index+1} / {img_list_length} PNG 이미지 저장 완료!')
            else:
                urlretrieve(src, dir_name + '/' + str(scraped_count + 1) + '.jpg')
                print(f' {index+1} / {img_list_length} JPG 이미지 저장 완료!')
    
            scraped_count += 1
        
        except HTTPError as e:
            print(e)
            pass #PNG, JPG 이외는 다 제외

     

     

    • 이제 리스트 포문을 타면서 'click_and_save()' 함수를 타면 문제가 없지만 이미지 스크래핑은 수많은 예외가 존재 한다. 이를 처리 하자.
    def scraping(dir_name, query):
        global scraped_count
        
        url = f'https://www.google.com/search?q={query}&tbm=isch&hl=en&tbs=isz:l&rlz=1C1CHZN_koKR971KR971&sa=X&ved=0CAIQpwVqFwoTCIC5mMe9qfUCFQAAAAAdAAAAABAC&biw=942&bih=941'
        
        wd.get(url)
        wd.maximize_window() #전체화면으로 스크래핑
        
        scroll_down()
        
        div = wd.find_element(By.XPATH, '//*[@id="islrg"]/div[1]')
        img_list = div.find_elements(By.CSS_SELECTOR, '.rg_i Q4LuWd')
        
        for index, img in enumerate(img_list):
            try:
                #이 함수로 저장 이름, 인덱스, 이미지, 이미지 총 개수를 파라미터로 넘긴다
                click_and_save(dir_name, index, img, len(img_list))
            except ElementClickInterceptedException as e: # 클릭시 문제 발생하면 아래로
                print(e)
                wd.execute_script("window.scrllTo(0, window.scrollY + 100)") #스크롤을 다시 하도록
                time.sleep(1)
                click_and_save(dir_name, index, img, len(img_list))
            except NoSuchElementException as e:
                print(e)
                wd.execute_script("window.scrllTo(0, window.scrollY + 100)") #스크롤을 다시 하도록
                time.sleep(1)
                click_and_save(dir_name, index, img, len(img_list))
            except ConnectionResetError as e: #연결 문제는 pass
                print(e)
                pass
            except URLError as e: #URL 문제시 PASS
                print(e)
                pass
            except socket.timeout as e: #소켓 통신 에러 pass
                print(e)
                pass
            except soket.gaierror as e: #소켓 통신 address 에러시 pass
                print(e)
                pass
            except ElementNotInteractableException as e: #엘리멘트 호환이 되지 않을때 
                print(e)
                break
                
        try:
            print("[스크래핑 종료 (성공률: %.2f%%)]" % (scraped_count / len(img_list) * 100.0))
        except ZeroDivisionError as e: #성공률 계산시 0이 나오면 패스
            print(e)
            pass
        
        wd.quit()
Designed by Tistory.