파이썬을 활용한 웹 크롤링_기본편
Language/Python

파이썬을 활용한 웹 크롤링_기본편

728x90
반응형

 웹 브라우저는 패킷을 만들고, 보내고, 돌아온 데이터를 해석해 사진, 소리, 비디오, 텍스트 등으로 표현하는 매우 유용한 애플리케이션이다.

웹 브라우저는 코드이기 때문에 코드는 떼어내서 기본 구성 요소를 나누고, 다시 만들고, 재사용하고, 원하는 어떤 것으로든 바꿀 수 있다.

 

웹 크롤링은 웹 브라우저로부터 사용자가 가공하고 싶은 데이터를 가져오는 과정이다. 여기에서는 가져오는 과정과 저장하는 과정, 다시 불러오는 과정 등이 포함되어 있다.

 

urllib 라이브러리는 파이썬의 표준 라이브러리로 웹을 통해 데이터를 요청하는 함수, 쿠키를 처리하는 함수, 심지어 헤더나 유저 에이전트 같은 메타 데이터를 바꾸는 함수도 있다.

urllib 라이브러리의 함수 중에서 urlopen은 네트워크를 통해 원격 객체를 읽는다.

 

BeautifulSoup잘못된 HTML을 수정하여 쉽게 탐색할 수 있는 XML 형식의 파이썬 객체로 변환하여 데이터를 제공해주는 라이브러리이다.

BeautifulSoup 라이브러리 중에서 가장 많이 사용되는 객체는 바로 BeautifulSoup이다. 이는 urlopen 으로 읽어온 객체의 read 함수로 인자로 받아 하나의 객체를 만들어낸다. 이를 이용하여 웹 크롤링을 한다.

 

BeautifulSoup 객체에는 find()findAll()이라는 함수가 있다.

find 함수는 지정된 페이지에서 해당하는 데이터 중 가장 위에 존재하는 하나의 데이터만 제공하고

findAll지정된 페이지에서 해당하는 모든 정보를 가져오게 된다.

 

 

findfindAll은 모두 다양한 인자를 받는다. tag 인자는 HTML의 문법의 tag와 같은 정보이다.

따라서 tag를 넣게 되면 이 tag에 해당하는 정보를 가져오게 된다.

attributes 인자는 속성으로 파이썬 딕셔너리를 받고 classid와 같은 속성 인자에 대한 정보를 가져온다.

text 인자는 속성이 아니라 content에 해당하는 정보를 가져오게 된다.

keyword 인자attributes와 비슷한 작동을 하는데 속성에 대한 정보를 통해 데이터를 가져온다. 하지만 attributes 인자는 딕셔너리 형태로 인자를 받지만 keyword는 등호 기호로 인자를 받는다.

 

BeautifulSoup 객체에는 자식과 자손이 있다.

자식HTML의 기준에서 바로 아래에 있는 tag를 의미하고 자손은 아래에 있는 모든 tag를 의미한다.

자식을 찾는 함수로는 children()을 사용할 수 있고 자손을 찾는 함수로는

descendants()를 사용할 수 있다.

 

정규 표현식은 정해진 규칙으로 문자나 숫자를 표현하는 방식으로 대신에 컴퓨터가 이해하기 쉽게 표현을 하는 방식이다.

*는 바로 앞의 문자나 하위 표현식이 0번 이상 나타나는 것을 의미하고 +1번 이상 나타나는 것을 의미한다.

이외에도 다양한 기호들이 존재한다. BeautifulSoup에서는 srchref와 같은 위치나 링크에 대한 정보를 표현할 때 많이 사용된다. 정규 표현식을 해석하는 것은 re 라이브러리를 이용하여 compile() 함수를 사용해야한다.

 

웹 크롤러라는 이름은 웹을 크롤링하기 때문에 붙여진 이름으로 그 핵심은 재귀이다.

 

