소스 공유

|  나만의 소스나 나만 알고 있는 좋은 소스를 서로 공유하는 자리입니다.

조회 수 218 추천 수 0 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
Extra Form
라이선스 기타(따로 작성)

안녕하세요?


구글 커스텀 서치가 제공되고 있지만, 아쉽게도 제한되는 부분이 많아서 구글 이미지 서치 결과를 받아오는 일종의 커스텀 API를 만들어보았습니다.


Requests로 구현할 수 있으면 좋겠지만, 자바스크립트로 렌더링되는 부분이 많고 아직 제 실력이 부족한 관계로 부득이 Selenium을 사용했습니다.


GET 요청에 대한 응답을 바로 BeautifulSoup에 넣는 것은 구글에서 난독화(?)로 막아놓은 것 같아서 div.rg_meta에 접근하는 방법으로 구현하였습니다.


작성한 함수에 검색어와 검색할 이미지 수를 입력하면, (1) 타이틀, (2) 이미지 URL, (3) 이미지 확장자, (4) 본문 URL을 반환합니다.


머신러닝이나 커뮤니티 사이트 운영을 위해 특정 테마 또는 확장자의 이미지를 다량으로 수집해야 하는 경우에 유용할 것 같습니다.



from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
import time, json

options = Options()
#options.headless = True
driver = webdriver.Firefox(options=options)
driver.implicitly_wait(5)

def search(keyword, number):
    driver.get('https://www.google.com/webhp?hl=국가코드') # 검색할 국가코드를 넣으세요(ex. 미국 en, 일본 ja).
    WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '/html/body/div/div[4]/form/div[2]/div[1]/div[1]/div/div[2]/input')))
    elem = driver.find_element_by_xpath('/html/body/div/div[4]/form/div[2]/div[1]/div[1]/div/div[2]/input')
    elem.clear()
    elem.send_keys(keyword)
    elem.submit() # 입력한 검색어를 제출합니다.
    WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '/html/body/div[6]/div[3]/div[5]/div/div/div[1]/div/div/div[1]/div/div[2]/a')))
    driver.find_element_by_xpath('/html/body/div[6]/div[3]/div[5]/div/div/div[1]/div/div/div[1]/div/div[2]/a').click() # 이미지 탭으로 이동합니다.
    WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located)

    images = []
    while len(images) < number:
        images = driver.find_elements_by_xpath('//div[contains(@class,"rg_meta")]')
        height1 = driver.execute_script("return document.body.scrollHeight") # Body의 height를 측정합니다.
        time.sleep(0.5)
        driver.execute_script("window.scrollBy(0, 1000000)") # 브라우저 스크롤을 내립니다.
        time.sleep(1) ##### Ajax를 기다리는 부분을 EC로 어떻게 표현해야 적절할지 모르겠습니다 ㅠㅠ #####
        height2 = driver.execute_script("return document.body.scrollHeight")
        time.sleep(1)
        if height1 == height2: # 스크롤을 더 이상 내릴 수 없는 경우를 처리합니다.
            try:
                time.sleep(1) ##### Ajax를 기다리는 부분을 EC로 어떻게 표현해야 적절할지 모르겠습니다 ㅠㅠ #####
                driver.find_element_by_xpath('//*[@id="smb"]').click() # 결과 더보기가 나오면 클릭합니다.
                time.sleep(1) ##### Ajax를 기다리는 부분을 EC로 어떻게 표현해야 적절할지 모르겠습니다 ㅠㅠ #####
                WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located)
            except:
                print('Not enough images.') # 더 이상 이미지를 찾을 수 없으면 루프를 탈출합니다.
                break

    images = driver.find_elements_by_xpath('//div[contains(@class,"rg_meta")]') # 검색결과를 출력합니다.
    cnt = 1
    for img in images:
        img_title = json.loads(img.get_attribute('innerHTML'))["pt"] # 타이틀
        img_url = json.loads(img.get_attribute('innerHTML'))["ou"] # 이미지 URL
        img_type = json.loads(img.get_attribute('innerHTML'))["ity"] # 확장자
        img_link = json.loads(img.get_attribute('innerHTML'))["ru"] # 본문 URL
        print(cnt, img_title, img_url, img_type, img_link, sep=' : ')
        cnt += 1
       
if __name__ == "__main__":
    search('검색어', 검색할 이미지수)



위 소스에 대해 알고리즘 측면에서 문제가 없는지 테스트해보았습니다.



1. 검색된 이미지수보다 적은 이미지수를 입력한 경우


search('site:ruliweb.com "게임기" "구매" "후기"', 1000) 이렇게 입력하고 테스트해봤습니다.



브라우저 스크롤과 '결과 더보기' 클릭도 잘 되고, 일단 아직까지는 특별한 문제를 발견하지 못했습니다.



2. 검색된 이미지수가 입력된 이미지수보다 많은 경우


search('site:ruliweb.com "방문" "후기" "엽기떡볶이" "강원도" -외국인', 1000)


강원도에서 엽기떡볶이에 방문하는 내국인은 별로 없는 것 같네요 ㄷㄷ




