본문 바로가기

Data handling/Web crawling

[selenium] python, 구글 이미지 크롤링하기 (원본화질, 고화질)

728x90

전체코드

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import os

options = webdriver.ChromeOptions()
options.add_argument('headless')
options.add_argument('window-size=1920x1080')
options.add_argument('disable-gpu')
options.add_argument('User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36')
options.add_argument('window-size=1920x1080')
options.add_argument('ignore-certificate-errors')


import os
import time
import socket

from urllib.request import urlretrieve
from urllib.error import HTTPError, URLError
from selenium import webdriver
from selenium.common.exceptions import ElementClickInterceptedException, NoSuchElementException, ElementNotInteractableException
from PIL import Image
from pygame import mixer

from urllib.error import HTTPError, URLError
from selenium import webdriver

def scroll_down():
    scroll_count = 0

    print("ㅡ 스크롤 다운 시작 ㅡ")

    # 스크롤 위치값 얻고 last_height 에 저장
    last_height = driver.execute_script("return document.body.scrollHeight")

    # 결과 더보기 버튼을 클릭했는지 유무
    after_click = False

    while True:
        print(f"ㅡ 스크롤 횟수: {scroll_count} ㅡ")
        # 스크롤 다운
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        scroll_count += 1
        time.sleep(1)

        # 스크롤 위치값 얻고 new_height 에 저장
        new_height = driver.execute_script("return document.body.scrollHeight")

        # 스크롤이 최하단이며
        if last_height == new_height:

            # 결과 더보기 버튼을 클릭한적이 있는 경우
            if after_click is True:
                print("ㅡ 스크롤 다운 종료 ㅡ")
                break

            # 결과 더보기 버튼을 클릭한적이 없는 경우
            if after_click is False:
                if driver.find_element_by_xpath('//*[@id="islmp"]/div/div/div/div/div[5]/input').is_displayed():
                    driver.find_element_by_xpath('//*[@id="islmp"]/div/div/div/div/div[5]/input').click()
                    after_click = True
                elif NoSuchElementException:
                    print("ㅡ NoSuchElementException ㅡ")
                    print("ㅡ 스크롤 다운 종료 ㅡ")
                    break

        last_height = new_height


def click_and_retrieve(index, img, img_list_length):
    global crawled_count
    try:
        img.click()
        time.sleep(1.5)
        driver.implicitly_wait(3)
        src = None
        _format = None
        try:
            srces = driver.find_elements_by_class_name("""n3VNCb""")
            for _src in srces:
                src = _src.get_attribute('src')
                _format = src.split('.')[-1]
                if any(map(lambda x: _format == x, ['jpg', 'jpeg', 'png'])):
                    break
                else:
                    continue
            if not any(map(lambda x: _format == x, ['jpg', 'jpeg', 'png'])):
                print('no jpg.... pass.')
                return
        except:
            print('error2')
        # src.split('.')[-1] = 확장자
        if src is None or _format is None:
            return
        urlretrieve(src, f"{query}/{date}/{crawled_count + 1}.{_format}")
        driver.implicitly_wait(3)
        print(f"{index + 1} / {img_list_length} 번째 사진 저장 (png)")
        crawled_count += 1

    except HTTPError:
        print("ㅡ HTTPError & 패스 ㅡ")
        pass


def crawling():
    global crawled_count
    print("ㅡ 크롤링 시작 ㅡ")

    # 이미지 고급검색 중 이미지 유형 '사진'
    url = f"https://www.google.com/search?as_st=y&tbm=isch&hl=ko&as_q={query}&as_epq=&as_oq=&as_eq=&cr=&as_sitesearch=&safe=images&tbs=itp:photo"
    driver.get(url)
    # driver.maximize_window()
    scroll_down()

    div = driver.find_element_by_xpath('//*[@id="islrg"]/div[1]')
    # class_name에 공백이 있는 경우 여러 클래스가 있는 것이므로 아래와 같이 css_selector로 찾음
    img_list = div.find_elements_by_css_selector(".rg_i.Q4LuWd")
    os.makedirs(os.path.join(query, date), exist_ok=True)
    print(f"ㅡ {query}/{date} 생성 ㅡ")

    for index, img in enumerate(img_list):
        try:
            click_and_retrieve(index, img, len(img_list))

        except ElementClickInterceptedException:
            print("ㅡ ElementClickInterceptedException ㅡ")
            driver.execute_script("window.scrollTo(0, window.scrollY + 100)")
            print("ㅡ 100만큼 스크롤 다운 및 3초 슬립 ㅡ")
            img.click()
            time.sleep(3)
            click_and_retrieve(index, img, len(img_list))

        except NoSuchElementException:
            print("ㅡ NoSuchElementException ㅡ")
            driver.execute_script("window.scrollTo(0, window.scrollY + 100)")
            print("ㅡ 100만큼 스크롤 다운 및 3초 슬립 ㅡ")
            time.sleep(3)
            img.click()
            click_and_retrieve(index, img, len(img_list))

        except ConnectionResetError:
            print("ㅡ ConnectionResetError & 패스 ㅡ")
            pass

        except URLError:
            print("ㅡ URLError & 패스 ㅡ")
            pass

        except socket.timeout:
            print("ㅡ socket.timeout & 패스 ㅡ")
            pass

        except socket.gaierror:
            print("ㅡ socket.gaierror & 패스 ㅡ")
            pass

        except ElementNotInteractableException:
            print("ㅡ ElementNotInteractableException ㅡ")
            break

    try:
        print("ㅡ 크롤링 종료 (성공률: %.2f%%) ㅡ" % (crawled_count / len(img_list) * 100.0))

    except ZeroDivisionError:
        print("ㅡ img_list 가 비어있음 ㅡ")

    driver.quit()


