Menu

Puppeteer 혹은 Selenium에서 안티 캡챠 플러그인 이용하는 방법

Puppeteer와 Selenium은 브라우저 자동화 및 원활한 플러그인 자동화를 위한 주요 엔진입니다. 이 글에서 저희는 노드JS와 파이썬 프로그래밍 언어 각각에 대해 Puppeteer와 Selenium으로 이용하는 방법을 보여드리겠습니다. Puppeteer와 Selenium 둘 중에서 하나를 고른다면, 자체 환경을 위해 노드JS+Puppeteer 조합을 강력하게 추천해드립니다.

1. Dependencies를 설치하세요. 노드JS의 경우, 아래에서 npm 패키지를 설치하세요. 파이썬의 경우에는 패키지를 설치하고 이것 페이지에서 실행 가능한 "chromedriver"를 다운로드하세요. 이 드라이버 버전은 시스템에서 설치한 크롬 버전과 일치해야 합니다.

Javascript
Python
npm install adm-zip puppeteer puppeteer-extra puppeteer-extra-plugin-stealth

2. 크롬의 경우 ZIP 버전의 플러그인을 다운로드하고, 프로젝트 폴더에 압축 해제합니다. 실제 버전은 여기서에 위치합니다. 또한, 다음과 같이 프로그래밍할 수 있습니다.

Javascript
Python
//npm install adm-zip
const https = require('https')
const fs = require('fs');
const AdmZip = require("adm-zip");

const pluginURL = 'https://antcpt.com/anticaptcha-plugin.zip';

(async () => {
    // 플러그인을 다운로드하세요.
    await new Promise((resolve) => {
        https.get(pluginURL, resp => resp.pipe(fs.createWriteStream('./plugin.zip').on('close', resolve)));
    })
    // 압축 해제하세요.
    const zip = new AdmZip("./plugin.zip");
    await zip.extractAllTo("./plugin/", true);
})();

3. 다음으로, ./plugin/js/config_ac_api_key.js 파일에서 API 키를 환경 설정하세요. 고객지원 페이지에서 API 키를 찾을 수 있습니다. API 키가 작동하도록 하려면, 잔액이 플러스여야 합니다.

Javascript
Python
const apiKey = 'API_KEY_32_BYTES';
if (fs.existsSync('./plugin/js/config_ac_api_key.js')) {
    let confData = fs.readFileSync('./plugin/js/config_ac_api_key.js', 'utf8');
    confData = confData.replace(/antiCapthaPredefinedApiKey = ''/g, `antiCapthaPredefinedApiKey = '${apiKey}'`);
    fs.writeFileSync('./plugin/js/config_ac_api_key.js', confData, 'utf8');
} else {
    console.error('plugin configuration not found!')
}

4. 플러그인으로 브라우저를 시작하세요. Puppeteer의 경우 저희는 'puppeteer-extra' 패키지를 위해 플러그인 'puppeteer-extra-plugin-stealth'를 추천합니다. 이 패키지는 웹 자동화 Chromium 브라우저의 모든 신호를 숨겨줍니다.

Javascript
Python
//npm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());

(async () => {
    const browser = await puppeteer.launch({
        headless: false,
        ignoreDefaultArgs: [
            "--disable-extensions",
            "--enable-automation"
        ],
        args: [
            '--disable-web-security',
            '--disable-features=IsolateOrigins,site-per-process',
            '--allow-running-insecure-content',
            '--disable-blink-features=AutomationControlled',
            '--no-sandbox',
            '--mute-audio',
            '--no-zygote',
            '--no-xshm',
            '--window-size=1920,1080',
            '--no-first-run',
            '--no-default-browser-check',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--enable-webgl',
            '--ignore-certificate-errors',
            '--lang=en-US,en;q=0.9',
            '--password-store=basic',
            '--disable-gpu-sandbox',
            '--disable-software-rasterizer',
            '--disable-background-timer-throttling',
            '--disable-backgrounding-occluded-windows',
            '--disable-renderer-backgrounding',
            '--disable-infobars',
            '--disable-breakpad',
            '--disable-canvas-aa',
            '--disable-2d-canvas-clip-aa',
            '--disable-gl-drawing-for-tests',
            '--enable-low-end-device-mode',
            '--disable-extensions-except=./plugin',
            '--load-extension=./plugin'
        ]
    });
    const page = await browser.newPage();
})();

5. 타겟 페이지로 탐색하시고 필요한 경우에는 양식을 작성해주세요. 플러그인에서 리캡챠를 자동으로 골라내고 리캡챠를 풀기 시작할 겁니다.

Javascript
Python
(async () => {
    const url = 'https://anti-captcha.com/demo/?page=recaptcha_v2_textarea';
    const login = 'Test login';
    const password = 'Test password';

    try {
        await page.goto(url, {
            waitUntil: "networkidle0"
        });
    } catch (e) {
        console.error('err while loading the page: '+e);
    }
    // 탐색 타임아웃 오류를 비활성화하세요.
    await page.setDefaultNavigationTimeout(0);

    await page.$eval('#login', (element, login) => {
        element.value = login;
    }, login);
    await page.$eval('#password', (element, password) => {
        element.value = password;
    }, password);

})();

6. 다음으로 넘어갈 부분은 조금 복잡합니다. 일부 웹 양식은 사용자에게 리캡챠를 푼 이후에 제출 버튼을 클릭하고, 일부 콜백을 이용하고 자동으로 제출할 것을 요청합니다. 처음에는 리캡챠가 풀린 이후에 제출 버튼을 누르고자 합니다. 적절한 시기에 제출 버튼을 누르려면, 셀렉터 .antigate_solver.solved가(이) 나타날 때까지 기다렸다가 제출 버튼을 누르시면 됩니다.

