프로그래밍/JS, Node.js

[Node.js] Selenium 사용법을 익혀보자

Lou Park 2021. 1. 3. 23:49

Selenium은 웹 드라이버를 통해 웹 브라우저를 자동화하여 웹 테스트를 도와주는 툴이다. 말은 착하지만 매크로를 만들거나 크롤링 할때도 도움이 된다. ^^ 실제 브라우저를 띄우고 코드를 수행할 수 있으며, 일반적으로 크롤링 하는 방식보다 훨씬 더 상호작용을 사람같이! 할 수 있다. 페이지가 다 뜰때까지 기다리고, 클릭하고, 입력하는 등...

그러면 Node.js 에서 selenium을 사용하는 방법을 익혀보자.

 

패키지 설치

먼저 selenium-webdriver 패키지를 설치해야한다.

npm i selenium-webdriver

파이어 폭스 브라우저로 Selenium을 실행해야 할 경우, geckodriver를 설치해야한다.

npm i geckodriver

실행환경이 Ubuntu일 경우 geckodriver 설치는 다음과 같이한다.

wget https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-linux32.tar.gz
tar -xvzf geckodriver-v0.24.0-linux32.tar.gz
chmod +x geckodriver

 

코드 작성

세팅이 끝났으니, selenium으로 네이버를 실행하고 종료하는 코드를 작성 해 보겠다. (app.js)

const {Builder, By, Key, until} = require('selenium-webdriver');

(async function example() {
    let driver = await new Builder()
    .forBrowser('firefox')
    .build();
    try {
        // 네이버 실행
        await driver.get('https://www.naver.com/');
    }
    finally{
        driver.quit();
    }
})();

node app.js 를 실행해보면 Firefox 브라우저가 열리고, 네이버에 접속 후 창이 꺼질 것이다.

그럼 유용한 메소드 전부를 활용해서 네이버 검색어 결과를 추리는 코드를 작성 해 보겠다.
세부 사항은 주석을 참조하면된다.

  • Input에 텍스트 입력하기
  • 요소 클릭하기
  • Loop를 돌며 Element 내용 읽기
const {Builder, By, Key, until} = require('selenium-webdriver');

(async function example() {
    let driver = await new Builder()
    .forBrowser('firefox')
    .build();
    try {
        // 네이버 실행
        await driver.get('https://www.naver.com/');

        // Javascript를 실행하여 UserAgent를 확인한다.
        let userAgent = await driver.executeScript("return navigator.userAgent;")

        console.log('[UserAgent]', userAgent);

        // 네이버 검색창의 id는 query이다. By.id로 #query Element를 얻어온다.
        let searchInput = await driver.findElement(By.id('query'));

        // 검색창에 '회 숙성하는 법'을 치고 엔터키를 누른다.
        let keyword = '회 숙성하는 법'
        searchInput.sendKeys(keyword, Key.ENTER);

        // css selector로 가져온 element가 위치할때까지 최대 10초간 기다린다.
        await driver.wait(until.elementLocated(By.css('#header_wrap')), 10000);

        // total_tit라는 클래스 명을 가진 element들을 받아온다.
        let resultElements = await driver.findElements(By.className('total_tit'));
        console.log('[resultElements.length]', resultElements.length)

        // 검색 결과의 text를 가져와서 콘솔에 출력한다.
        console.log('== Search results ==')
        for (var i = 0; i < resultElements.length; i++) {
            console.log('- ' + await resultElements[i].getText())
        }

        // 검색결과의 첫번째 링크를 클릭한다.
        if (resultElements.length > 0) {
            await resultElements[0].click();
        }

        // 4초를 기다린다.
        try {
            await driver.wait(() => { return false; }, 4000);
        } catch (err) {

        }   
    }
    finally{
        // 종료한다.
        driver.quit();
    }
})();

크롤링에 자주 쓰이는 메소드들은 거의 나온것 같다! 이 코드를 실행시키면, 네이버에 회 숙성하는 법을 검색하고 제일 첫번째 글을 클릭한뒤, 종료하게 된다. 실행시 로그는 다음과 같다.

[UserAgent] Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:83.0) Gecko/20100101 Firefox/83.0
[resultElements.length] 11
== Search results ==
- 입질의 추억 :: 생선회를 숙성하는 방법(숙성회를 먹는 이유) - 입질의 추억
- 다시마 숙성회, 광어 곤부지메 만드는 방법
- 참돔 숙성회 만드는 방법
- 생선회 맛있게 먹는법!! Part.4 숙성회가 맛있는 이유
- 사가정 맛집 도미회가 맛있는슌락 맛있는 참돔 마쓰가와 숙성법
- 참치회 인터넷 주문 배송 :: 참치 해동/숙성 하는 법. 참치몰 후기
- 노량진수산시장 모듬회, 숙성회, 방어회 싸게먹는법
- 연남동 맛집, 경양마구로! 숙성시킨 참치회로 만든 회덮밥! 최고! (+ 맛있게 먹는 )
- 연안부두1월제철수산물 10키로이상 특대방어 가격 시세 숙성대방어회먹는법 장군씨푸드
- 연어 숙성 하는법::생연어 곤부지메해서 회 떠먹기
- 이렇게 맛있는 12월의 수산물이라니! 과메기부터 굴, 방어까지 겨울 수산물 레시피

실제 검색결과 이미지

 

For Non-gui systems

GUI가 없는 Ubuntu등 서버OS에서 이 코드를 실행하려고하면, (내 기억이 맞다면) 아래와 같은 오류가 날 것이다. 이때는 headless 모드로 selenium을 실행시켜 주어야한다. headless로 설정하게 되면 실제 브라우저가 보이지 않는다.

selenium.common.exceptions.WebDriverException: Message: invalid argument: can't kill an exited process

Firefox Options에서 headless 모드를 설정할 수 있다. options에는 이 외에도 다른 유용한 설정을 할 수 있다. 아래는 몇가지 예시다.

const {Builder, By, Key, until} = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');

(async function example() {
    let driver = await new Builder()
    .forBrowser('firefox')
    .setFirefoxOptions(
        new firefox.Options()
        // headless 모드 사용
        .headless()
        // 창 크기 설정
        .windowSize({
            width: 640,
            height: 480
        })
        // custom user agent 설정하기
        .setPreference("general.useragent.override", "custom-user-agent")
    )
    .build();
    try {
        // ... your code
    }
    finally{
        driver.quit();
    }
})();

 

맺으며

오늘 Selenium을 가지고 놀면서 Cloudflare로 보호되는 웹을 뚫어보려고 삽질을 했는데 결국 실패해서 그냥 저냥 알게된 사용법을 블로그에 올려봤다. ㅠㅠ 그래도 실제 브라우저로 딱딱딱 집어주는 코드를 작성할 수 있다니 다음에 유용한일이 생기겠지...
마지막으로 Javascript용 Selenium document 주소 뿌리고 가겠다.

https://www.selenium.dev/selenium/docs/api/javascript/index.html