웹 크롤러는 URL에서 페이지를 가져오고, 그 페이지를 검사해 다른 URL을 찾고, 다시 그 페이지를 가져오는 작업을 무한히 반복한다. 하지만 웹 크롤러를 사용할 때는 반드시 대역폭에 세심한 주의를 기울여야 하며, 타켓 서버의 부하를 줄일 방법을 강구해야 한다.

 

API는 애플리케이션 프로그래밍 인터페이스로 서로 정보를 공유해야 하는 소프트웨어 사이에서 국제어 구실을 하도록 디자인된 인터페이스이다.

API가 요청을 보낼 때는 HTTP를 통해 데이터를 요청하며 API는 이 데이터를 XML이나 JSON 형식으로 반환한다.

 

HTTP를 통해 웹 서버에 정보를 요청하는 방법은 GETPOST가 주를 이룬다.

GET은 브라우저의 주소 표시줄을 통해 웹사이트에 방문할 때 쓰는 방법이고 POST는 폼을 작성하거나, 서버에 있는 스크립트에 정보를 보낼 때 사용한다.

응답은 XMLJSON이다. XMLtag의 형식으로 데이터를 보내고 JSON은 딕셔너리 형태로 데이터를 보낸다. JSON 파일과 XML 파일을 비교해보면 JSON 파일이 크기가 더 작아서 유용하게 사용된다.

 

API를 사용하기 위해서는 key를 사용해야 되는 경우가 많은데, 해당하는 API에 대한 명령을 입력한 후 key에 대한 값을 넣어주어야 인증이 되는 것이다.

 

JSON 파싱 라이브러리는 파이썬의 핵심 라이브러리 일부분이다.

jsonimport 하여 json 파일을 사용자가 가공할 수 있는 데이터로 만드는 것이다. JSON 객체는 딕셔너리로, JSON 배열은 리스트로, JSON 문자열은 문자열로 변환이 되게 된다.

 

데이터를 수집하여 출력하는 것도 중요하지만 적절하게 크롤링을 이용하여 수집한 데이터를 저장하는 것도 중요하다.

데이터를 관리하는 방법으로는 3가지가 있는데 데이터 베이스를 이용하는 방법, 파일 스트림을 이용하는 것 또는 이메일이다. 데이터를 받을 때에 중요한 판단 중 하나가 데이터 자체를 받을 것인가 아니면 데이터에 대한 url 링크를 받을 것인가이다. 둘 다 장점과 단점이 있다. 한두 번 이상 실제로 데이터를 보거나 읽는다면 파일을 통해 데이터를 저장하고 그것이 아니리면 데이터베이스를 유지하면서 수집을 해야 한다.

 

대표적인 데이터 저장 방식 중 하나는 CSV 파일이다.

파이썬은 csv 라이브러리를 이용하여 CSV 파일을 쉽게 수정하거나 만들 수 있다. csv의 대표적인 함수로는 writerwriterow가 있다.

writer는 쓸 준비를 하는 것이고 writerow는 줄 별로 데이터를 입력해 나가는 것을 의미한다.

 

MySQL은 가장 널리 쓰이는 오픈 소스 관계형 데이터베이스 관리 시스템이다.

파이썬은 PyMySQL이라는 라이브러리를 사용하여 MySQL과 연결할 수 있게 된다. pymysqlimport 시키고 connect 함수를 사용한다. 이후 cursor를 지정해주면 MySQL과 연동할 수 있는 준비가 다 되었다. execute함수에 인자로 mysql의 명령어를 넣어주게 되면 파이썬에서 mysql을 사용할 수 있게 된다.

 

문서 인코딩은 애플리케이션이 그 문서를 읽는 방법을 지정한다.

