프로그램언어+/┗Crawling

웹 크롤러 - 파이썬(1일)

logthink 2018. 7. 10. 23:04

개발을 하는 과정을 즐기고 알아보자.


○ 파이썬 사용하는 이유

1. 다양한 모듈

2. 가독성

3. 빠른 개발


○ 목표 : 웹 크롤러


○ 개요 ' 웹 크롤러란?'


방대한 웹을 돌아다니며 각종 정보를 수집하는 주체.

혹은 스파이더링(spidering)라고 부르기도한다.

검색 엔진과 같은 여러 사이트에서는 데이터의 최신 상태 유지를 위해 항상 웹 크롤링을 합고,

웹 크롤러는 대체로 방문한 사이트의 모든 페이지의 복사본을 생성하는 데 사용된다.

또한 크롤러는 링크체크나 HTML 코드 검증과 같은 웹 사이트의 자동 유지 관리 작업을 위해 사용되기도 하며, 자동 이메일 수집과 같은 웹 페이지의 특정 형태의 정보를 수집하는데도 사용한다.

웹 크롤러는 봇이나 소프트웨어 에이전트의 한 형태로 대개 시드(seed)라고 불리는 URL리스트에서부터 시작하며 페이지의 모든 하이퍼링크를 인식, URL 리스트를 갱신하여 확인한다.


이번에는 웹 페이지 보다 특정 게시판을 탐색해보자.


○ PIP

HTTP통신부터 html문서 파싱까지 크롤러가 동작하는데, 필요한 기능들을 내가 만들지 않고, 지원되는 모듈들을 쓰자.

이때 모듈을 가져오는 툴을 pip 이며, (파이썬 패키지들 유지, 관리하는 워킹 그룹 PyPA(Python Package Index)에서 추천하는 패키지 관리 프로그램) 필요한 패키지를 찾아 설치 및 관리하자.


이제 pip 이용해 크롤러가 동작하는데 필요한 모듈들을 설치해보자.

먼저, 특정 url에 해당하는 웹페이지를 웹서버에서 받아오기 위해 http 요청 기능이 필요하다.

작성할 크롤러에서 http 요청을 하는데 있어 고급 기능이 필요치 않아 표준 모듈에 포함되어 있는 urllib 모듈을 사용하자.

(표준모델은 패키지관리자로 설치할 필요X)


다음 필요한 것이 BeautifulSoup 패키지.

이것은 html로 작성된 웹페이지를 파싱하기 위해 필요하다.

표준 모듈로 HTMLParser가 있긴하지만 설정이 번거롭고 복잡해 BeautifulSoup 사용하자.



Python 코드 작성은 개발활경에서 사용(Visual Studio, 서브라임, 아톰 등)


크롤러를 만들기 위해 가장 먼저해야할 것은 웹 서버에 웹 페이지를 요청하고 응답받을 수 있게 해야한다.

이때 http 프로토콜에 따라 요청, 응답받기 위해 파이썬의 표준 모듈에 포함되어 있는 urllib 모듈을 사용할 것이다.


간단하게 웹서버에 http 요청을 하고 응답받은 결과를 살펴볼 수 있는 코드를 작성해보자.


import urllib.request


if __name__ == "__main__":

    

    req = urllib.request.Request("http://www.daum.net")

    data = urllib.request.urlopen(req).read()


    print(data)


    f = open("./response.txt","w")

    f.write(str(data))

    f.close()


1.1 http 요청과 응답 소스


우선은 daum 메인을 http 프로토콜에 따라 요청하고 응답받았다.

daum서버로 부터 응답받은 데이터는 txt 파일에 저장해 두었다.


여기서 우리는 추상화된 인터페이스에 따라 우리가 반드시 입력해주어야 할 정보를 입력하고 원하는 동작을 할 함수를 호출하면 urllib 모듈은 입력받은 매개변수에 따라 내부적으로 적절한 설정과 과정을 통해 daum 웹 서버에 요청을 하게 되고 응답을 반환 받는다.


코드에서 req 는 urllib.request.Request 형으로 

Request 클래스는 URL 요청과 관련한 정보를 담고 있는 추상화된 클래스이다.

http 통신을 하는데 있어서 헤더값을 설정함으로서

특정 브라우저 처럼 행세도 하고 컨텐트 타입에 따라 bytes 객체를 담아 사용도한다.


우리가 호출한 urlopen 함수는 urllib.request 모듈에 정의되어 있다.
urlopen 함수는 url를 입력받거나 Request 객체를 입력받는다.
위 코드는 Request 객체 req를 만들어 전달하고 있지만 Request 객체를 만들지 않고 url 그대로 urlopen 함수에 전달할 수 도 있다.
당장에는 간단한 코드로 요청과 응답을 받았지만, 다음부터는 바로 http header 정보를 설정해 사용해보자.


이번에는 앞서 언급한 http header를 설정함으로서 웹 서버가 어떻게 응답하는지 살펴보자.
아래의 코드를 실행해보면 이전과 달리 open() 함수에서 데이터 쓰는 방법에 차이가 있다.


import urllib.request

if __name__ == "__main__":

    #hdr = {'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Accept-Encoding': 'none', 'Accept-Language': 'en-US,en;q=0.8', 'Connection': 'keep-alive'}


    
    req = urllib.request.Request("http://www.daum.net")
    data = urllib.request.urlopen(req).read()

    print(data)

    f = open("./response.txt","w") #f = open("./response_iphone.html", "wb")
    f.write(str(data))
    f.close()

