Selenium 및 Puppeteer 탐지 마스킹에 대한 완벽한 가이드
현대의 안티봇 시스템은 JavaScript 변수부터 WebDriver의 행동 특성까지 수십 가지 신호를 통해 자동화된 브라우저를 쉽게 인식합니다. 사이트는 Cloudflare, DataDome, PerimeterX 및 자체 솔루션을 사용하여 표준 구성에서 Selenium 및 Puppeteer로부터 오는 요청의 90%까지 차단합니다.
이 가이드에서는 기본 설정부터 탐지 우회를 위한 고급 기술까지 모든 자동화 마스킹 방법을 다룰 것입니다. 대부분의 보안 시스템에 대해 작동하는 Python 및 Node.js의 코드 예제와 함께 준비된 솔루션을 제공합니다.
사이트가 자동화를 탐지하는 방법
안티봇 시스템은 여러 매개변수를 동시에 분석하여 브라우저를 검사합니다. 하나의 신호를 숨기더라도 나머지가 자동화를 드러낼 수 있습니다. 모든 탐지 방법을 이해하는 것이 효과적인 마스킹의 열쇠입니다.
WebDriver 지표
가장 간단한 탐지 방법은 자동화된 브라우저에만 존재하는 JavaScript 변수를 확인하는 것입니다:
// 이 변수들은 Selenium/Puppeteer를 드러냅니다
navigator.webdriver === true
window.navigator.webdriver === true
document.$cdc_ // ChromeDriver 전용 변수
window.document.documentElement.getAttribute("webdriver")
navigator.plugins.length === 0 // 자동화된 브라우저에는 플러그인이 없습니다
navigator.languages === "" // 언어 목록이 비어 있습니다
Cloudflare 및 유사한 시스템은 이러한 속성을 우선적으로 확인합니다. 하나라도 긍정적인 결과를 반환하면 요청이 차단됩니다.
브라우저 핑거프린팅
고급 시스템은 수십 가지 매개변수를 기반으로 고유한 브라우저 핑거프린트를 생성합니다:
- Canvas 핑거프린팅 — 숨겨진 이미지를 렌더링하고 픽셀 데이터를 분석합니다
- WebGL 핑거프린팅 — 그래픽 렌더러 및 비디오 카드의 매개변수
- Audio Context — 오디오 처리의 고유한 특성
- Fonts 핑거프린팅 — 시스템에 설치된 글꼴 목록
- Screen Resolution — 화면 해상도, 색 깊이, 사용 가능한 영역
- Timezone & Language — 시간대, 브라우저 언어, 시스템 로케일
자동화된 브라우저는 종종 이러한 매개변수의 비정상적인 조합을 가집니다. 예를 들어, Headless Chrome은 플러그인이 없지만 WebGL을 지원합니다 — 이러한 조합은 실제 사용자에게서 매우 드뭅니다.
행동 분석
현대 시스템은 행동 패턴을 추적합니다:
- 마우스 움직임 — 봇은 커서를 직선으로 이동하거나 전혀 움직이지 않습니다
- 행동 속도 — 즉각적인 양식 작성, 비인간적인 클릭 속도
- 스크롤 패턴 — 부드러운 스크롤 대신 급격한 점프
- 키보드 이벤트 — 눌림 사이의 자연스러운 지연이 없습니다
- 요청 빈도 — 행동 간의 간격이 너무 규칙적입니다
중요: DataDome 및 PerimeterX는 행동 분석을 위해 머신 러닝을 사용합니다. 그들은 수백만 개의 세션에서 학습했으며, 기술적 매개변수를 올바르게 마스킹하더라도 행동이 비자연적으로 보이면 봇을 인식할 수 있습니다.
Selenium 기본 마스킹
Selenium WebDriver는 표준 구성에서 많은 흔적을 남깁니다. Python 및 ChromeDriver의 예를 통해 탐지를 최소화하기 위한 단계별 설정을 살펴보겠습니다.
webdriver 플래그 비활성화
첫 번째 단계는 navigator.webdriver 변수를 숨기는 것입니다. 이는 Chrome DevTools 프로토콜을 통해 수행됩니다:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
# Chrome 옵션 설정
chrome_options = Options()
# 자동화 플래그 비활성화
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
# 드라이버 생성
driver = webdriver.Chrome(options=chrome_options)
# CDP를 통해 webdriver 제거
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
'''
})
driver.get('https://example.com')
User-Agent 및 기타 헤더 설정
Headless 브라우저는 종종 구식이거나 특정한 User-Agent 문자열을 사용합니다. 실제 브라우저의 최신 User-Agent를 설정해야 합니다:
# Windows의 최신 User-Agent Chrome
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
chrome_options.add_argument(f'user-agent={user_agent}')
# 마스킹을 위한 추가 인수
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-gpu')
# 실제 사용자와 같은 창 크기
chrome_options.add_argument('--window-size=1920,1080')
chrome_options.add_argument('--start-maximized')
플러그인 및 언어 추가
자동화된 브라우저는 플러그인이 없으며 종종 언어 목록이 비어 있습니다. CDP를 통해 이를 수정합니다:
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en', 'ru']
});
Object.defineProperty(navigator, 'plugins', {
get: () => [
{
0: {type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format"},
description: "Portable Document Format",
filename: "internal-pdf-viewer",
length: 1,
name: "Chrome PDF Plugin"
},
{
0: {type: "application/pdf", suffixes: "pdf", description: ""},
description: "",
filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai",
length: 1,
name: "Chrome PDF Viewer"
}
]
});
Object.defineProperty(navigator, 'platform', {
get: () => 'Win32'
});
'''
})
Selenium 설정의 전체 예
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import random
def create_stealth_driver():
chrome_options = Options()
# 기본 마스킹 설정
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
# User-Agent
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
chrome_options.add_argument(f'user-agent={user_agent}')
# 창 크기
chrome_options.add_argument('--window-size=1920,1080')
chrome_options.add_argument('--start-maximized')
driver = webdriver.Chrome(options=chrome_options)
# CDP를 통한 마스킹 스크립트
stealth_script = '''
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
Object.defineProperty(navigator, 'platform', {get: () => 'Win32'});
window.chrome = {
runtime: {}
};
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
'''
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': stealth_script
})
return driver
# 사용 예
driver = create_stealth_driver()
driver.get('https://bot.sannysoft.com/') # 탐지 테스트 사이트
탐지 우회를 위한 Puppeteer 설정
Puppeteer는 Selenium과 동일한 탐지 문제를 가지고 있습니다. 그러나 Node.js용으로는 puppeteer-extra-plugin-stealth라는 준비된 라이브러리가 있어 대부분의 마스킹 설정을 자동화합니다.
puppeteer-extra 설치
npm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
stealth 플러그인을 사용한 기본 구성
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// 마스킹 플러그인 연결
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({
headless: 'new', // 새로운 headless Chrome 모드
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu',
'--window-size=1920,1080',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process'
]
});
const page = await browser.newPage();
// 뷰포트 설정
await page.setViewport({
width: 1920,
height: 1080,
deviceScaleFactor: 1
});
// User-Agent 설정
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
// 추가 헤더 설정
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-US,en;q=0.9',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
});
await page.goto('https://bot.sannysoft.com/');
// 스크린샷 확인
await page.screenshot({ path: 'test.png' });
await browser.close();
})();
플러그인 없이 수동 설정
전체 제어를 원하거나 타사 라이브러리를 사용할 수 없는 경우 수동으로 마스킹을 설정합니다:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
// webdriver 및 기타 속성 재정의
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en']
});
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
// Chrome headless 마스킹
Object.defineProperty(navigator, 'platform', {
get: () => 'Win32'
});
window.chrome = {
runtime: {}
};
// permissions 재정의
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications' ?
Promise.resolve({ state: Notification.permission }) :
originalQuery(parameters)
);
});
await page.goto('https://example.com');
await browser.close();
})();
Cloudflare 우회를 위한 설정
Cloudflare는 고급 탐지 방법을 사용합니다. 우회를 위해서는 무작위 지연 및 행동 에뮬레이션을 추가해야 합니다:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function bypassCloudflare(url) {
const browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-web-security'
]
});
const page = await browser.newPage();
// 무작위 User-Agent
const userAgents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
];
await page.setUserAgent(userAgents[Math.floor(Math.random() * userAgents.length)]);
// 페이지로 이동
await page.goto(url, { waitUntil: 'networkidle2' });
// Cloudflare 확인 대기 (보통 5-10초)
await page.waitForTimeout(8000);
// 무작위 마우스 움직임
await page.mouse.move(100, 100);
await page.mouse.move(200, 200);
const content = await page.content();
await browser.close();
return content;
}
JavaScript 핑거프린팅 방지
JavaScript 핑거프린팅은 여러 매개변수를 기반으로 고유한 브라우저 핑거프린트를 생성하는 것입니다. webdriver를 숨겼더라도 시스템은 자동화를 식별하기 위해 수백 가지 다른 속성을 분석합니다.
주요 핑거프린팅 벡터
안티봇 시스템은 다음 매개변수를 확인합니다:
| 매개변수 | 확인되는 내용 | 탐지 위험 |
|---|---|---|
| navigator.webdriver | 자동화 플래그의 존재 | 치명적 |
| navigator.plugins | 플러그인의 수 및 유형 | 높음 |
| window.chrome | Chrome API의 존재 | 중간 |
| navigator.permissions | 브라우저 권한 API | 중간 |
| screen.colorDepth | 화면의 색 깊이 | 낮음 |
| navigator.hardwareConcurrency | CPU 코어 수 | 낮음 |
종합 마스킹 스크립트
아래 스크립트는 대부분의 문제 속성을 재정의합니다. CDP(Selenium) 또는 evaluateOnNewDocument(Puppeteer)를 통해 주입하세요:
const stealthScript = `
// webdriver 제거
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// chrome 객체 추가
window.chrome = {
runtime: {},
loadTimes: function() {},
csi: function() {},
app: {}
};
// permissions 재정의
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
parameters.name === 'notifications' ?
Promise.resolve({ state: Notification.permission }) :
originalQuery(parameters)
);
// 플러그인 마스킹
Object.defineProperty(navigator, 'plugins', {
get: () => {
return [
{
0: {type: "application/x-google-chrome-pdf", suffixes: "pdf"},
description: "Portable Document Format",
filename: "internal-pdf-viewer",
length: 1,
name: "Chrome PDF Plugin"
},
{
0: {type: "application/pdf", suffixes: "pdf"},
description: "Portable Document Format",
filename: "internal-pdf-viewer",
length: 1,
name: "Chrome PDF Viewer"
},
{
0: {type: "application/x-nacl"},
description: "Native Client Executable",
filename: "internal-nacl-plugin",
length: 2,
name: "Native Client"
}
];
}
});
// 언어
Object.defineProperty(navigator, 'languages', {
get: () => ['en-US', 'en']
});
// 플랫폼
Object.defineProperty(navigator, 'platform', {
get: () => 'Win32'
});
// 공급업체
Object.defineProperty(navigator, 'vendor', {
get: () => 'Google Inc.'
});
// Selenium의 흔적 제거
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
// 배터리 API (headless에서는 없음)
if (!navigator.getBattery) {
navigator.getBattery = () => Promise.resolve({
charging: true,
chargingTime: 0,
dischargingTime: Infinity,
level: 1
});
}
`;
WebDriver 속성 제거
ChromeDriver 및 기타 WebDriver 구현은 전역 범위에 특정 변수를 추가합니다. 이러한 변수는 cdc_ 접두사로 시작하며 보안 시스템에 쉽게 감지됩니다.
cdc 변수 감지
이러한 변수가 존재하는지 확인하는 간단한 스크립트입니다:
// 모든 cdc 변수를 찾기
for (let key in window) {
if (key.includes('cdc_')) {
console.log('WebDriver 변수가 발견되었습니다:', key);
}
}
// 일반적인 ChromeDriver 변수:
// cdc_adoQpoasnfa76pfcZLmcfl_Array
// cdc_adoQpoasnfa76pfcZLmcfl_Promise
// cdc_adoQpoasnfa76pfcZLmcfl_Symbol
// $cdc_asdjflasutopfhvcZLmcfl_
방법 1: CDP를 통한 제거
가장 신뢰할 수 있는 방법은 페이지 로드 전에 Chrome DevTools 프로토콜을 통해 변수를 제거하는 것입니다:
from selenium import webdriver
driver = webdriver.Chrome()
# 모든 cdc 변수를 제거합니다
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
// 알려진 cdc 변수를 제거합니다
const cdcProps = [
'cdc_adoQpoasnfa76pfcZLmcfl_Array',
'cdc_adoQpoasnfa76pfcZLmcfl_Promise',
'cdc_adoQpoasnfa76pfcZLmcfl_Symbol',
'$cdc_asdjflasutopfhvcZLmcfl_'
];
cdcProps.forEach(prop => {
delete window[prop];
});
// 'cdc_'가 포함된 모든 변수를 제거합니다
Object.keys(window).forEach(key => {
if (key.includes('cdc_') || key.includes('$cdc_')) {
delete window[key];
}
});
'''
})
방법 2: ChromeDriver 수정
보다 급진적인 접근 방식은 ChromeDriver의 바이너리 파일을 수정하여 cdc_ 문자열을 다른 문자열로 바꾸는 것입니다. 이렇게 하면 이러한 변수가 생성되는 것을 방지할 수 있습니다:
import re
def patch_chromedriver(driver_path):
"""
ChromeDriver를 패치하여 'cdc_'를 임의의 문자열로 교체합니다
"""
with open(driver_path, 'rb') as f:
content = f.read()
# 모든 'cdc_' 발생을 'dog_' (또는 동일한 길이의 다른 문자열)로 교체합니다
patched = content.replace(b'cdc_', b'dog_')
with open(driver_path, 'wb') as f:
f.write(patched)
print(f'ChromeDriver가 패치되었습니다: {driver_path}')
# 사용 예
patch_chromedriver('/path/to/chromedriver')
주의: ChromeDriver의 바이너리 파일을 수정하면 작동이 중단될 수 있습니다. 패치하기 전에 항상 백업을 만드세요. 이 방법은 모든 ChromeDriver 버전에서 작동하지 않을 수 있습니다.
방법 3: undetected-chromedriver 사용
undetected-chromedriver 라이브러리는 실행 시 ChromeDriver를 자동으로 패치합니다:
pip install undetected-chromedriver
import undetected_chromedriver as uc
# 자동 패치가 적용된 드라이버 생성
driver = uc.Chrome()
driver.get('https://nowsecure.nl/') # 탐지 테스트 사이트
input('Enter 키를 눌러 종료합니다...')
driver.quit()
Canvas, WebGL 및 Audio API 마스킹
Canvas, WebGL 및 Audio 핑거프린팅은 그래픽 렌더링 및 오디오 처리의 특성을 기반으로 고유한 핑거프린트를 생성하는 방법입니다. 브라우저, 운영 체제 및 하드웨어의 조합마다 고유한 결과를 제공합니다.
Canvas 핑거프린팅
시스템은 Canvas에 숨겨진 이미지를 그린 후 생성된 픽셀을 분석합니다. Headless 브라우저는 GPU 가속이 없기 때문에 비정상적인 결과를 제공하는 경우가 많습니다.
// 일반적인 Canvas 핑거프린팅 코드
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillText('Browser fingerprint', 2, 2);
const fingerprint = canvas.toDataURL();
보호를 위해 Canvas API에 무작위 노이즈를 추가할 수 있습니다:
const canvasNoiseScript = `
// Canvas에 무작위 노이즈 추가
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
const originalToBlob = HTMLCanvasElement.prototype.toBlob;
const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
// 노이즈 추가 함수
const addNoise = (canvas, context) => {
const imageData = originalGetImageData.call(context, 0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
// RGB에 최소한의 노이즈 추가 (눈에 띄지 않음)
imageData.data[i] += Math.floor(Math.random() * 3) - 1;
imageData.data[i + 1] += Math.floor(Math.random() * 3) - 1;
imageData.data[i + 2] += Math.floor(Math.random() * 3) - 1;
}
context.putImageData(imageData, 0, 0);
};
// toDataURL 재정의
HTMLCanvasElement.prototype.toDataURL = function() {
if (this.width > 0 && this.height > 0) {
const context = this.getContext('2d');
addNoise(this, context);
}
return originalToDataURL.apply(this, arguments);
};
`;
WebGL 핑거프린팅
WebGL은 비디오 카드 및 드라이버에 대한 정보를 제공합니다. Headless 브라우저는 실제 GPU 대신 SwiftShader(소프트웨어 렌더러)를 표시하는 경우가 많습니다:
const webglMaskScript = `
// WebGL 매개변수 마스킹
const getParameter = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) {
return 'Intel Inc.';
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return 'Intel Iris OpenGL Engine';
}
return getParameter.call(this, parameter);
};
// WebGL2에도 적용
if (typeof WebGL2RenderingContext !== 'undefined') {
const getParameter2 = WebGL2RenderingContext.prototype.getParameter;
WebGL2RenderingContext.prototype.getParameter = function(parameter) {
if (parameter === 37445) {
return 'Intel Inc.';
}
if (parameter === 37446) {
return 'Intel Iris OpenGL Engine';
}
return getParameter2.call(this, parameter);
};
}
`;
Audio Context 핑거프린팅
Audio API도 고유한 핑거프린트를 제공합니다. 오디오 처리에 노이즈를 추가합니다:
const audioMaskScript = `
// Audio Context에 노이즈 추가
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (AudioContext) {
const originalCreateAnalyser = AudioContext.prototype.createAnalyser;
AudioContext.prototype.createAnalyser = function() {
const analyser = originalCreateAnalyser.call(this);
const originalGetFloatFrequencyData = analyser.getFloatFrequencyData;
analyser.getFloatFrequencyData = function(array) {
originalGetFloatFrequencyData.call(this, array);
// 최소한의 노이즈 추가
for (let i = 0; i < array.length; i++) {
array[i] += Math.random() * 0.0001;
}
};
return analyser;
};
}
`;
인간 행동 모방
완벽한 기술적 마스킹이 있더라도 봇은 행동으로 자신을 드러냅니다. 머신 러닝 시스템은 마우스 움직임 패턴, 행동 속도 및 이벤트 순서를 분석합니다.
행동 간 무작위 지연
고정된 지연을 사용하지 마세요. 실제 사용자는 다양한 길이의 일시 정지를 합니다:
import random
import time
def human_delay(min_seconds=1, max_seconds=3):
"""사람을 모방한 무작위 지연"""
delay = random.uniform(min_seconds, max_seconds)
time.sleep(delay)
# 사용 예
driver.get('https://example.com')
human_delay(2, 4) # 2-4초 일시 정지
element = driver.find_element(By.ID, 'search')
human_delay(0.5, 1.5) # 입력 전 짧은 일시 정지
element.send_keys('search query')
human_delay(1, 2)
부드러운 마우스 움직임
봇은 마우스를 직선으로 이동하거나 커서를 순간이동합니다. 실제 사용자는 가속 및 감속이 있는 곡선 경로를 만듭니다:
// Puppeteer: 부드러운 마우스 움직임
async function humanMouseMove(page, targetX, targetY) {
const steps = 25; // 중간 점의 수
const currentPos = await page.evaluate(() => ({
x: window.mouseX || 0,
y: window.mouseY || 0
}));
for (let i = 0; i <= steps; i++) {
const t = i / steps;
// 부드러움을 위해 easing 사용
const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
const x = currentPos.x + (targetX - currentPos.x) * ease;
const y = currentPos.y + (targetY - currentPos.y) * ease;
await page.mouse.move(x, y);
await page.waitForTimeout(Math.random() * 10 + 5);
}
// 위치 저장
await page.evaluate((x, y) => {
window.mouseX = x;
window.mouseY = y;
}, targetX, targetY);
}
// 사용 예
await humanMouseMove(page, 500, 300);
await page.mouse.click(500, 300);
자연스러운 스크롤
실제 사용자는 부드럽게 스크롤하며 콘텐츠를 읽기 위해 멈춥니다:
async function humanScroll(page) {
const scrollHeight = await page.evaluate(() => document.body.scrollHeight);
const viewportHeight = await page.evaluate(() => window.innerHeight);
let currentPosition = 0;
while (currentPosition < scrollHeight - viewportHeight) {
// 무작위 스크롤 단계 (200-500px)
const scrollStep = Math.floor(Math.random() * 300) + 200;
currentPosition += scrollStep;
// 부드러운 스크롤
await page.evaluate((pos) => {
window.scrollTo({
top: pos,
behavior: 'smooth'
});
}, currentPosition);
// 콘텐츠를 "읽기" 위한 일시 정지 (1-3초)
await page.waitForTimeout(Math.random() * 2000 + 1000);
}
}
// 사용 예
await page.goto('https://example.com');
await humanScroll(page);
자연스러운 텍스트 입력
사람들은 다양한 속도로 입력하며 오타를 내고 이를 수정합니다:
async function humanTypeText(page, selector, text) {
await page.click(selector);
for (let char of text) {
// 키 입력 간 무작위 지연 (50-200ms)
const delay = Math.random() * 150 + 50;
await page.waitForTimeout(delay);
// 5% 확률로 오타 발생
if (Math.random() < 0.05) {
// 무작위 문자 입력
const wrongChar = String.fromCharCode(97 + Math.floor(Math.random() * 26));
await page.keyboard.type(wrongChar);
await page.waitForTimeout(100 + Math.random() * 100);
// 삭제 (Backspace)
await page.keyboard.press('Backspace');
await page.waitForTimeout(50 + Math.random() * 50);
}
await page.keyboard.type(char);
}
}
// 사용 예
await humanTypeText(page, '#search-input', 'example search query');
완전한 익명을 위한 프록시 통합
브라우저 마스킹은 모든 요청이 동일한 IP 주소에서 발생하면 무용지물입니다. 안티봇 시스템은 각 IP에서의 요청 수를 추적하고 의심스러운 활동을 차단합니다. 프록시는 모든 진지한 자동화의 필수 요소입니다.
프록시 유형 선택
다양한 작업에는 다양한 유형의 프록시가 필요합니다:
| 프록시 유형 | 장점 | 적용 |
|---|---|---|
| 프록시 유형 1 | 장점 1 | 적용 1 |
| 프록시 유형 2 | 장점 2 | 적용 2 |
| 프록시 유형 3 | 장점 3 | 적용 3 |
준비된 라이브러리 및 도구
자동화 마스킹을 위한 여러 준비된 라이브러리와 도구가 있습니다. 이들은 일반적인 문제를 해결하고 마스킹을 쉽게 해줍니다.
Selenium Stealth
Selenium Stealth는 Selenium의 탐지를 최소화하기 위한 여러 기능을 제공합니다. 이 라이브러리를 사용하여 자동화된 브라우저의 흔적을 숨길 수 있습니다.
Puppeteer Extra
Puppeteer Extra는 Puppeteer의 기능을 확장하는 플러그인 시스템입니다. 이 시스템을 통해 다양한 마스킹 기능을 쉽게 추가할 수 있습니다.
탐지 수준 테스트
자동화 마스킹이 효과적인지 확인하려면 탐지 수준을 테스트해야 합니다. 여러 도구를 사용하여 자동화된 브라우저가 탐지되는지 확인할 수 있습니다.
결론
Selenium 및 Puppeteer를 사용한 자동화는 강력한 도구이지만, 탐지 우회를 위해서는 적절한 마스킹 기술이 필요합니다. 이 가이드를 통해 다양한 방법을 배우고, 효과적인 마스킹을 통해 자동화의 성공률을 높일 수 있습니다.