Puppeteer 혹은 Selenium에서 안티 캡챠 플러그인 이용하는 방법
Puppeteer와 Selenium은 브라우저 자동화 및 원활한 플러그인 자동화를 위한 주요 엔진입니다. 이 글에서 저희는 노드JS와 파이썬 프로그래밍 언어 각각에 대해 Puppeteer와 Selenium으로 이용하는 방법을 보여드리겠습니다. Puppeteer와 Selenium 둘 중에서 하나를 고른다면, 자체 환경을 위해 노드JS+Puppeteer 조합을 강력하게 추천해드립니다.
1. Dependencies를 설치하세요. 노드JS의 경우, 아래에서 npm 패키지를 설치하세요. 파이썬의 경우에는 패키지를 설치하고 이것 페이지에서 실행 가능한 "chromedriver"를 다운로드하세요. 이 드라이버 버전은 시스템에서 설치한 크롬 버전과 일치해야 합니다.
npm install adm-zip puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
pip install selenium
2. 크롬의 경우 ZIP 버전의 플러그인을 다운로드하고, 프로젝트 폴더에 압축 해제합니다. 실제 버전은 여기서에 위치합니다. 또한, 다음과 같이 프로그래밍할 수 있습니다.
//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);
})();
import urllib.request
import zipfile
url = 'https://antcpt.com/anticaptcha-plugin.zip'
# 플러그인을 다운로드하세요.
filehandle, _ = urllib.request.urlretrieve(url)
# 압축 해제하세요.
with zipfile.ZipFile(filehandle, "r") as f:
f.extractall("plugin")
3. 다음으로, ./plugin/js/config_ac_api_key.js 파일에서 API 키를 환경 설정하세요. 고객지원 페이지에서 API 키를 찾을 수 있습니다. API 키가 작동하도록 하려면, 잔액이 플러스여야 합니다.
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!')
}
from pathlib import Path
import zipfile
# 환경설정 파일에서 API 키를 설정하세요.
api_key = "API_KEY_32_BYTES"
file = Path('./plugin/js/config_ac_api_key.js')
file.write_text(file.read_text().replace("antiCapthaPredefinedApiKey = ''", "antiCapthaPredefinedApiKey = '{}'".format(api_key)))
# 플러그인 디렉토리를 다시 plugin.zip으로 압축하세요.
zip_file = zipfile.ZipFile('./plugin.zip', 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./plugin"):
for file in files:
path = os.path.join(root, file)
zip_file.write(path, arcname=path.replace("./plugin/", ""))
zip_file.close()
4. 플러그인으로 브라우저를 시작하세요. Puppeteer의 경우 저희는 'puppeteer-extra' 패키지를 위해 플러그인 'puppeteer-extra-plugin-stealth'를 추천합니다. 이 패키지는 웹 자동화 Chromium 브라우저의 모든 신호를 숨겨줍니다.
//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();
})();
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_extension('./plugin.zip')
browser = webdriver.Chrome('./chromedriver', options=options)
5. 타겟 페이지로 탐색하시고 필요한 경우에는 양식을 작성해주세요. 플러그인에서 리캡챠를 자동으로 골라내고 리캡챠를 풀기 시작할 겁니다.
(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);
})();
browser.get('https://anti-captcha.com/demo/?page=recaptcha_v2_textarea')
# filling form
browser.find_element_by_css_selector('#login').send_keys('Test login')
browser.find_element_by_css_selector('#password').send_keys('Test password')
6. 다음으로 넘어갈 부분은 조금 복잡합니다. 일부 웹 양식은 사용자에게 리캡챠를 푼 이후에 제출 버튼을 클릭하고, 일부 콜백을 이용하고 자동으로 제출할 것을 요청합니다. 처음에는 리캡챠가 풀린 이후에 제출 버튼을 누르고자 합니다. 적절한 시기에 제출 버튼을 누르려면, 셀렉터 .antigate_solver.solved가(이) 나타날 때까지 기다렸다가 제출 버튼을 누르시면 됩니다.
// "solved" 셀렉터가 나타날 때까지 기다려주세요.
await page.waitForSelector('.antigate_solver.solved').catch(error => console.log('failed to wait for the selector'));
console.log('리캡챠 풀림');
// 제출 버튼을 누르세요.
await Promise.all([
page.click('#submitButton'),
page.waitForNavigation({ waitUntil: "networkidle0" })
]);
console.log('태스크 완료됨(리캡챠 우회 양식).');
# "solved" 셀렉터가 나타날 때까지 기다려주세요.
webdriver.support.wait.WebDriverWait(browser, 120).until(lambda x: x.find_element_by_css_selector('.antigate_solver.solved'))
# 제출 버튼을 누르세요.
browser.find_element_by_css_selector('#submitButton').click()
이렇게 하면 완료됩니다. 양식을 채우고, 리캡챠를 풀고 우회하는 거죠. 전체 코드 샘플은 다음과 같습니다.
// 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('리캡챠 풀림');
// 제출 버튼을 누르세요.
await Promise.all([
page.click('#submitButton'),
page.waitForNavigation({ waitUntil: "networkidle0" })
]);
console.log('리캡챠 풀림');
})();
import urllib.request
import zipfile
import os
from pathlib import Path
from selenium import webdriver
# 플러그인을 다운로드하세요.
url = 'https://antcpt.com/anticaptcha-plugin.zip'
filehandle, _ = urllib.request.urlretrieve(url)
# 압축 해제하세요.
with zipfile.ZipFile(filehandle, "r") as f:
f.extractall("plugin")
# 환경설정 파일에서 API 키를 설정하세요.
api_key = "YOUR_API_KEY_HERE!"
file = Path('./plugin/js/config_ac_api_key.js')
file.write_text(file.read_text().replace("antiCapthaPredefinedApiKey = ''", "antiCapthaPredefinedApiKey = '{}'".format(api_key)))
# 플러그인 디렉토리를 다시 plugin.zip으로 압축하세요.
zip_file = zipfile.ZipFile('./plugin.zip', 'w', zipfile.ZIP_DEFLATED)
for root, dirs, files in os.walk("./plugin"):
for file in files:
path = os.path.join(root, file)
zip_file.write(path, arcname=path.replace("./plugin/", ""))
zip_file.close()
# 브라우저 시작 옵션을 설정하세요.
options = webdriver.ChromeOptions()
options.add_extension('./plugin.zip')
# 브라우저 시작 옵션을 설정하세요.
browser = webdriver.Chrome('./chromedriver', options=options)
# 타겟 페이지로 탐색하세요.
browser.get('https://anti-captcha.com/demo/?page=recaptcha_v2_textarea')
# 양식을 채우세요.
browser.find_element_by_css_selector('#login').send_keys('Test login')
browser.find_element_by_css_selector('#password').send_keys('Test password')
# "solved" 셀렉터가 나타날 때까지 기다려주세요.
webdriver.support.wait.WebDriverWait(browser, 120).until(lambda x: x.find_element_by_css_selector('.antigate_solver.solved'))
# 제출 버튼을 누르세요.
browser.find_element_by_css_selector('#submitButton').click()
보너스: 크롬에서 플러그인으로 브라우저 확장 프로그램을 지원하지 않기 때문에 숨김 모드로 플러그인을 시행하는 트릭이 있습니다. 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