Javascript
Python
// "solved" 셀렉터가 나타날 때까지 기다려주세요.
await page.waitForSelector('.antigate_solver.solved').catch(error => console.log('failed to wait for the selector'));
console.log('{{ $t('articles.how-to-integrate.code-comments.recaptcha-solved') }}');

// 제출 버튼을 누르세요.
await Promise.all([
    page.click('#submitButton'),
    page.waitForNavigation({ waitUntil: "networkidle0" })
]);
console.log('태스크 완료됨(리캡챠 우회 양식).');

이렇게 하면 완료됩니다. 양식을 채우고, 리캡챠를 풀고 우회하는 거죠. 전체 코드 샘플은 다음과 같습니다.

Javascript
Python
// first run the following to install required npm packages:
//
// npm install adm-zip follow-redirects puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
//
//
const https = require('follow-redirects').https;
const fs = require('fs');
const AdmZip = require("adm-zip");

const apiKey = 'YOUR_API_KEY_HERE!';
const pluginURL = 'https://antcpt.com/anticaptcha-plugin.zip';
const url = 'https://anti-captcha.com/demo/?page=recaptcha_v2_textarea';
const login = 'Test login';
const password = 'Test password';
let page = null;


const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());

(async () => {
    // 플러그인을 다운로드하세요.
    await new Promise((resolve) => {
        https.get(pluginURL, resp => resp.pipe(fs.createWriteStream('./plugin.zip').on('close', resolve)));
    })
    // 압축 해제하세요.
    const zip = new AdmZip("./plugin.zip");
    await zip.extractAllTo("./plugin/", true);

    // 환경설정 파일에서 API 키를 설정하세요.
    await new Promise((resolve, reject) => {
        if (fs.existsSync('./plugin/js/config_ac_api_key.js')) {
            let confData = fs.readFileSync('./plugin/js/config_ac_api_key.js', 'utf8');
            confData = confData.replace(/antiCapthaPredefinedApiKey = ''/g, `antiCapthaPredefinedApiKey = '${apiKey}'`);
            fs.writeFileSync('./plugin/js/config_ac_api_key.js', confData, 'utf8');
            resolve();
        } else {
            console.error('plugin configuration not found!')
            reject();
        }
    });

    // 브라우저 시작 옵션을 설정하세요.
    const options = {
        headless: false,
        ignoreDefaultArgs: [
            "--disable-extensions",
            "--enable-automation"
        ],
        args: [
            '--disable-web-security',
            '--disable-features=IsolateOrigins,site-per-process',
            '--allow-running-insecure-content',
            '--disable-blink-features=AutomationControlled',
            '--no-sandbox',
            '--mute-audio',
            '--no-zygote',
            '--no-xshm',
            '--window-size=1920,1080',
            '--no-first-run',
            '--no-default-browser-check',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--enable-webgl',
            '--ignore-certificate-errors',
            '--lang=en-US,en;q=0.9',
            '--password-store=basic',
            '--disable-gpu-sandbox',
            '--disable-software-rasterizer',
            '--disable-background-timer-throttling',
            '--disable-backgrounding-occluded-windows',
            '--disable-renderer-backgrounding',
            '--disable-infobars',
            '--disable-breakpad',
            '--disable-canvas-aa',
            '--disable-2d-canvas-clip-aa',
            '--disable-gl-drawing-for-tests',
            '--enable-low-end-device-mode',
            '--disable-extensions-except=./plugin',
            '--load-extension=./plugin'
        ]
    }

    try {
        // 플러그인으로 브라우저를 시작하세요.
        const browser = await puppeteer.launch();
        page = await browser.newPage();
    } catch (e) {
        console.log('could not launch browser: '+e.toString())
        return;
    }

    // 타겟 페이지로 탐색하세요.
    try {
        await page.goto(url, {
            waitUntil: "networkidle0"
        });
    } catch (e) {
        console.error('err while loading the page: '+e);
    }

    // 탐색 타임아웃 오류를 비활성화하세요.
    await page.setDefaultNavigationTimeout(0);

    // 양식을 채우세요.
    await page.$eval('#login', (element, login) => {
        element.value = login;
    }, login);
    await page.$eval('#password', (element, password) => {
        element.value = password;
    }, password);

    // "solved" 셀렉터가 나타날 때까지 기다려주세요.
    await page.waitForSelector('.antigate_solver.solved').catch(error => console.log('failed to wait for the selector'));
    console.log('{{ $t('articles.how-to-integrate.code-comments.recaptcha-solved') }}');

    // 제출 버튼을 누르세요.
    await Promise.all([
        page.click('#submitButton'),
        page.waitForNavigation({ waitUntil: "networkidle0" })
    ]);
    console.log('리캡챠 풀림');

})();

보너스: 크롬에서 플러그인으로 브라우저 확장 프로그램을 지원하지 않기 때문에 숨김 모드로 플러그인을 시행하는 트릭이 있습니다. Xvfb이라는 유틸리티를 이용하여 어플리케이션에 가상 데스크톱을 제공합니다.

# 패키지를 설치하세요.
apt-get install -y xvfb

# 디스플레이 변수를 설정하세요.
export DISPLAY=:0

# 백그라운드에서 Xvfb 데몬을 시작하세요(한 번만).
/usr/bin/Xvfb :0 -screen 0 1024x768x24 &

# 나타날 때까지 잠깐만 기다려주세요(한 번만).
sleep 5

# "xvfb-run" 접두사를 "node" 혹은 "python" 스크립트에 붙이세요.
xvfb-run node myscript.js
# 아니면
xvfb-run python myscript.py