2015. 10. 12. 13:48ㆍProgramming/python
대부분의 웬만한 웹사이트는 Beautiful soup를 사용하여 parsing을 하면 손쉽게 데이터를 가져올 수 있다.
그런데 간혹 웹사이트들 중 Javascript에서 html을 만들어서 실행되는 동적페이지가 있다.
곤란하게도 Beautiful soup에서는 Javascript를 실행해주지 않으며, 그 결과로 parsing도 제대로 동작하지 않느다.
이를 해결하기 위해서는 Javascript가 rendering 된 후에 parsing을 할 수 있는 방법을 찾아야 하며 아래 사이트는 그에 대한 해결 방안을 3가지 방법으로 제시하였다.
https://www.quora.com/Can-beautifulsoup-scrape-javascript-rendered-webpages
내가 사용한 방법은 위의 세 가지 중 PhantomJS를 이용한 방법을 사용하였다.
우선 PhantomJS를 설치해 보자.
환경은 우분투 14.04를 기준으로 작성하였다.
처음에는 PhantomJS 실행을 위한 기본 라이브러리들을 설치한다.
$> sudo apt-get install build-essential g++ flex bison gperf ruby perl libsqlite3-dev libfontconfig1-dev libicu-dev libfreetype6 libssl-dev libpng-dev libjpeg-dev python libx11-dev libxext-dev
설치가 완료되었으면, git 저장소에 있는 PhantomJS 소스 파일을 가져온 후, 빌드를 해준다.
$> git clone git://github.com/ariya/phantomjs.git
$> cd phantomjs
$> git checkout 2.0
$> ./build.sh
빌드가 완료되면 bin 폴더 안에 phantomjs라는 실행 파일이 생성될 것이다. 이제 이 파일을 이용해서 동적 웹페이지를 호출하면 된다.
이제 본격적으로 PhantomJS를 어떻게 사용하는지를 살펴보자.
PhantomJS를 사용하기 위해서는 실행 가능한 Javascript 파일이 하나 필요하다.
나는 아래와 같은 내용의 Javascript 파일을 만들었다.
var page = require('webpage').create();
var system = require('system');
var fs = require('fs');
if (system.args.length === 1){
console.log('Usage: loadspeed.js <some URL>');
phantom.exit();
}
var address = system.args[1];
page.open(address, function(status){
if (status) {
fs.write('test.html', page.content, 'w');
}
phantom.exit();
});
로딩될 동적 페이지 주소를 파라미터로 받아서 test.html을 만들어 주는 Javascript 파일이다.
$> ./phantomjs test.js <someURL>
위의 명령을 입력하면 동적페이지가 로딩된 test.html의 이름을 가진 파일이 생성된다.
이제 이렇게 만들어진 html 파일을 beautiful soup에서 parsing하면 크롤링 작업이 완료된다.
나 같은 경우에는 phantomjs 실행 명령 자체를 python 코드에 내재해서 코드를 작성하였다.
import urllib
import urllib2
import os
import sys
import logging
from bs4 import BeautifulSoup
reload(sys)
sys.setdefaultencoding('utf-8')
def getWebSite(link) :
os.system("phantomjs test.js '" + link + "'")
try:
f = open('test.html')
tt = f.read()
soup = BeautifulSoup(tt)
except urllib2.HTTPError, e:
logging.exception('http Error : ' + e.reason)
pass
except urllib2.URLError, e:
logging.exception('url Error : ' + e.reason)
pass