본문 바로가기

Programming/[Python]

[Python] 데이터 수집을 위한 웹 크롤링(Crawling)

원하는 데이터를 수집하고자 할 때 크롤링(Crwaling)을 하곤 한다.

크롤링은 해당 페이지의 데이터들을 추출하는 행위를 의미한다.

 

이 때 크롤링을 하기 위한 소프트웨어를 크롤러(Crawler)라고 하며,

봇, 스파이더, 지능 에이전트 라고도 불린다.

 

스크래이핑과 크롤링의 차이 ?

Scraping :

웹 사이트의 특정 정보를 추출하는 것. 웹 데이터의 구조 분석이 필요

Crawling :

프로그램이 웹사이트를 정기적으로 돌며 정보를 추출하는 것

 

 

 수집 데이터의 형태

 

데이터를 수집하기 앞서, 수집 데이터의 형태를 3가지로 구분할 수 있다.

 

수집 데이터 형태


정형일정한 규격에 맞춰서 구성된 데이터 (어떠한 역할을 알고 있는 데이터)
관계형 데이터베이스 시스템의 테이블과 같이 고정된 컬럼에 저장되는 데이터 파일 등이 될 수 있다. , 구조화 된 데이터가 정형 데이터

 

반정형일정한 규격으로 구성되어 있지 않지만 일정한 틀을 갖추기 위해서 태그나 인덱스형태로 구성된 데이터
연산이 불가능한 데이터 ex) XML, HTML, JSON

 

비정형구조화 되지 않는 형태의 데이터 (정형과 반대로 어떠한 역할인지 알수 없는 데이터)
형태가 없으며, 연산도 불가능한 데이터 ex) SNS, 영상, 이미지, 음성, 텍스트 등

 

 검색 내용 크롤링

 

네이버 검색 엔진에서 '정보처리기사'를 검색하여

해당 단어가 포함되는 구문을 리스트로 뽑아 보자.

 

requests 모듈을 import 하고

get 메서드를 호출하여 인자를 전달해준다. 

import requests as req

# url read
url = 'http://search.naver.com/search.naver'
res = req.get(url, params={'query' : '정처기'})

# print(res.text)

 

우선 원하는 정보를 얻기 위해

html 문서 내의 태그를 확인한다.

 

for문과 if 문을 이용하여 '정보처리기사'가 포함된 페이지의 구문을 

리스트로 반환해준다.

strings = res.text
strings_row = strings.split('<')

target_lst = []

for text in strings_row:
    target_idx = text.find('정보처리기사')
    target = text[target_idx:]
    if len(target) > 6:
        target_lst.append(target)

target_lst = target_lst[1:]
print(target_lst)

# ['정보처리기사란 응시자격을 갖춘 자가 산업인력공단에서 시행하는 정보처리기사 시험에 합격하여 그 자격을 취득한 자를 말한다. 정보처리기사 시험은 필기시험과 실기시험으로 이루어지며, 필기시험은 객관식으로 100점을 만점으로 하여 과목당 40점 이상, 전 과목 평균 60점 이상이면 합격한다. 실기시험은 주관식 시험으로 100점을 만점으로 하여 평균 60점 이상이면... ',

 

 BeautifulSoup 활용

 

앞선 크롤링에선 직접 태그와 내용 위치를 찾고

정보 추출을 위한 로직을 새로 만들었지만,

bs4 라이브러리를 사용하면 위와 같은 수고를 덜어낼 수 있다.

 

bs4를 import하고 

단한 html 문을 만들어준다.

**html 문서는 하나의 거대한 문자열과 같음을 인지해야한다.

 

태그의 id에 해당하는 내용을 string을 통해 확인 가능하다.

# id find

from bs4 import BeautifulSoup

html = """
<html><body>
<h1 id = 'title'>python</h1>
<p id = 'p1'>hello</p>
<p id = 'p2'>world</p>
</body></html>
"""

# BeatufulSoup 객체 생성
soup = BeautifulSoup(html,'html.parser')

# find 메서드 호출
h1 = soup.find(id='title')
p1 = soup.find(id='p1')
p2 = soup.find(id='p2')

print(h1.string)
# python

앵커 태그의 url 또한 가져올 수 있다.

 

예시 html 문을 생성한 후 해당 문서의 a 태그를 모두 찾아 links 변수에 할당한다.