def filtering():
    print("ㅡ 필터링 시작 ㅡ")
    filtered_count = 0
    dir_name = os.path.join(query, date)
    for index, file_name in enumerate(os.listdir(dir_name)):
        try:
            file_path = os.path.join(dir_name, file_name)
            img = Image.open(file_path)

            # 이미지 해상도의 가로와 세로가 모두 350이하인 경우
            if img.width < 351 and img.height < 351:
                img.close()
                os.remove(file_path)
                print(f"{index} 번째 사진 삭제")
                filtered_count += 1

        # 이미지 파일이 깨져있는 경우
        except OSError:
            os.remove(file_path)
            filtered_count += 1

    print(f"ㅡ 필터링 종료 (총 갯수: {crawled_count - filtered_count}) ㅡ")


def checking():
    # 입력 받은 검색어가 이름인 폴더가 존재하면 중복으로 판단
    for dir_name in os.listdir(query):
        file_list = os.listdir(os.path.join(query, dir_name))
        if query in file_list:
            print(f"ㅡ 중복된 검색어: ({dir_name}) ㅡ")
            return True


def playing_mp3():
    mp3 = "Mococo_Seed.mp3"
    mixer.init()
    mixer.music.load(mp3)
    mixer.music.play()
    while mixer.music.get_busy():
        pass
    print(f"ㅡ 검색어: {query} ㅡ")

수정한 부분

def click_and_retrieve(index, img, img_list_length):
    global crawled_count
    try:
        img.click()
        time.sleep(1.5)
        driver.implicitly_wait(3)
        src = None
        _format = None
        try:
            srces = driver.find_elements_by_class_name("""n3VNCb""")
            for _src in srces:
                src = _src.get_attribute('src')
                _format = src.split('.')[-1]
                if any(map(lambda x: _format == x, ['jpg', 'jpeg', 'png'])):
                    break
                else:
                    continue
            if not any(map(lambda x: _format == x, ['jpg', 'jpeg', 'png'])):
                print('no jpg.... pass.')
                return
        except:
            print('error2')
        # src.split('.')[-1] = 확장자
        if src is None or _format is None:
            return
        urlretrieve(src, f"{query}/{date}/{crawled_count + 1}.{_format}")
        driver.implicitly_wait(3)
        print(f"{index + 1} / {img_list_length} 번째 사진 저장 (png)")
        crawled_count += 1

    except HTTPError:
        print("ㅡ HTTPError & 패스 ㅡ")
        pass

xpath로 클릭시 이미지 접근이 되지 않아, (디버깅 상에선 분명 됐는데도 불구하고) class name으로 접근하게 했다. class name이 2~3개로 중복되는 것도 있어서 뒷 자리 확장자가 이미지일 경우만 저장하도로 했다.

고화질 이미지의 경우 click 이후에 기다리는 시간이 있어야만 보인다. 1.5초 이상 기다리면 완벽히 저장된다. 2초 정도 기다리면 좋지 않을까 싶다.

참고

hanryang1125.tistory.com/5

 

Selenium을 이용하여 구글 이미지 크롤링 (Python)

import os import time import socket from urllib.request import urlretrieve from urllib.error import HTTPError, URLError from selenium import webdriver from selenium.common.exceptions import ElementC..

hanryang1125.tistory.com

크롤링 동영상