위 hdr을 해석해보면 아이폰 6에 설치되어 있는 사파리 웹브라우저가 http요청시 설정하는 user agent 정보이다.

웹서버에서는 header 정보를 확인해 어떤 브라우저에서 온지 확인을 하고(위 코드는 아이폰에서 사용하는 사파리 브라우저에서 온 요청이라고 생각한 것)
이처럼 이와 같은 방법으로 정상 동작 여부가 확인해보고 필요한 정보에따라 적절히 활용해보자.



○ 실제 게시판 크롤링


테스트 가능한 서비스 웹을 긁어보자.

앞써 파이썬 모듈을 이용해 http 통신을 주고 받는 과정은 생각보다 단순했다.

(Request 객체를 만들고 urlopen 메소드를 호출했을뿐)


우리는  f = open("./response1.html","wb") 통해서 html파일을 생성 후 브라우저를 통해 보이는 모습은 그다지 중요하지 않다.

데이터에 집중 하자.


우리가 생각해 볼 문제.

 ★ 이 게시판에서 무엇을 얻고 싶은것인가?

 ★ 왜 해당 게시판의 데이터를 긁는 봇을 만든 것인가?


해답은

[해답 1]. 프로젝트를 위한 재미

[해답 2]. 게시판에 새로 게시된 글을 일일이 클릭하지 않고 한번에 데이터 획득 가능

우리는 대략적으로 기능을 명시해보자.

[기능 1]. 기본적으로 우리가 만든 봇은 '게시판'에 올라온 게시물을 웹 서버에 요청할 수 있어야 한다.

[기능 2]. 이미지가 포함된 게시물인지 텍스트로만 있는지 확인해야한다.

[기능 3]. 이미지가 포함되어 있다면 첨부된 것은 PC로 저장할 수 있어야 한다.


[기능 1] 부터 해보자 

우선 다른 사용자가 올린 게시물을 웹 서버에 요청해야한다.

그래야 응답을 받고 파싱해서 확인가능하다.


현재 긁어오려는 대상은 게시물들의 URL이 모두 포함되어 있다.

여러 게시물 글 만큼 URL이 있을 것이다. (URL을 파싱하기만 하면 끝)


다음은 위의 계획을 실행하기 위해 beautifulSoup 라는 모듈을 사용할 것이다


○ beautifulSoup 

HTML, XML을 분석(파싱) 해주는 라이브러리.

beautifulSoup 라이브러리에 대해서 알아보자. 

beautifulSoup에 대한 설명과 메소드 사용법에 관해서는 beautifulSoup 공식 문서를 참고하였다.


beautifulSoup는 HTML과 XML 파일에서 데이터를 읽어내는 파이썬 라이브러리로

파서 트리를 탐색, 검색, 수정하는데 간편하고 사용자가 만든 파서와 함께 사용하기 쉽다.

프로그래머들의 작업 시간을 줄여 칼퇴근을 가능케하는데 크게 기여할 것이다.


BeautifulSoup 객체를 생성하는 과정에서 파싱 작업은 완료된다.

생성자의 인수로 파싱할 문서를 전달하면 

파싱 처리 후 결과 데이터를 갖는 BeautifulSoup 객체를 반환할 것이다.


아래 코드에서는 BeautifulSoup 라이브러리를 사용해 문서를 파싱하고 그 결과를 출력하는 과정을 확인할 수 있다.

prettify 함수는 BeautifulSoup 에서 파싱 처리한 파서 트리를 유니코드 형태로 리턴하는 함수이다.




웹 페이지를 표현하는 html은 Markup Language로 태그, 요소, 속성 등 구성요소를 이용해 문서를 구조적으로 표현한다.


구조화된 문서는 파싱(탐색)에 효율적이다 심지어 스택이나 큐와 같은 자료구조를 이용해 일일이 내용을 파싱하지 않아도 파이썬 기본모듈로 제공되는 HTMLParser를 이용하거나 BeautifulSoup 등의 파이썬 라이브러리를 통해서 더욱 손쉽게 파싱할 수 있다.


우리는 HTMLParser를 수입(import)하는 것으로 끝낼 수 있지만 더 수월한 beautifulSoup를 수입(import) 해보자.

(공식 문서 : https://www.crummy.com/software/BeautifulSoup/bs4/doc/)


from bs4 import BeautifulSoup

<html 코드들>

soup = BeautifulSoup(html, 'html.parser')  #BeautifulSoup 객체를 가져와서 1번 인자는 html형식의 문자 2번 인자는 어떤 파서를 사용할지 지정.
soup.select_one() #하나를 선택하는 메서드
soup.select() #여러개 선택하는 메서드

소스.py

가져온 후 각종 파싱을 위한 각종 선택자를 알아보자.

태그 선택자
"h1"
"html"
아이디 선택자
"#<아이디 이름>"
클래스 선택자
".<클래스 이름>.<클래스 이름>.<클래스 이름>"
후손 선택자 (어떤 태그 아래 있는 모든녀석)
"#meigen li"         (meigen태그 아래있는 li를 선택해라)
자식 선택자 (어떤 태그 바로아래 있는 녀석)
"ul.items > li"

그리고 list_items = soup.select("ul.items > li") 직접적으로 선택 후에 for문으로 뽑을 것인데, 글자를 추출하는법은
문자일때는 test.string 으로, 내부에 요소를 선택할때는 test.attrs 이다.
ex) header.attrs["title"] 도 가능.




참고 : http://hurderella.tistory.com/96

참고 : https://www.youtube.com/watch?v=STrewyaxWq0