동물상 테스트 웹, 앱 만들기! (2편)

HyungJin Han·2022년 6월 15일
0

따라쟁이 코딩

목록 보기
2/4
post-thumbnail

실전 수익형 웹, 앱 서비스 동물상 테스트 만들기
2번째 영상을 통해 제작된 코드입니다.

해당 내용에 도움이 되는 부분은 링크를 걸어두었습니다!

그럼 시작하도록 하겠습니다!


1편에서는 원하는 Beautiful Soup를 이용하여 원하는 키워드를 입력하면,

해당 키워드에 맞춰 이미지를 자동을 다운로드 받아지는 프로그래밍을 해봤다.

하지만 문제가 살짝 있다면, 보안상의 문제로 최근 브라우저 자체에서

사용자가 직접 Crawling하는 것을 막아두었다고 한다.

그래서 그런지 사실 1편에서 이미지 다운로드 중 오류가 조금씩 있었지만
어찌저찌 다운로드가 가능은 했었다...

이러한 상황에서 희망으로 떠오른 것이 바로 Selenium을 통한 Crawling이다!

간단하게 이해한 바로는, Python을 이용하여 프로그래밍을 위한

가상 작업 환경을 조성하고, Selenium이라는 프레임워크를 통해

Crawling 작업을 할 수 있도록 하는 프로그래밍이라고 이해했다.

그럼 차근차근 작업을 시작할 차례!


1. Python 가상 작업 환경 조성

우선 Python의 가상 작업 환경을 조성해야 하는 이유를 설명하자면,

BeautifulSoup, Request 등의 라이브러리에 따라 호환되는

Python의 버전이 다르기 때문에 하나의 로컬 컴퓨터에서도 여러 개의

Python의 버전이나 정보들을 분리하여 각 버전별로 환경을 가상으로 조성하고

이러한 가상 환경을 선택해서 사용하도록 하는 것이

Python의 가상 환경 조성의 개념이다.

그렇다면 우선 Python의 가상 환경을 조성해보자!

Python의 가상 환경 생성을 위한 사이트, Python venv에서 방법을 찾아보도록 한다.

사이트를 보면 cmd를 통한 가상 환경 명령어를 볼 수 있다.

python3 -m venv /path/to/new/virtual/environment

이렇게 나와있지만, 필자의 경우에는 python3에서 3을 지우고 실행했다.

물론 위의 코드에서 경로는 각자 다르게 해도 무관하다.

가상 환경을 생성했다면, cmd를 통해

 cd /path/to/new/virtual/environment/Scripts

위의 명령어를 통해 Python 가상 환경인 venv를 조성한 디렉토리의

Scripts로 이동한 후, cmd를 통해

activate

위의 명령어를 입력하게 되면 가상 환경 생성이 완료된다!

그렇다면 가상 환경은 조성했고, Selenium이라는 라이브러리를

다운로드 받아 가상 환경과 함께 사용하도록 해보자!


2. 프레임워크 Selenium 설치

우선 Selenium이 일반적인 방법과 달리 Crawling을 수월하게

진행할 수 있는 원리에 대해 생각해 볼 필요가 있다.

일반적으로 BeautifulSoup를 통한 Crawling은 코드를 통해

html의 정보를 Parsing하는 원리라면,

Selenium의 작동 원리는 실제 브라우저를 코드로 조작하는 원리로,

마우스의 움직임, 클릭, 스크롤과 같은 웹 동작들을 실행시켜

웹을 Crawling 해주는 원리이다.

결론적으로, 필자는 흔히 생각하는 메크로의 원리라고 이해했다.

그렇다면 마찬가지로 cmd를 이용하여 설치를 해보자.

pip install selenium

위의 명령어를 통해 설치를 한 후, 먼저 조성한 가상 환경에 사용할

브라우저 실행 파일을 넣어줘야 한다. 왜냐!!!

생각해보면 굉장히 간단하다.

현재 사용하고 있는 로컬 컴퓨터의 웹은 가상 작업 환경이 아닌

실제 컴퓨터를 통한 로컬 작업 환경이고,

가상 환경의 경우 PythonSelenium, 이 둘 밖에 설치된게 없다.

우리의 목적은 웹 Crawling인데 중요한 웹이 없다.

그래서 웹 브라우저 실행파일을 따로 넣어주어야 한다.


3. 브라우저 드라이버 다운로드


필자의 경우 크롬 브라우저를 이용해 작업을 했다.

Downloads, 이 링크를 통해 크롬 브라우저 실행 파일을 다운받을 수 있다.

여기서 주의할 점이라고 하면, 로컬 환경의 크롬 버전과

chromedriver.exe의 버전이 동일해야 한다.

