-
python-09 크롤링코딩/python 2023. 6. 19. 18:49
1. 크롤링
크롤링이란 웹페이지에 담겨있는 정보를 자동적으로 탐색하고 수집하는 기술이다.
크롤링을 하는 행위 자체는 불법이 아니지만 크롤링을 함으로써 해당 웹사이트의 트래픽에 무리를 준다거나 해당 데이터를 상업적으로 이용하여 이득을 편취하는 경우에는 불법이 될 수 있다.
실제로 데이터는 모든 산업에 필요하고 정형화된 데이터를 구하기 쉽지 않다는 점에서 데이터를 무단으로 사용하거나 트래픽을 증가시키는 경우가 일어날 수 있지만 그것을 일일이 찾아내서 고소하는 것은 번거로울 수 있다. 따라서 이러한 크롤링 봇이 웹사이트를 탐색할 때 그것을 제한하기 위해서 트래픽의 요청 수에 제한을 두거나 robots.txt 같은 탐색제한 프로그램을 사용하여 보안을 강화하고 있다.
2. 정적 크롤링
!pip install requests==2.28.1 !pip install beautifulsoup4==4.11.1
필요한 모듈을 다운로드한다. requests는 http 요청을 보내고 응답을 처리하는 모듈이며 beautifulsoup4는 요청한 html, xml 파일에서 데이터를 추출하기 위한 모듈이다.
import requests from bs4 import BeautifulSoup import os query = '항공기' url = f"https://www.google.com/search?q={query}&source-lnms&tbm=isch" header= { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3" } response = requests.get(url, headers=header) soup = BeautifulSoup(response.text, "html.parser") img_tags = soup.find_all("img") urls_list = [] for img_tag in img_tags: try: urls_list.append(img_tag['src']) except KeyError: try : urls_list.append(img_tag['data-src']) except KeyError: try : urls_list.append(img_tag['data-iurl']) except KeyError : pass os.makedirs('./image01_data/',exist_ok=True) for i ,url in enumerate(urls_list) : try: img_data = requests.get(url, headers=header).content file_path = f'./image01_data/aircraft_{i}.png' with open(file_path,'wb') as f: f.write(img_data) if os.path.getsize(file_path) == 0 : os.remove(file_path) except : pass
requests.get 함수를 사용하여 http를 요청하고 응답을 response 변수에 저장한다. url은 항공기를 구글에 검색한 url이며 header 부분은 windows 10 운영 체제애서 동작하는 user-agent 값을 전달하여 requests를 브라우저로 인식하여 차단을 우회할 수 있다.
요청한 requests의 텍스트값을 html.parser 인자를 사용하여 beautifulsoup로 파싱 한다. 이제 soup에 담긴 정보를 사용하여 원하는 데이터를 추출하는 데 사용할 수 있다.
이미지를 추출하기 위해서 soup.find_all('img')로 img 태그가 붙은 text들을 img_tag에 저장한다. img 태그의 src 속성은 이미지 소스의 url을 명시하기 때문에 for문을 돌려서 img_tag에서 src 속성을 찾아서 빈 리스트에 추가하며 에러가 발생 시 data-src, data_iurl 속성을 시도한다.
이미지 파일을 저장할 폴더를 생성하고 enumrate로 리스트에서 사진을 하나씩 꺼내서 파일 경로를 순서대로 지정한다. ㅁrequests.get.content 함수를 사용하여 해당 이미지의 데이터를 얻는다.
이미지 데이터를 파일로 읽기 위해서 with open으로 파일 경로를 넣고 wb모드로 이진 파일을 쓰기 모드로 연다. as f를 사용하여 f를 객체로 지정하고 f.write에 이미지 데이터를 넣어서 이미지 바이너리 데이터를 파일에 작성한다.
getsize 함수를 사용하여 만약 file_path가 0 즉 빈 이미지라면 파일을 remove 한다. 이제 해당 경로로 이동하여 해당 검색어로 수집된 사진들이 정렬되어 있는 것을 확인할 수 있다.
3. 동적 크롤링
!pip install selenium==4.2.0
requests 모듈과 beautiful soup 모듈을 사용하여 데이터를 단순히 수집하고 저장한 것과 달리 selenium 모듈은 웹사이트를 동적으로 자동화하여 원하는 정보를 수집함으로써 어느 정도 크롤링보다는 스크래핑에 가까운 모듈이다.
from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.chrome.service import Service import time import os import urllib, requests
셀레니움 라이브러리를 사용하기 위해서는 라이브러리를 설치하고 chrome driver를 다운로드하여야 한다.
https://chromedriver.chromium.org/downloads
해당 링크에서 다운로드할 수 있으며 크롬 설정에서 크롬버전을 확인하여 일치하는 버전을 다운로드하여야 한다.
query= 'ski resort' service = Service('./chromedriver/chromedriver.exe') driver = webdriver.Chrome(service=service) driver.implicitly_wait(3) driver.get('https://www.google.co.kr/imghp?h1=ko') keyword = driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/textarea') keyword.send_keys(query) driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/button').click() elem = driver.find_element_by_tag_name('body') for i in range(60): elem.send_keys(Keys.PAGE_DOWN) time.sleep(0.1) try: driver.find_element_by_xpath('/html/body/div[2]/c-wiz/div[3]/div[1]/div/div/div/div/div[1]/div[2]/div[2]/input').click() for i in range(60): elem.send_keys(Keys.PAGE_DOWN) time.sleep(0.1) except: pass links = [] images = driver.find_elements_by_css_selector('img.rg_i.Q4LuWd') for image in images : if image.get_attribute('data-src') != None: links.append(image.get_attribute('data-src')) elif image.get_attribute('data-iurl') != None: links.append(image.get_attribute('data-iurl')) elif image.get_attribute('src') != None: links.append(image.get_attribute('src')) print('찾은 이미지 개수: ',len(links)) count =0 for i in links : start = time.time() url = i os.makedirs('./download_image_data',exist_ok=True) while True : try : urllib.request.urlretrieve(url,f'./download_image_data/{str(count)}_apple.png') print(f'{str(count + 1)}/{str(len(links))}/{query}/다운 시간: {str(time.time()-start)[:5]}초') break except urllib.error.HTTPError as e: print(e) time.sleep(5) except Exception as e : print(e) time.sleep(5) if time.time() - start >60 : print('false') break count = count +1 print('download') driver.close() query= 'ski resort' service = Service('./chromedriver/chromedriver.exe') driver = webdriver.Chrome(service=service) driver.implicitly_wait(3) driver.get('https://www.google.co.kr/imghp?h1=ko') keyword = driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/textarea') keyword.send_keys(query) driver.find_element_by_xpath('/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/button').click() elem = driver.find_element_by_tag_name('body') for i in range(60): elem.send_keys(Keys.PAGE_DOWN) time.sleep(0.1) try: driver.find_element_by_xpath('/html/body/div[2]/c-wiz/div[3]/div[1]/div/div/div/div/div[1]/div[2]/div[2]/input').click() for i in range(60): elem.send_keys(Keys.PAGE_DOWN) time.sleep(0.1) except: pass links = [] images = driver.find_elements_by_css_selector('img.rg_i.Q4LuWd') for image in images : if image.get_attribute('data-src') != None: links.append(image.get_attribute('data-src')) elif image.get_attribute('data-iurl') != None: links.append(image.get_attribute('data-iurl')) elif image.get_attribute('src') != None: links.append(image.get_attribute('src')) print('찾은 이미지 개수: ',len(links)) count =0 for i in links : start = time.time() url = i os.makedirs('./download_image_data',exist_ok=True) while True : try : urllib.request.urlretrieve(url,f'./download_image_data/{str(count)}_ski resort.png') print(f'{str(count + 1)}/{str(len(links))}/{query}/다운 시간: {str(time.time()-start)[:5]}초') break except urllib.error.HTTPError as e: print(e) time.sleep(5) except Exception as e : print(e) time.sleep(5) if time.time() - start >60 : print('false') break count = count +1 print('download') driver.close() 찾은 이미지 개수: 400 1/400/ski resort/다운 시간: 0.015초 2/400/ski resort/다운 시간: 0.023초 3/400/ski resort/다운 시간: 0.022초 4/400/ski resort/다운 시간: 0.012초 5/400/ski resort/다운 시간: 0.012초 6/400/ski resort/다운 시간: 0.018초 7/400/ski resort/다운 시간: 0.002초
동일하게 검색할 키워드를 만들어주고 chromdriver.exe 파일의 경로를 찾아서 Service 함수에 넣어주면 service에chromedriver의 정보가 할당된다.
chrome 드라이버는 셀리니움 드라이버와 크롬 브라우저를 연결하기 위한 역할이다. service 변수를 webdriver.chrome을 사용하여 크롬 브라우저와 연결한다. implicity_wait(3)은 암묵적 대기시간을 설정해 주는 것으로 웹이 로드될 때까지 최대 3초간 대기하는 것이다.
driver.get을 사용하여 구글의 이미지 검색창을 불러오고 검색을 수행하기 위한 검색창의 xpath를 keyword 변수에 저장한다. xpath는 웹페이지에서 경로를 지정하는 언어이다. 이미지 검색 페이지에서 개발자 도구를 실행시키고 select element 메뉴를 클릭하여 검색창을 선택하면 검색창의 코드 영역을 얻을 수 있다. 우클릭을 해서 full xpath를 카피한다.
send_keys를 사용하여 검색창에 검색어를 전달한다. 이제 검색 버튼의 xpath를 찾고 click 함수를 이용하여 검색을 실시한다.
diver.find 함수를 사용하여 검색 결과에서 body 태그를 찾아서 elem 변수에 저장한다. 60번 동안 page_down 이벤트를 elem 변수에 보내서 페이지를 스크롤 다운한다. time.sleep은 0.1초의 지연을 생성하여 스크롤 속도를 조절한다.
스크롤을 하다 보면 이미지가 한 번에 로드되지 않고 결과 더 보기를 통해 이미지를 로드하게 된다. 따라서 결과 더 보기 버튼을 만났을 때 자동적으로 스크롤할 수 있도록 결과 더 보기 버튼의 xpath를 찾으면 click 이벤트를 실행하고 계속해서 스크롤을 내리는 동작을 하게 한다.
css 선택자를 사용하여 img.rg_i.Q4LuWd의 태그를 가진 이미지들을 찾아서 images에 저장한다. images에서 data-src, data-iurl, src 속성을 차례로 훑어가면서 값이 none이 아닌 경우에만 links 리스트에 저장한다.
links 리스트에 저장된 이미지 url을 반복하면서 이미지를 다운로드한다. 폴더를 생성하고 오류가 발생할 경우 5초 동안 기다린다. 다운로드 시간이 60초를 초과하면 다음 이미지로 넘어가서 urlib.request.urlretreive 함수로 다운로드를 시작한다.
카운트 값과 전체 개수, time 현재시간 변수에서 start 시작 시간을 뺀 값을 5자리까지 출력한다. stacount 변수를 1씩 증가시켜서 이미지 파일명을 순차적으로 저장한다.
작업이 끝났기 때문에 driver.close 함수를 사용하여 열려있는 webdriver를 종료하고 저장된 경로에 파일을 확인한다.
'코딩 > python' 카테고리의 다른 글
python-08 matplotlib (0) 2023.05.04 python-07 pandas (0) 2023.05.04 python-06 numpy (0) 2023.05.04 python-05 예외처리 (0) 2023.05.02