티스토리 뷰

phantom - Fast NodeJS API for PhantomJS

이 문서는 2017-09-18 에 작성되었습니다.

변경이 많지 않은 프론트엔드를 만들고, 테스트 프로그램을 작성하기 위해 찾아보니 PythonSelenium 을 사용해 Chrome Driver 또는 PhantomJS, Firefox Driver 등을 사용해 테스트 코드를 작성한 후 자동으로 테스트 할 수 있었습니다.(Jenkins 씨를 통해서)

샘플로 메인 페이지만 하고, 작성하였는데 문제는 Chrome Driver 와 같이 GUI를 보면서 할 수 있는 드라이버들은 문제가 없었는데 Headless 인 PhantomJS 의 같은 경우는 너무 느리고, Chrome Dirver 에서 잘 되던 기능들이 안되는 문제가 있었습니다.

아무래도, Python Context 위에 Selenium 을 올리고 RPC 와 같은 통신 방법으로 각 드라이버들을 제어하는 방식이 느리고, 공통 메서드 래핑에 문제가 있었던거 같았습니다. 자세한 분석은 능력 부족으로 인해 하지 않고, 다른 대안을 찾아보려고 했습니다.

우선 GUI를 지원하지 않는 리눅스 서버에서 테스트할 환경이어서 PhantomJS를 사용해야만 했습니다. PhantomJS 자체를 사용하는 방법도 있었는데, 생각보다 깔끔하지 못했습니다. 이래 저래 답답하여, 어떤 언어 환경에 PhantomJS를 라이브러리 처럼 불러와 직접 코딩하는 방법이 빠르고 에러등의 예외처리도 처리하기가 쉬울거 같아 찾아보니 NodeJS 에 라이브러리처럼 불러와 코딩할 수 있는 오픈소스가 있었습니다.

그럼 패키지를 설치하고 사용법에 대해 알아보겠습니다.

사전 지식들

PhantomJS 를 사용하기 위해 필요한 사전 지식들입니다.

  • Javascript Syntax
  • ES6 Syntax and Promise
  • HTML DOM
  • NodeJS and NPM

미리 설치해야할 것들

PhantomJS Pakcage 를 설치하기 위해서 NodeJS 가 설치되어 있어야합니다. 지원하는 버전은 다음과 같습니다.

  • v6.x
  • v5.x
  • older than 5.x

NodeJS 설치하러가기

설치하기

v6.x and 이후 버전에서:

$ npm install phantom --save

v5.x:

$ npm install phantom@3 --save

v5.x 보다 낮은 nodejs 버전:

$ npm install phantom@2 --save

Note! 위의 --save 옵션을 모르신다면, npm init 명령에 대해 알아보신 후 하셔도 좋습니다.
공식 홈페이지: npm init
설명이 괜찮은 블로그

PhantomJS 버전별 동작 방식은?

v1.0.xNodeJSphantomJS 사이에 통신을 하려고 dnode 를 수단으로 사용했습니다. 이 방법은 보안 제한 상승과 clusterpm2 를 사용할 때 동작시키지 못했습니다.

v2.0.xphantomJS 프로세스와 통신하기 위해 sysinsysout 를 사용하여 완전히 재작성하였습니다. 이것은 clusterpm2 박스 밖에서 동작하죠. 만일 메시지를 보길 원한다면, DEBUG=true를 예외처리 부분에 추가하면 됩니다. 예: DEBUG=true node path/to/test.js. 이 새로운 코드는 매우 단순하고 깔끔합니다. PhantomJS 는 객체와 페이지에 대한 모든 메시지를 대신 전달하는 작은 라이브러리(shim)과 함께 시작합니다.

간단하게 시작하기

간단하게 프로젝트 디렉토리를 만들어, npm 환경으로 구성한 다음 phantomJS 패키지를 설치한 다음에 네이버에 접근해 보도록하겠습니다.

환경 만들기:

$ mkdir phantomjs-sample
$ cd phantomjs-sample

phantomjs-sample$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (phantomjs-sample)
version: (1.0.0)
description: sample for phantomjs begin
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/cho/Desktop/Dev/projects/getting_start/phantomjs-sample/package.json:

{
  "name": "phantomjs-sample",
  "version": "1.0.0",
  "description": "sample for phantomjs begin",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

phantomjs-sample$ ls -all

drwxr-xr-x  3 cho  staff  102  9 18 11:53 .
drwxr-xr-x  3 cho  staff  102  9 18 11:52 ..
-rw-r--r--  1 cho  staff  238  9 18 11:53 package.json

phantomjs-sample$_

package.json 파일 하나 생성되었네요.

이렇게 환경을 구상한 다음 phantomJS 패키지를 설치해보죠:

phantomjs-sample$ npm install phantom --save

설치가 완료가 되면 (오래 걸릴거예요) 파일(index.js)을 하나 생성해서 다음과 같은 내용으로 채워 넣어요. :

var phantom = require('phantom');
var _ph, _page, _outObj;

phantom
  .create()
  .then(ph => {
    _ph = ph;
    return _ph.createPage();
  })
  .then(page => {
    _page = page;
    return _page.open('https://stackoverflow.com/');
  })
  .then(status => {
    console.log(status);
    return _page.property('content');
  })
  .then(content => {
    console.log(content);
    _page.close();
    _ph.exit();
  })
  .catch(e => console.log(e));

개발자들이 좋아하는 스택오버플로에 가서 html 본문 내용을 그대로 출력해주는 내용이예요.

결과:

phantomjs-sample$ node ./index.js

를 실행하면, stackoverflow의 html 코드들이 출력될겁니다.

클러스터링과 완전 비동기 방식 동작등은 깃허브에 가서 /example 밑에 다양한 샘플들이 있으니 참조하시면되요.

Pooling

PhantomJS 를 생성하는 (phantomjs.create()) 데 많은 시간이 걸립니다. 그래서 객체를 생성한 다음 재사용하여 속도를 향상(약 8배) 시킬 수 있습니다. 그 패키지는 여기에서 확인 할 수 있습니다.

참조할 것들.