مقدمه
در این مقاله قدمبهقدمِ فنی به روشهای عملی برای «اسکریپ کردن» سایتهایی که از PerimeterX استفاده میکنند میپردازیم. فرض میکنیم خواننده توسعهدهنده پایتون در سطح متوسط است و با مفاهیم پایه Playwright آشنایی دارد. پس از خواندن این مطلب، قادر خواهید بود ترکیبی از پروکسیهای مسکونی، تغییر هدرها، تزریق اسکریپت برای کاهش قابلتشخیص بودن و الگوهای مدیریتی (ریتری، مدیریت سشن، ذخیرهسازی کوکی) را در پروژهی وب اسکریپینگ خود پیادهسازی کنید.
درک PerimeterX و روشهای شناسایی
PerimeterX و سرویسهای مشابه از ترکیب مکانیزمهای سمت سرور و سمت کلاینت برای شناسایی رفتارهای غیرعادی استفاده میکنند. مهم است که بدانیم چه چیزهایی معمولاً بررسی میشود تا بتوانیم راهحلهای مناسب طراحی کنیم:
- آنالیز رفتاری: نرخ درخواست، الگوی ناوبری و کلیکها.
- فینگرپرینتینگ: اندازه فونتها، لیست پلاگینها، WebGL و ویژگیهای محیط مرورگر.
- چالشهای CAPTCHA: اگر سایت CAPTCHA نشان دهد، معمولاً به معنی شناسایی قویِ ترافیک است.
- پایش آیپی و DNS: ترافیک از آیپیهای دیتاسنتر سریعتر شناسایی میشود؛ آیپیهای مسکونی/موبایل معمولاً کمتر مشکوکاند.
- هدرها و کوکیها: مطابقت User-Agent و سایر هدرها با رفتار مرورگر واقعی و مدیریت سشن ضروری است.
استراتژیهای عملی برای عبور از PerimeterX
ترکیب روشها معمولاً بهترین نتیجه را میدهد؛ فقط تکیه بر یکی از آنها پایدار نیست. فهرست کلی رویکردها:
- استفاده از پروکسیهای مسکونی یا موبایل و روتیشن آیپی.
- تنظیم هدرها و User-Agent مطابق مرورگر واقعی.
- اجتناب از headless یا تقلید ویژگیهای محیطِ headed (یا اجرا در حالت headed).
- تزریق اسکریپت برای اصلاح ویژگیهای قابل فینگرپرینتِ مرورگر (مثلاً حذف navigator.webdriver).
- مدیریت سشن (ذخیره و بازخوانی کوکیها و localStorage).
- کنترل نرخ درخواست، بکآف و retry هماهنگ با سیاست سایت.
مثال عملی: Playwright (Python) — پروکسی مسکونی و هدر
# مثال: راهاندازی Playwright با پروکسی و User-Agent (async)
from playwright.async_api import async_playwright
import os
import random
PROXY_SERVER = os.getenv('PROXY_SERVER') # e.g. 'http://residential-proxy.example:8181'
PROXY_USER = os.getenv('PROXY_USER')
PROXY_PASS = os.getenv('PROXY_PASS')
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
]
async def run():
ua = random.choice(USER_AGENTS)
async with async_playwright() as pw:
browser = await pw.chromium.launch(proxy={
'server': PROXY_SERVER,
'username': PROXY_USER,
'password': PROXY_PASS,
}, headless=True)
context = await browser.new_context(user_agent=ua)
page = await context.new_page()
try:
await page.goto('https://www.zillow.com', timeout=120000)
await page.screenshot(path='zillow-proxied.png')
print('screenshot saved')
except Exception as e:
print('navigation failed:', e)
finally:
await browser.close()
# ورودیها: متغیرهای محیطی پروکسی و لیست User-Agent
# خروجی: فایلی به نام zillow-proxied.png در صورت موفقیتتوضیح خطبهخط و نقشها:
- ورودیها: PROXY_SERVER, PROXY_USER, PROXY_PASS و یک User-Agent تصادفی.
- با pw.chromium.launch(proxy=...) مرورگر با پروکسی اجرا میشود تا منبع آیپی را پنهان کند.
- در new_context هدر User-Agent تنظیم میشود تا با مرورگر واقعی مطابقت داشته باشد.
- خروجی تابع یک اسکرینشات است یا استثنا چاپ میشود؛ در محیط تولید بهتر است لاگ و retry داشته باشید.
تزریق اسکریپت برای کاهش فینگرپرینت (معادل stealth)
# مثال: تزریق JS برای پنهانسازی navigator.webdriver و اصلاح چند ویژگی
from playwright.async_api import async_playwright
import asyncio
INIT_SCRIPT = """
// حذف پرچم webdriver
Object.defineProperty(navigator, 'webdriver', {get: () => false});
// تقلید زبانها
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
// نمونهای از تعریف fake plugins
Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3]});
"""
async def stealth_example():
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=False)
context = await browser.new_context()
# اجرای اسکریپت قبل از هر صفحه تا فینگرپرینت کاهش یابد
await context.add_init_script(INIT_SCRIPT)
page = await context.new_page()
await page.goto('https://www.whatismybrowser.com/')
await page.screenshot(path='what-is-stealth.png')
await browser.close()
asyncio.run(stealth_example())توضیحات:
- این روش معادل «stealth» در Node است اما دستی انجام میشود. add_init_script باعث میشود هر صفحه قبل از اجرای کد سایت این تغییرات را داشته باشد.
- محدودیتها: این روش کشندهٔ تمام فینگرپرینتها نیست؛ باید ترکیبی از تغییرات (فونتها، WebGL، timeZone، mediaDevices و…) پیاده شود.
- نکته امنیتی: برخی سایتها بررسیهای پیچیدهتری دارند؛ تغییر بیش از حدِ خصیصهها ممکن است الگوی غیرطبیعی ایجاد کند.
ریتری، مدیریت خطا و سیاست نرخ درخواست
حتماً برای هر درخواست سیاست retry و backoff داشته باشید تا از بلاکهای موقتی و ناپایداری جلوگیری کنید و به سایت فشار نیاورید. نمونه سادهای از الگوی اکسپوننشیال:
import asyncio
async def with_retries(corofn, retries=3):
delay = 1
for i in range(retries):
try:
return await corofn()
except Exception as e:
if i == retries - 1:
raise
await asyncio.sleep(delay)
delay *= 2نکات عملی:
- از نرخمحدودکن (rate limiter) استفاده کنید؛ درخواستهای همزمان را محدود کنید.
- کوکیها و localStorage را ذخیره و بین اجراها بازخوانی کنید تا سشن واقعیتر به نظر برسد.
مطالعهی موردی: اعمال ترکیبی روی Zillow
برای سایتهایی مثل Zillow که PerimeterX به کار میگیرد، ترکیب سه مؤلفه زیر معمولاً مؤثر است:
- پروکسی مسکونی با روتیشن آیپی
- هدرها و User-Agent واقعی یا اجرای headed
- تزریق init script برای حذف navigator.webdriver و اصلاح ویژگیها
# نمونه ترکیبی: پروکسی + init script + ذخیره کوکی
from playwright.async_api import async_playwright
import os, random, json
PROXY = os.getenv('PROXY_SERVER')
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
INIT = "Object.defineProperty(navigator, 'webdriver', {get: () => false});"
async def case_study():
async with async_playwright() as pw:
browser = await pw.chromium.launch(proxy={'server': PROXY}, headless=False)
context = await browser.new_context(user_agent=UA)
await context.add_init_script(INIT)
page = await context.new_page()
await page.goto('https://www.zillow.com', timeout=120000)
# ذخیره کوکیها برای اجراهای بعدی
cookies = await context.cookies()
with open('cookies.json', 'w') as f:
json.dump(cookies, f)
await page.screenshot(path='zillow-combined.png')
await browser.close()
# خروجی: اسکرینشات و فایل cookies.jsonتوضیح: این کد ورودیهای محیطی (پروکسی)، User-Agent مشخص، و init script را میگیرد، پس از لود صفحه کوکیها را ذخیره میکند تا در اجراهای بعدی سشن ادامه یابد. این کار به دیدهشدن ترافیک بهعنوان یک کاربر واحد کمک میکند.
هشدارها، محدودیتها و مسائل اخلاقی
- قانونی و اخلاقی: قبل از اسکریپ کردن سایتها سیاست robots.txt و شرایط استفاده را بررسی کنید؛ اسکریپ بدون مجوز ممکن است پیامد حقوقی داشته باشد.
- CAPTCHA: مواجهه با CAPTCHA نشانهٔ شناسایی قوی است؛ استفاده از سرویسهای حل CAPTCHA را فقط بعد از بررسی قانونی و اخلاقی انجام دهید.
- پایداری: روشهای جلوگیری از شناسایی دائمی نیستند؛ همیشه احتمال تغییر سرویسهای آنتی-بات وجود دارد.
جمعبندی و توصیههای عملی
برای عبور از PerimeterX دنبالِ «ترکیب» باشید: پروکسی مسکونی (یا موبایل) + هدر و سشن مطابق مرورگر واقعی + اصلاح فینگرپرینت با add_init_script و در نهایت کنترل نرخ درخواست و مدیریت خطا. در پایتون، بهجای تکیه بر بستههای آماده Node مانند puppeteer-extra-plugin-stealth، باید چند تکنیک دستی را ترکیب کنید. همیشه کلیدها و شناسههای حساس را در محیط (env) نگه دارید، و سیاست ریتری/بکآف را برای پایداری پیاده کنید.