모든 문서는 근본적으로 01로 인코딩되어 있다. 인코딩 알고리즘을 통해 문서에 대한 다양한 정보가 저장된다. 다양한 언어나 숫자에 대한 정보를 저장하고 출력하기 위해서는 UTF-8에 대한 것을 알아야한다. 원래 영어를 사용하는 입장에서는 ASCII를 사용하게 되면 인코딩을 쉽게 할 수 있었다. 하지만 다른 나라의 언어를 담고 있지는 못했다. 이에 대해 Universal Character Set Transformation Format 8 bitUTF-8을 사용하여 인코딩을 하면 모든 글자를 표현할 수 있게 된다.

 

CSV 파일을 읽는 방법은 다양하다.

원하는 파일을 직접 내려 받은 후 파이썬에 그 파일의 위치를 알려주는 방법, 파일을 내려 받는 파이썬 스크립트를 작성해서 읽고 원한다면 삭제도 하는 방법, 파일을 문자열 형식으로 읽은 후 StringIO 객체로 바꿔서 파일처럼 다루는 방법이 있다.

첫 번째 두 번째 방법도 좋지만 메모리에서 쉽게 처리하는 방법으로는 세 번째 방법이 사용된다. urlopen을 통해 csv 파일을 받은 후 StringIO 객체로 만든다. 이 후 reader를 이용해서 해당 객체를 읽어 오는 것이다. 이는 파이썬의 리스트 객체로 만들어지게 된다.

 

PDF 파일은 아주 널리 쓰이고 특히 공식 서식에 많이 사용된다.

pdfminer 라이브러리를 사용하여 PDF 파싱을 할 수 있게 된다.

 

데이터를 읽는 것에 있어서 지저분한 데이터를 정리하는 것도 중요하다.

웹 크롤러에서 제한된 곳에서만 데이터를 수집할 수는 없을 때가 많다. 잘못된 구두점, 일관성 없는 대문자 사용, 줄 바꿈, 오타 등 지저분한 데이터는 웹의 큰 문제점 중 하나이다. 이를 잘 수정하여 문제가 발생하지 않게 하는 것이 중요하다.

 

예외 처리를 위한 코드는 존재한다. urllib에서 HTTPError에 대한 import를 시킨 후 tryexcept를 사용하면 쉽게 예외를 처리할 수 있게 된다. 하지만 이뿐만 아니라 예상 못한 상황에 대해 대응하는 방어적 코드도 중요하다. n-그램은 텍스트나 연설에서 연속으로 나타난 단어 n개를 말한다. splitappend 함수를 이용하게 되면 입력 문자열을 받고 연속된 단어를 n개의 숫자에 맞게 배열을 만들어 반환할 수 있다. 하지만 여기에는 쓸모없는 다양한 값들이 같이 들어가게 된다. 이는 정규 표현식을 사용하여 이스케이프 문자를 제거하고 유니코드 문자도 제거하면 정리가 될 것이다. 또한 대괄호나 구두점, 한 글자로 된 단어 같은 경우에도 쓸모없는 정보이기 때문에 코드를 작성하여 제거를 시켜야한다.

 

 

위의 작업을 직접 코드로 작성할 수도 있지만 오픈리파인 같은 프로그램을 사용하면 빠르고 쉽게 정리할 수 있다.

 

웹 크롤링을 통해 자연어를 읽고 쓰는 기능을 수행할 수 있다. 이는 웹 크롤링 파트에서 데이터를 분석하는 부분에 속한다고 볼 수 있다. 웹에서 자연어로 구성된 데이터를 가지고 와서 이를 사용자가 원하는 결과물로 바꾸어주는 과정을 의미한다. 또한 일반적으로 구글이나 네이버와 같은 검색 엔진을 통해 사용자가 원하는 정보를 찾는데 이런 과정이 어떻게 진행하는지에 대해서도 알 수 있게 된다. 검색창에 자연어를 입력하였는데 어떻게 해서 사용자가 원하는 결과물로 잘 도달할 수 있는지가 바로 웹 크롤링의 과정에 속하게 된다.

 