links 리스트 0번째 요소의 attrs 를 확인해 보면 딕셔너리 형태이고

이때 value가 해당 url이 된다.

from bs4 import BeautifulSoup

html = """
<html><body>
  <ul>
    <li><a href="http://www.naver.com">naver</a></li>
    <li><a href="http://www.daum.net">daum</a></li>
  </ul>
</body></html>
"""
soup = BeautifulSoup(html, 'html.parser')
links = soup.find_all("a")

print(links[0].string)
print(links[0].attrs['href'])

for a in links:
    href = a.attrs['href']
    text = a.string
    print(href, text)

태그의 하위 분기 내용을 가져올 수도 있다.

 

select_one, select 메서드를 통해

하위 분기의 태그를 지정하여 해당 태그와 내용을 반환한다.

from bs4 import BeautifulSoup

html = """
<html><body>
<div id='books'>
  <h1> 위키북스 도서 </h1>
  <ul class='item'>
    <li> 게임 입문 </li>
    <li> 파이썬 입문 </li>
    <li> 웹 디자인 입문 </li>
  </ul>
</div>
</body></html>
"""
soup = BeautifulSoup(html, 'html.parser')
h1 = soup.select_one("div#books > h1")
li_lst = soup.select("div#books > ul.item > li")

print(h1)
print(li_lst)
#

 

이번엔 기상청 xml 날씨 데이터를 가져와 보자.

 

tag 이름이 'title' 과 'wf' 인 부분의 내용을 가져오면

일기 예보 제목과 내용 요약을 확인할 수 있다.

import urllib.request
from bs4 import BeautifulSoup

# weather

url = "http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp"
res = urllib.request.urlopen(url)

soup = BeautifulSoup(res, 'html.parser')
title = soup.find('title').string
wf = soup.find('wf').string

print(title)
print('-'*20)
print(wf)

 

 Selenium을 이용한 동적 페이지 크롤링

 

동적 페이지(Dynamic page)란 url에 관계 없이 해당 웹 페이지 내에서

사용자의 요청을 바탕으로 서버 측 에서 컨텐츠를 채워넣는 방식이다.

(네이버 지도, 스타벅스 지점 확인 페이지 등..)

 

기존 정적(Static)인 페이지에서 했던 방식대로 크롤링을 할 수 없기 때문에

웹 페이지 제어를 위한 자동 테스팅 모듈인 Selenium 라이브러리를 사용한다.

**

Selenium 을 사용하여 크롤링을 진행 할 때

특정 웹 페이지에서 과도한 사용을 할 경우 IP ban을 당할 수 있으므로 주의하자.

 

사용에 앞서 파이썬에서 크롬으로 접속할 수 있게 해주는

chrome driver를 현재 working directory에 설치한다.

 

이 후 terminal 에서  Selenium을 설치한다.

-- pip install selenium

 

키워드로 검색한 화면 자동 캡처 후 저장하는 방법.

from selenium import webdriver
from urllib.parse import quote_plus
import time

# 필요한 옵션 설정
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')

# Chrome 드라이버 생성
driver = webdriver.Chrome('chromedriver')

# 해당 URL로 브라우저 실행
driver.get('http://naver.com')

# 키워드 입력한 페이지 스크린샷
url = 'https://search.naver.com/search.naver?where=image&sm=tab_jum&query='
kword = input('검색어를 입력:')
base_url = url + quote_plus(kword)

driver.get(base_url)
time.sleep(1)
driver.save_screenshot('website.png')

만약 페이지 범위가 넓어 스크롤이 필요할 경우

검색 페이지를 자동 스크롤하며 스크린 샷을 저장하는 방법이다.

from selenium.webdriver.common.keys import Keys
from selenium import webdriver
from urllib.parse import quote_plus
import time
import numpy as np

# Chrome 드라이버 생성
driver = webdriver.Chrome('chromedriver')

# 해당 URL로 브라우저 실행
driver.get('http://naver.com')

url = 'https://search.naver.com/search.naver?where=image&sm=tab_jum&query='
kword = input('검색어를 입력:')
base_url = url + quote_plus(kword)

driver.get(base_url)

# 스크롤하며 스크린 샷
body = driver.find_element_by_css_selector('body')
for i in range(5):
  body.send_keys(Keys.PAGE_DOWN)
  time.sleep(abs(np.random.normal(1, 3)))
  driver.save_screenshot(f'website_{i}.png')