현재 사용하는 크롬의 버전을 보는 방법은??


버전을 확인했다면, 버전에 맞게 링크를 들어가서

window, mac 등의 각 운영 체제에 맞게 다운받으면 된다.

그렇게 다운받은 압축 파일을 압축 해제한 후에 chromedriver.exe

앞서 조성한 가상 환경 디렉토리에 이동시켜 준다.

그리고 chromedriver.exe가 있는 디렉토리에 google.py를 생성해준다.

여기서도 문제는 모든 환경은 조성이 되었을지 몰라도 코드에 대한 지식이
부족(없다싶이)하기 때문에 이 또한 구글링을 통해 어떻게 사용하는지에 대한
예제를 찾아보고 그러한 예제를 조금 더 응용하여 사용하도록 해보자...


4. Selenium을 이용한 Crawling

python selenium example라는 키워드는 검색하면

Simple Usage 이러한 사이트가 나온다.

Simple Usage 부분을 보면

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

driver = webdriver.Firefox()
driver.get("http://www.python.org")
assert "Python" in driver.title
elem = driver.find_element(By.NAME, "q")
elem.clear()
elem.send_keys("pycon")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
driver.close()

위와 같은 코드를 볼 수 있다.

그렇다면 이 코드를 앞서 생성했던 google.py에 에디터 툴을 통해 입력해보도록 하자!

미리 얘기를 하자면 Terminal을 통한 작업이 굉장히 많은 관계로,
이러한 작업을 하는데 있어서 귀찮아질 것을 염려해서 Atom에서 Visual Studio Code
에디터 툴을 바꿔서 사용하게 되었다.

그리고 실행을 해보면?

Python 공식 홈페이지가 열리는 것을 볼 수 있다.

여기서 주목할 것은 바로

이 부분이다!

가상 환경에서 Selenium의 자동화 코드로 제어된다고 뜬다.

이렇게 코드의 실행은 해봤고, 이제는 응용을 할 차례!

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

위의 코드는 selenium 모듈에서 webdriver를 불러오고,

selenium.webdriver.common.keys 모듈에서는 Keys를,

selenium.webdriver.common.by 모듈에서는 By를 불러온다는 뜻이고,

driver = webdriver.Firefox()
driver.get("http://www.python.org")

위의 코드는 웹 드라이버를 Firefox로 실행한다는 코드를 driver 변수에 담고

이를 통해 Python 사이트를 불러온다는 뜻이다.

그렇다면 여기서 조성 환경에 따라 코드를 고쳐야 할 필요가 있다.

driver = webdriver.Google()
driver.get("https://www.google.co.kr/imghp?hl=ko&tab=ri&ogbl")

위의 코드처럼 드라이버는 Google로, url은 구글 이미지 검색 url로 변경해줬다.

assert "Python" in driver.title

위의 부분은 코드가 실행되었을 때, title에 해당하는 값이

Python이라는 단어를 포함하고 있는지를 확인하는

일종의 보장의 개념이라고 필자는 이해했다.

이렇게 포함한다는 것을 가정하거나 하는 식으로 보장하는 프로그래밍을

'방어적 프로그래밍'이라고 불린다고 한다.

하지만 필자는 사용하지 않도록 하겠다.

저러한 보장 코드를 사용하는 것을 권장한다고는 하지만, 이해할 것은 태산이고...
이해를 위한 Crawling을 하는 것이기 때문에 해당 코드를 지우고 사용했다.

다음으로 더 해석해보자면,

elem = driver.find_element(By.NAME, "q")

위의 코드는 q라는 name 태그를 가진 요소를 찾도록 하는 코드를 elem 변수에 담는다!

elem.clear()

다음 코드는 리스트에 있는 값을 삭제하는 코드인데

q라는 name 태그를 가진 요소는 구글 검색창이기에 비워준다는 의미인 것 같다.

필자는 애초에 비워진 검색창에 검색어를 입력하기에 해당 코드를 삭제했다.

elem.send_keys("pycon")

위의 코드는 pycon이라는 키워드를 검색창에 입력해주는 역할을 한다.

여기서 필자는 테스트를 위해 adidas를 입력할 예정이다.

elem.send_keys(Keys.RETURN)

위의 코드는 키보드 입력 코드로, RETURN은 엔터를 의미,

위의 코드로 실행 가능한 작업은 지정한 검색어 입력, 엔터 입력으로

원하는 키워드의 이미지를 검색하는 코드가 된다.

나머지 코드를 해석해보자면,

assert "No results found." not in driver.page_source
driver.close()

위의 코드에서 assert문은 사용하지 않을 예정으로 넘어가고,

마지막 줄은 작업이 끝나면 웹 브라우저를 나가주는 코드이다.