우선 웹 상에 있는 자연어 데이터에는 불필요한 정보들이 매우 많다. 우리는 이러한 정보를 정리하고 필요한 정보만을 추출할 필요가 있다. 그래서 n-그램의 방법과 re 라이브러리를 이용하여 \n 나 기타 필요 없는 어구들, 유니코드로의 변환 등등을 통해 어느 정도 언어를 걸러 낼 수 있다. 그런데 이뿐만 아니라 사용자가 자연어를 볼 때 thea 와 같은 관사나 of, in, to와 같은 전치사에 대한 정보보다는 다른 명사의 형태를 내용적 측면에서 중요시 여기는 경우가 많다. 이런 어휘들도 걸러주는 것이 필요할 때에는 미국 용어 중 가장 많이 사용되는 언어를 모아놓은 리스트를 통해 걸러줄 수 있다.

 

마르코프 모델은 어떤 특정 사건이 다른 특정 사건에 뒤이어, 일정 확률로 일어나는 대규모 무작위 분포를 분석할 때 자주 쓰인다. 이는 자연어를 처리할 때 매우 도움이 된다. 이를 사용하게 되면 특정 어휘 뒤에 따라 나오는 어휘가 어떤 것이 많은 지를 알 수 있게 되고 완벽하진 않지만 이를 통해 문장을 새로 완성시키는 과정도 가능하게 된다.

 

너비 우선 탐색은 방향성 그래프에서 가장 짧은 경로를 찾을 때 가장 좋은 방법이다. 방향성 그래프는 마르코프 모델과 같이 하나의 방향으로 체인을 연결하듯이 연결되는 형태를 말한다. 이러한 예시로는 웹 페이지의 링크를 따라가서 최종적으로 원하는 링크에 도달하는 방법이 있다. 너비 우선 탐색은 우선 시작 페이지에서 출발하는 링크를 모두 검색하고 검색한 링크에 목표 페이지가 들어 잇지 않으면 링크된 페이지에서 다시 또 다른 링크된 페이지를 찾는다. 링크 단계 제한에 걸리거나, 목표 페이지를 찾을 때까지 반복하여 진행하게 된다.

 

NLTK는 자연어 툴킷으로 영어 텍스트의 부분마다 식별하고 태킹하도록 설계된 파이썬 라이브러리 모음이다.

NLTK는 텍스트에서 단어 숫자, 단어 빈도, 어휘 다양도 같은 통계적 정보를 생성할 때 아주 유용하다. 특히 비교적 광범위한 텍스트 분석이 필요하다면 이 모듈에서 제공하는 많은 함수를 통해 원하는 거의 모든 작업을 수행할 수 있다.

 

 

 

 

 

 

1. 웹 페이지 분석 절차

 

웹 페이지에서 정보를 추출하는 절차는 대략적으로 다음과 같다.

 

1) HTML 문서를 불러온다.

대부분의 정보들은 첫 번째 요청에 의해 가져오는 HTML 문서에 있으므로

파이썬에서는 웹 페이지를 요청하기 위한 urlopenrequests 라이브러리가

있다.

2) HTML을 분석한다.

앞 단계에서 분석할 대상을 가져왔으므로 이를 분석하여 데이터를 추출한다.

, 분석할 대상을 가지고 있는 웹 페이지의 주소 등을 분석한다.

3) 정보를 처리한다.

두 번째 단계에서 원하는 정보를 찾아서 가져왔으므로 가져온 정보를 다시

처리해야 한다.

, 사용자가 원하는 작업을 하는 단계이다.

 

 

 

2. 분석 대상 가져오기

 

1) urllib 모듈을 사용하여 정보 가져오기

urllib는 파이썬의 표준 모듈로 파이썬이 설치되면 사용할 수 있는 모듈이다.

 

from urllib.request import urlopen

html = urlopen('http://www.naver.com')

doc = html.read().decode('utf-8')

print(doc)

 

위 코드를 실행하면 NAVER 홈페이지가 HTML 코드로 출력된다.

