단일 페이지 어플리케이션(SPA : Single Page Application)은 웹을 구성하는 정적인 리소스를 한번에 다운로드하고 새로운 페이지 구성에는 필요한 데이터만 서버로부터 받거나 클라이언트에서 만들어
사용합니다. 즉, 동적으로 웹 페이지를 구성합니다. 동적으로 웹 페이지가 만들어진다는 점에서 웹 크롤링에 어려움이 있습니다. SPA 구성에는 주로 backbone,
angular, React와 같은 javascript
framework를 이용하는데 backbonejs를 이용한 예제가 http://backbonejs.org/examples/todos/index.html
입니다.
“book4”와 “book5”는
사용자의 키 입력과 javascript가 실행되면서 만들어진 요소입니다. 웹브라우저에서 제공하는 “소스 보기” 기능을 이용해도 “book4”와 “book5”가 보이지 않아 당황스럽습니다.
http://backbonejs.org/examples/todos/index.html를 두가지 방법으로 웹 크롤링을 합니다. 하나는 selenium과 파이썬을 이용하는 방법이고 다른 하나는 casperjs를 이용한 방법입니다.
selenium를 사용하려면 웹 브라우저에 맞은 드라이버를 구해야 합니다. 여기서는 firefox를 사용할 것이라 https://github.com/mozilla/geckodriver/releases 에서 geckodrive 이름으로 된 드라이버를 구합니다. 압축을 푼 geckodriver.exe는 파이썬 실행 프로그램과 같은 디렉토리에 둡니다.
그리고 아래 파이썬 프로그램을 실행합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = webdriver.Firefox() # keckodriver in the same directory with this script driver.implicitly_wait(3) driver.get('http://backbonejs.org/examples/todos/index.html') driver.find_element_by_id("new-todo").send_keys('book4') driver.find_element_by_id("new-todo").send_keys(Keys.ENTER) driver.find_element_by_id("new-todo").send_keys('book5') driver.find_element_by_id("new-todo").send_keys(Keys.ENTER) print(driver.page_source) print("** body **") print(driver.find_element_by_tag_name("body").text) print("** li **") elements = driver.find_elements_by_tag_name("li") for e in elements: print(e.text) |
라인 7까지는 웹 페이지 접속을 위한 기본 내용입니다. 라인10에서 14까지는 “book4”와 “book5”를 등록하는 과정인데 “ENTER”도 따로 보내야 함에 주의합니다. 라인 16의 “driver.page_source”를 출력하면 웹 페이지의 html 소스를 볼 수 있는데 아래와 같이 “book4”와 “book5” 내용이 들어 있습니다. 이것은 웹 브라우저에서 볼 수 없었던 내용이고 이런 방식으로 동적으로 생성된 웹 페이지를 크롤링할 수 있습니다. 라인 19 에서는 <body> 태그에 담긴 텍스트, 라인 22이하에서는 <li> 태그에 담긴 텍스트를 정확하게 가져 옵니다.
selenium는 웹 브라우저를 구동하고 프로그램으로 조작하는 반면에
caspejs는 웹 브라우저 없이 작동합니다. 대신 casperjs는
phantomjs라는 프로그램 위에서 작동해야 합니다. 윈도우용으로 http://phantomjs.org/download.html 에서 MS 윈도우용 phantomjs를 먼저 구합니다.
압축을 풀면 example를 포함한 여러 파일이 있지만 bin 디렉토리의 phantomjs.exe 만 있으면 됩니다. 원도우 환경 변수 PATH에 phantomjs.exe가 있는 디렉토리를 지정하도록 합니다.
윈도우용 casperjs 는 http://casperjs.org/ 의 바로 첫 화면에서 다운로드 가능하고 모든 파일의 압축을
풀고 환경 변수 PATH에 casterjs.exe가 있는 폴더 이름을
포함하도록 합니다. 도스창에서 “casper”만 실행해서 casperjs와 phanatomjs 버전이
나오면 설치는 성공입니다.
아래는http://backbonejs.org/examples/todos/index.html를 읽기 위한 casperjs 프로그램입니다. javascript인데 파일 이름은 todo3.js라면 도스창에서 “casterjs
todo3.js”로 실행합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | var casper = require('casper').create(); var bd = []; var links = []; function getBody () { var b = __utils__.findAll('body'); return b; } function getTodos () { var todos = document.querySelectorAll('#todo-list li'); return todos; } casper.start("http://backbonejs.org/examples/todos/index.html", function() { links = this.evaluate(getTodos); this.echo('num: ' + links.length); }); // add new todo casper.then(function() { this.sendKeys('#new-todo', 'book1'); this.sendKeys('#new-todo', casper.page.event.key.Enter); }); // add new todo casper.then(function() { this.sendKeys('#new-todo', 'book2'); this.sendKeys('#new-todo', casper.page.event.key.Enter); }); casper.then(function() { bd = this.evaluate(getBody); this.echo(bd[0].outerHTML); links = this.evaluate(getTodos); this.echo('num: ' + links.length); this.echo("0: " + links[0].outerHTML); this.echo("1: " + links[1].outerHTML); }); casper.run(); |
첫번째 라인은 casperjs를 시작하기 위한 casper 변수 선언입니다. 나중에 다시 언급하겠지만 casperjs를 test 모드에서 실행하려면 이 줄을 없애야 합니다. 라인6이하와 라인 11이하는 각각 <body>와 <todo-list><li> 태그를 찾기 위한 함수 선언입니다. 라인 16에서 URL를 인수로 하는 casper.start()로 시작합니다. selenium와 달리 명시적으로 wait를 주지 않아도 페이지가 올라올 때까지 기다립니다. casper API 실행 순서는 start() -> then() -> ... -> run() 순입니다. 기본적으로 비동기로 실행되는 then이 순서를 정하고 run()은 반드시 존재해야 합니다. 라인 17에서 getTodos()를 호출해서 <todo-list><li> 태그를 가진 요소가 몇개인지 알아 봅니다. 라인 22이하와 라인 28이하를 통해 요소 두개를 추가합니다. 라인 34에서 <body> 태그에 요소를 찾아 HTML 형식으로 출력합니다. 라인 36이하는 다시 <todo-list><li> 태그를 가진 요소를 찾습니다.
여기에 casperjs의 버그가 있습니다. 첫번째 요소를 찾아 출력하는데에는 문제가 없는데 두번째 요소 경우 아예 에러도 없이 아무런 내용이 나오지 않습니다. 아무런 에러가 없다는 것이 더 문제인데 이 에러를 보려면 casperjs를 test
모드에서 실행해야 합니다. test
모드는 자동적으로 변수 선언이 있기 때문에 첫번째 라인에서 선언문과 겹칩니다.
그래서 첫번째 라인의 casper 변수 선언은 comment처리하고 “casperjs test
todo3.js”로 실행해야 비로소 links[1]은 null임을 알 수 있습니다.
selenium는 java와 파이썬과
같이 여러 언어도 지원하고 파싱을 위한 beautifulsoup까지 사용할 수 있는 반면에 casperjs는 javascript로만 가능하고 따라서 웹 크롤링에 도움이 되는 지원 도구도 거의 없습니다.
selenium와 파이썬으로 크롤링하다가 한계나 극복할 수 없는 에러만 생기지 않으면 계속 selenium를 사용할 것 같습니다.
댓글 없음:
댓글 쓰기