이 경우 또한 별다른 문제를 발견하지 못했습니다.



3. 애매한(?) 숫자를 입력한 경우


search('site:ruliweb.com "게임기" "구매" "후기"', 100)




100개를 입력했는데 200개에서 끊기네요 ㄷㄷ


스크롤해서 찾은 이미지가 원하는 이미지수를 넘어가면 루프를 탈출하도록 했는데, 스크롤 한 번에 찾아지는 이미지수가 상당히 많기 때문에 발생하는 현상 같습니다.


정확한 이미지 개수를 반환받기를 원하는 경우에는 출력하는 단계 등에서 적절히 자를 필요가 있겠습니다.



결론적으로 스크립트에 군더더기도 많고 완전히 깔끔하게 구동되지는 않지만, 2019년 12월 현재 큰 문제 없이 작동하여 어느 정도 원하는 결과를 얻을 수 있었습니다.


스크립트에 각주로 ##### Ajax를 기다리는 부분을 EC로 어떻게 표현해야 적절할지 모르겠습니다 ##### 라고 달아놓은 부분에 대해서는 좀 더 공부를 해야될 것 같네요 ㅠㅠ


예전에는 Selenium으로 구현하면 쉽고, Requests로 구현하면 어렵다고 막연히 생각했는데요.


여러 사이트에서 크롤링 연습을 해보니 공부를 하면 할수록 둘 다 어렵다고 느껴지네요 ㅠㅠ



스포어에는 고수님들도 많이 계시는데 저의 허접한 스크립트를 읽어주셔서 감사합니다 ^-^


그럼 저녁식사 맛있게 드시고 편안한 저녁 되세요!



소스 공유

나만의 소스나 나만 알고 있는 좋은 소스를 서로 공유하는 자리입니다.

List of Articles
번호 분류 제목 글쓴이 날짜 조회 수
73 자료 한국어+영어 기초적 욕설필터 REGEX file title: 지구배경포함포인트폭탄- 2020.03.28 19
72 코드 [Python] url 주소로부터 IP 주소 알아내기 title: 황금 서버 (30일)humit 2020.02.20 117
71 코드 [Python] 네이버 실시간 검색어 3 title: 황금 서버 (30일)humit 2020.01.23 252
70 코드 Koa에서 자동으로 라우팅 채워주기 Seia 2020.01.22 79
69 코드 JavaScript에서 파이썬 문자열 처리 함수 중 하나 (바인딩)를 구현 7 Seia 2020.01.20 121
» 코드 [Python] Google Image Search 결과를 받아오기 file 이니스프리 2019.12.09 218
67 코드 [파이썬] Requests를 사용한 네이버 카페 크롤링 - 일정수 이상의 리플이 달린 게시글만 텔레그램 알림 3 file 이니스프리 2019.11.17 1066
66 코드 [JS] 클라이언트단 GET Parameter title: 투명 아이콘Hanam09 2019.11.16 139
65 코드 [Python] 싸이월드 미니홈피 백업 스크립트 이니스프리 2019.11.07 876
64 코드 [Python] PIL을 이용한 Animated GIF의 리사이징 file 이니스프리 2019.11.03 169
63 코드 [PyQt] sir.kr에서 스크랩한 게시글을 보여주는 윈도우앱 (검색 및 정렬 가능) 7 file 이니스프리 2019.08.09 340
62 코드 [아미나] Dropbox API를 이용한 이미지 호스팅 보드스킨 11 file 이니스프리 2019.07.13 484
61 코드 [Python] 네이버 모바일 이미지 검색에서의 이미지 파일을 멀티스레드로 다운받고 1개의 파일로 병합 11 file 이니스프리 2019.07.12 436
60 코드 [PHP/Javascript] 아미나에 자동으로 게시글을 생성하고 Ajax로 전송하여 결과를 표시하기 2 file 이니스프리 2019.07.09 220
59 코드 [Python] Selenium을 이용하여 특정 element를 캡처하는 스크립트 2 file 이니스프리 2019.07.03 1061
58 코드 [Python] 선택한 파일을 Dropbox API를 이용하여 업로드하고 공유링크를 받아서 이미지 호스팅 용도로 URL을 변환하기 1 file 이니스프리 2019.07.02 342
57 코드 [JS]클라이언트에서 Ip를 얻어보자 2 title: 투명 아이콘Hanam09 2019.01.21 347
56 코드 [JS] http를 https로 리디렉션! 3 title: 투명 아이콘Hanam09 2018.12.30 408
55 코드 [PHP] 이미지를 원하는 크기(원본비율 유지)로 리사이즈 하여 출력 (원본 이미지는 수정하지 않습니다) 6 이니스프리 2018.12.20 2568
54 코드 [아미나] 네이트 실시간 검색어 순위 위젯 (아미나 캐시 적용) 3 file 이니스프리 2018.12.18 419
목록
Board Pagination Prev 1 2 3 4 Next
/ 4
서버에 요청 중입니다. 잠시만 기다려 주십시오...