, doc에서 html 문서로 읽어들이기 때문이다.

 

2) requests 모듈을 사용하여 가져오기

이 모듈은 urllib에서 제공하는 기능을 더 쉽고 간단하게 사용할 수 있는 모듈

이다.

또한, 이 모듈은 설치 후 사용해야 한다.

 

위에서 작성한 코드를 requests로 변경하여 실행하면 위와 동일하게 NAVER

홈페이지가 HTML 코드 형식으로 출력된다.

 

import requests

r=requests.get('http://www.naver.com')

print(r.text)

 

requests는 대부분의 번거로운 작업들을 내부적으로 처리해 준다.

, urllib 모듈을 사용하면 데이터를 요청하고 가져온 데이터를 읽어서 디코딩

해 주어야 하는데, requests는 문서의 내용을 파악하여 필요한 인코딩 방식에

따라 디코딩 작업을 자동으로 해준다.

 

 

3. 분석 방법

 

HTML을 분석하는 방법은 간단하게 str 타입의 findindex를 이용할 수 있고 정규식을 이용할 수도 있다.

 

더 좋은 방법은 HTML이 트리구조를 가지고 있는 구조적인 문서이므로 HTML 문서 구조를 분석할 수 있는 beautifulsoup 라이브러리를 사용하는 것이다.

, 웹 페이지의 HTML 문서를 파싱(Parsing)하기 위해 사용하는 라이브러리이며, BeautifulSoup 모듈을 import하여야 하는데 모듈명은 bs4 이다. bs4 모듈이 import 된 후, bs4.BeautifulSoup(HTML문서) 생성자를 호출하여 BeautifulSoup 객체를 생성하여 사용한다.

 

이 라이브러리를 사용하려면,

bs4안에 beautifulsoup이라는 라이브러리를 사용해야 하며, beautifulsoup(또는 beautifulsoup4) 라이브러리는 http.parserxml을 이용하여 html이나 xml을 분석할 수 있고 다양한 방식으로 데이터를 조회할 수 있다.

 

beautifulsoup 라이브러리를 설치하는 방법은

1) 파이참 실행 후

[File] [setting] [project Interpreter] [python 3.6]

+(plus 버튼) ["beautifulsoup4"]을 검색하여 설치한다.

 

2) Pythonscripts 폴더에서 pip3 명령으로 설치한다.

 

pip3 install beautifulsoup4

 

3) Anaconda3를 이용하여 설치한다.

anaconda3 프롬프트를 실행한 후 conda 명령으로 설치한다.

 

conda install beautifulsoup4

 

 

#coding:euc-kr

import requests

r=r = requests.get('https://en.wikipedia.org/wiki/Main_Page')

html_doc = r.text

 

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc)

 

BeautifulSoup 클래스에 html 문서 스트링을 전달해주면 된다.

 

#coding:euc-kr

import requests

r = requests.get('https://en.wikipedia.org/wiki/Main_Page')

html_doc = r.text

 

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc, 'lxml')

 

css_path = '#mp-otd > ul > li > a'

tags = soup.select(css_path)

 

ret = []

for tag in tags:

year = tag["title"]

desc = tag.parent.get_text().split('',1)[1]

ret.append("{}: {}".format(year, desc))

 

 

 

4. 처리 방법

 

1~3 항목과 같이 데이터를 가져오면 데이터는 모두 메모리에 올라오게 되며, 이러한 데이터들은 다양하게 처리할 수 있다.

 

1) DB나 파일로 저장한다.

2) 데이터를 인포그래픽으로 만든다.

3) 데이터를 이용하여 빅데이터 처리를 한다.

4) 데이터가 변경되었음을 메일로 알린다.

5) SNS에 데이터를 올린다.

 

 

 

 

 

5. 크롤링 활용

 

 

[1] 웹 이미지 크롤링

 

웹 이미지를 크롤링하는 방법은 다음과 같이 두 가지로 분류한다.

 