이렇게 준비된 코드를 입맛에 맞게 수정해준 결과,

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)
# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

위의 코드를 에디터의 Terminal로 실행하게 되면???


자동으로 검색창에 키워드 입력 후, 엔터까지 된다!!!


5. 원하는 키워드의 이미지 다운

5-1. 원하는 이미지의 URL 얻어내기


이제 원하는 것은 키워드에 맞는 이미지를 다운로드 받는 것!

위의 사진을 보면 작은 이미지 하나를 클릭하고,

오른편에 큰 사진이 나오게 되고 우리는 저 큰 고화질의 이미지를 다운받을 코드를 짜야한다.

그렇다면 먼저 해야할 것은??

작은 이미지를 클릭하는 코드와 작은 이미지의 class, name 등의 태그 이름이다.

그렇다면 구글링!

python selenium click이라는 키워드를 통해 검색한 결과,

python selenium click on button, 이 사이트에서 내려가다 보면 보이는 코드인,

driver.find_element_by_css_selector('.button .c_button .s_button').click()

이러한 코드를 볼 수 있다.

위의 코드는 사실 2가지의 팁을 알 수 있다.

하나는 .click(), 또 하나는 .find_element_by_css_selector

우선 찾을 이미지의 class, name 등의 태그 끝에 .click()를 통해

클릭을 할 수 있다는 것을 알 수 있고,

.find_element_by_css_selector를 통해 웹 페이지에 있는 모든 이미지의

class, name 등의 태그를 알아내는 코드 이렇게 두 개인 셈이다.

우리가 찾을 작은 이미지는 여러개이기 때문에

.find_elements_by_css_selector 이렇게 elements로 입력할 예정이다.

또한 찾으려는 작은 이미지의 태그 명을 보기 위해 크롬의 개발자 모드로~

위의 이미지처럼 빨간 표시인 '요소 선택'을 클릭하고,

작은 이미지를 클릭해서 작은 이미지의 정보를 확인한다.

확인하다 보면 빨간 부분처럼 class 명을 확인할 수 있다.

이렇게 알아낸 class 명과 .onclick을 우리의 코드에 적용해 볼 차례!

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_elements_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

이제 작은 이미지를 클릭했다면, 앞서 봤던 큰 이미지가 켜지고 다운을 받아야 한다.

방금 알아봤던 개발자 모드를 이용해 큰 이미지의 class명을 알아내보자!

하지만 class만을 통해서는 다운을 받을 수 없으며,

저 큰 이미지의 url을 알아내야 한다.

또다시 찾아온 구글링 시간!

python selenium img src라는 키워드를 통해 검색하고,


How to get img src in string in selenium using python라는 사이트에서 밑으로 내려 찾다보면,

src = driver.find_element_by_xpath("//div[@class='rc]/h3[@class='r']/a/img").get_attribute("src")

이러한 코드가 나온다.

위의 코드를 보면 idclass가 아니라 xpath라는 것의 src를 알아내는 코드다.

이걸 가져온 이유는 작은 이미지와 다르게 큰 이미지의 경우,

idclass의 이름이 다른 것들이 사이에 스파이처럼 껴있다.

그렇게 되면 오류가 생겨 코드가 종료되기 때문에

위의 이미지처럼 각각의 이미지의 idclass의 이름이 다르더라도

최대한의 교집합으로써 xpath를 사용해 안정적으로 원하는 이미지의

scr를 얻어낼 수 있게 된다.

더욱더 안정적으로 scr를 얻어내기 위해서

각각의 이미지의 full xpath를 얻어서 코드에 적용시키도록 했다.

full xpath를 구하는 방법은 다음과 같다.

위의 이미지처럼 해당 이미지의 코드에 우클릭을 한 후 복사를 클릭,

전체 xpath 복사를 클릭해서 코드에 적용시키면 된다.

이렇게 코딩 작업을 한 결과,

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_elements_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

driver.find_element_by_xpath("/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[3]/div/a/img").get_attribute("src")

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

위의 코드가 완성된다.

이제 출력을 통해 src를 알아내기 위해

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_element_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

print(driver.find_element_by_xpath("/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[3]/div/a/img").get_attribute("src"))

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

이렇게 print()로 에디터의 Terminal로 출력하면???

Bluetooth: bluetooth_adapter_winrt.cc:1074 Getting Default Adapter failed

이러한 Terminal에서의 에러 문구가 나온다...

이유는 작은 이미지까지는 잘 클릭이 되었으나, 큰 이미지의 url을 불러오는 과정에서

큰 이미지의 불러오기 위한 로딩 시간을 주지 않고

url을 불러오면서 생기는 오류이다.

이러한 오류는 시간 지연을 주는 코드를 삽입하면서 해결이 가능하다.

import time

time.sleep(2)

위의 코드처럼 time 모듈을 불러와주고

2초 정도 지연시킴으로써 오류를 해결하면 된다.

이렇게 수정한 코드는

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_element_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

time.sleep(2)

print(driver.find_element_by_xpath("/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[3]/div/a/img").get_attribute("src"))

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

위의 코드를 Terminal을 통해 실행하면??

위의 이미지처럼 빨간 부분에 큰 이미지의 url을 출력해준다.

5-2. URL을 통한 이미지 다운로드


하지만 우리가 원하는 것은 출력이 아닌 다운로드!

이것도 물론 구글링이다.

python download image by url라는 키워드를 통해 들어간 사이트인

Downloading a picture via urllib and python에 들어가서 천천히 내리다보면 나오는

# Python 3

import urllib.request

urllib.request.urlretrieve("http://www.gunnerkrigg.com//comics/00000001.jpg", "00000001.jpg")

위의 코드를 활용해 보자!

간단하게 해석하자면, urllib.request라는 url 요청 모듈을 불러오고

urllib.request.urlretrieve를 사용하여 ("url", "다운로드명.jpg") 이렇게 지정해주면

다운로드가 된다고 한다!

이제 코드를 적용시키면

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
import urllib.request

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_element_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

time.sleep(2)

imgUrl = driver.find_element_by_xpath("/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[3]/div/a/img").get_attribute("src")
# imgUrl이라는 변수에 담기

urllib.request.urlretrieve(imgUrl, "test.jpg")
# imgUrl 변수를 인자로 입력
# test.jpg는 임의로 정한 이미지 명

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

그리고 위의 코드를 Terminal을 통해 실행하면???

urllib.error.HTTPError: HTTP Error 403: Forbidden

Terminal에서 이러한 에러를 뿜어낸다...

또다시 구글링의 방으로...

드디어...찾았다...

download image from url using python urllib but receiving HTTP Error 403: Forbidden의 결과

import urllib.request

opener=urllib.request.build_opener()
opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')]
urllib.request.install_opener(opener)


이해한 만큼으로 설명해보자면, 우리는 사람으로써 브라우저를 Crawling하고 있기에,

브라우저 측에서 보안 이슈로 다운로드를 막아둔 듯하다.

그래서 우리는 headers를 통해 브라우저가 코드를 실행 중이다 ~ 하고

크롬을 속여줘야 한다는 것이다.

그럼 다시 코드를 적용해보면

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time
import urllib.request

driver = webdriver.Chrome()
driver.get("https://www.google.co.kr/imghp?hl=ko&ogbl")
elem = driver.find_element(By.NAME, "q")
elem.send_keys("adidas")
elem.send_keys(Keys.RETURN)

driver.find_element_by_css_selector('.rg_i.Q4LuWd')[0].click()
# 실제 class명은 rg_i Q4LuWd이지만 by_css_selector를 사용해서
# class명의 공백을 .으로 입력하여 rg_i.Q4LuWd이 됨
# [0]은 수많은 class 중에서 index값이 0인(맨처음) 이미지를 클릭한다는 의미

time.sleep(2)

imgUrl = driver.find_element_by_xpath("/html/body/div[2]/c-wiz/div[3]/div[2]/div[3]/div/div/div[3]/div[2]/c-wiz/div/div[1]/div[1]/div[3]/div/a/img").get_attribute("src")
# imgUrl이라는 변수에 담기

opener=urllib.request.build_opener()
opener.addheaders=[('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.115 Safari/537.36')]
# 여기서 Chrome의 버전을 현재 버전의 것으로 수정
urllib.request.install_opener(opener)

urllib.request.urlretrieve(imgUrl, "test.jpg")
# imgUrl 변수를 인자로 입력
# test.jpg는 임의로 정한 이미지 명

# driver.close()
# 브라우저 종료는 잠시 주석으로 처리

그리고 Terminal을 통해 실행시켜보자!

🤣 Complete 🤣


마치며...

오늘은 이렇게 Python의 가상 작업 환경을 조성하고 Selenium을 통해

원하는 키워드에 맞는 이미지를 다운받는 작업을 해보았다.

지극히 개인적으로 1편의 웹 Crawing은 오늘의 작업에 비하면 정말 아무것도
아닌 수준인 것처럼 오늘 코딩 작업이 어려웠다.
하지만 이런 코드들도 다뤄보면서 실력을 점점 키워가면 언젠가는...
컨텐츠 만들기에 성공하지 않을까 싶다.

다음 3편으로 계속 진행해보겠습니다~!

profile
토끼보다는 거북이처럼 꾸준하게

0개의 댓글