1) 첫 번째 방법

url로 표기된 네트워크 객체를 지역 파일로 가져오는 함수인 urlretrieve

사용한다.

 

cmd 창에서 "pip3 install requests"를 설치한다.

네이버나 또는 원하는 웹 사이트에 접속하여 이미지 링크를 복사한다.

파이참을 이용해서 코드를 구현한다.

 

import urllib.request

 

# naver 홈페이지에서 이미지 링크를 복사

url = "https://ssl.pstatic.net/tveta/libs/1173/1173551

/cd062c53dbb106a5f2bd_20180115165020097.jpg"

 

savename ='imagetest.jpg'# 이미지를 저장할 파일명 지정

 

urllib.request.urlretrieve(url, savename)

print('save..........')

 

위 코드를 실행한 후 Window 탐색기를 열어 Python 폴더를 확인하면 소스에서

지정한 파일명으로 이미지 파일이 생성된 것을 확인할 수 있으며, 실행시키면

다음과 같이 이미지 파일이 출력된다.

 

[결과]

 

 

 

 

 

 

2) 두 번째 방법

리소스를 읽어오는 함수인 urlopen 함수를 사용하여 urlopen에 원하는 주소를

넣어 데이터를 가져온다.

 

첫 번째 방법과 같이 다음 소스를 작성하여 실행하면 위 소스와 동일한 결과를

얻을 수 있다.

#coding:euc-kr

import urllib.request

 

# naver 홈페이지에서 이미지 링크를 복사

url = "https://ssl.pstatic.net/tveta/libs/1173/1173551

/cd062c53dbb106a5f2bd_20180115165020097.jpg"

 

savename ='imagetest1.jpg'# 이미지를 저장할 파일명 지정

 

mem = urllib.request.urlopen(url).read()

with open(savename, mode = "wb") as frank: # wb = write binary

frank.write(mem)

print("저장되었습니다")

 

* open() 메소드는 파일을 생성하거나 새로 만들 때 사용하는 메소드이며,

open(파일 이름, 파일 열기 모드) 그리고 close()가 붙어야 open() 메소드가

정상적으로 종료된다.

하지만 with open()으로 사용하면 close()를 사용하지 않아도 정상적으로

종료된다.

 

[2] HTML 크롤링

 

#coding:euc-kr

from bs4 import BeautifulSoup

html ="""

<html><body>

<h1>삼순이 호떡집</h1>

<p>호떡의 생명은 밀가루인가?</p>

<p>충남의 랜드마크 - 천안아산역에서 5분거리</p>

</html></body>

"""

# 파싱을 통해서 한 줄로 나열된 소스를 보기 쉽게 한다.

soup = BeautifulSoup(html, 'html.parser')

h1 = soup.html.body.h1

p1 = soup.html.body.p

p2 = p1.next_sibling.next_sibling

print("h1 = " + h1.string)

print("p1 = " + p1.string)

print("p2 = " + p2.string)

 

[결과]

 

 

 

 

 

 

 

 

 

 

#coding:euc-kr

from bs4 import BeautifulSoup

html ="""

<html><body>

<div id = "food">

<h1> 초밥이 먹고 싶을 때 </h1>

<ul class = "items">

<li> 유부초밥</li>

<li> 연어초밥</li>

<li> 광어 사시미</li>

</ul>

</div>

</body></html>

"""

# 제목 부분 추출하기

soup = BeautifulSoup(html,'html.parser')

# 하나만 수집 div id =food 안에 h1을 가져온다.

h1= soup.select_one("div#food > h1")

print( "h1 =" ,h1.string) # string은 문자만 추출 해낸다.

# 목록 부분 추출

# ul class = items안에 li를 모두 가져온다.

li_list = soup.select("div#food > ul.items > li" )

for li in li_list:

print("li = ", li.string) # string = 문자만 가져온다.

 

[결과]

 

 

 

728x90
반응형