ProxynetProxynet

Puppeteer و CAPTCHA: چرا ظاهر می‌شود و چگونه کم می‌شود؟

تاریخ انتشار:

11 دقیقه مطالعه

Acar Diveroli
نویسنده: Acar Diveroli
شبکه کپچا با چند خانه علامت‌خورده و نشانگر ماوس
فهرست مطالب

Puppeteer کتابخانه‌ای برای Node.js است که به شما امکان می‌دهد Chrome را از درون کد کنترل کنید: صفحه باز می‌کند، فرم پر می‌کند، از صفحه تصویر می‌گیرد و تا بارگذاری محتوای پویا صبر می‌کند. از آزمون‌های سرتاسری تا تهیه گزارش در کارهای بسیاری به کار می‌رود. اما با بزرگ‌تر شدن خودکارسازی، بیشتر تیم‌ها با مانع یکسانی روبه‌رو می‌شوند: CAPTCHA.

این نوشته درباره «حل کردن» کپچا نیست؛ درباره فهم اینکه چرا ظاهر می‌شود و جلوگیری از فعال شدن بی‌دلیل آن در یک خودکارسازی مشروع است. کپچا روشی است که مالک سایت با آن می‌گوید «از این ترافیک مطمئن نیستم»؛ خواندن درست این نشانه همیشه پایدارتر از رقابت با آن است. در پایان نوشته یک پیکربندی کارای Puppeteer، یک فهرست بررسی و کارهایی که نباید انجام دهید آمده است.

انواع کپچا و منطق فعال شدن آن

بیشتر کپچاهایی که امروز می‌بینید صفحه «چراغ‌های راهنمایی را در تصویر انتخاب کنید» نیستند. سه شکل رایج وجود دارد:

  • تأیید نامرئی. هنگام بارگذاری صفحه، محیط مرورگر و رفتار در پس‌زمینه امتیاز می‌گیرند؛ کاربر چیزی نمی‌بیند. اگر امتیاز پایین باشد، به مرحله‌ای دیدنی می‌رود.
  • تأیید با یک کلیک. کادر «من ربات نیستم» یا تأیید سبکی مانند Cloudflare Turnstile. بیشتر کاربران واقعی با یک کلیک از آن می‌گذرند.
  • معمای تصویری یا تعاملی. مرحله‌ای که وقتی امتیاز خیلی پایین است نمایش داده می‌شود و تلاش انسانی می‌خواهد.

این سه شکل مرحله‌های مختلف یک سامانه امتیازدهی هستند. یعنی پرسش این نیست که کپچا «آمد» یا «نیامد»، بلکه این است که امتیاز شما از کدام آستانه گذشت. هر اقدامی که امتیاز را پایین نگه دارد باعث می‌شود اصلاً به مرحله دیدنی نرسید.

چرا کپچا ظاهر می‌شود؟

محافظت‌های نوین در برابر ربات به یک قاعده نگاه نمی‌کنند؛ نشانه‌های بسیاری را با هم امتیاز می‌دهند. در نشست‌های Puppeteer این عوامل بیش از همه امتیاز را بالا می‌برند:

1. نوع و اعتبار نشانی IP

اگر درخواست از یک IP دیتاسنتر بیاید، از همان ابتدا نسبت به درخواستی از یک کاربر خانگی عادی مشکوک‌تر شمرده می‌شود. باز شدن نشست‌های زیاد از یک IP در زمانی کوتاه یا سوءاستفاده پیشین از آن نشانی، امتیاز را بیشتر هم می‌کند. تفاوت انواع IP را در نوشته تفاوت پروکسی مسکونی و دیتاسنتر توضیح داده‌ایم.

2. سرعت و الگوی درخواست‌ها

انسان‌ها صفحه‌ها را می‌خوانند، پیمایش می‌کنند و صبر می‌کنند. نشستی که در هر ثانیه ده‌ها صفحه باز می‌کند و در هر صفحه دقیقاً در همان زمان همان کلیک را انجام می‌دهد به رفتار انسان شبیه نیست. حتی انتظار با فاصله ثابت هم یک الگوست؛ انتظارهای انسانی نامنظم‌اند.

3. ردپای مرورگر بدون رابط

مرورگری که با خودکارسازی باز شده، در تنظیمات پیش‌فرض ممکن است ردپاهایی داشته باشد که خودش را لو می‌دهد. شناخته‌شده‌ترین آن‌ها true بودن ویژگی navigator.webdriver است؛ این ویژگی در استاندارد دقیقاً برای نشان دادن مرورگری که زیر خودکارسازی است وجود دارد. اندازه‌های پنجره غیرواقعی (پیش‌فرض Puppeteer برابر 800×600 است)، ویژگی‌های ناموجود مرورگر و مقدارهای User-Agent ویژه حالت بدون رابط هم جلب توجه می‌کنند.

4. نمایه ناسازگار

یک IP از تهران با مرورگری که زبان en-US و منطقه زمانی نیویورک دارد؛ یا یک نشست که اثر انگشتش در هر درخواست تغییر می‌کند. تناقض نشانه‌ها با یکدیگر از هرکدام به‌تنها مشکوک‌تر است. چگونگی شکل‌گیری اثر انگشت را در نوشته اثر انگشت مرورگر چیست؟ بررسی کرده‌ایم.

5. نبودن پیشینه نشست

مرورگری که در هر اجرا از صفر باز می‌شود و هیچ کوکی ندارد، مانند کاربری به نظر می‌رسد که نخستین بار از سایت دیدن می‌کند. این به‌تنها مشکل نیست؛ اما در ترکیب با نشانه‌های دیگر امتیاز را بالا می‌برد. کوکی نشستی که پیش‌تر از یک تأیید گذشته، در بازدیدهای بعدی امتیاز را پایین می‌آورد.

6. تحلیل رفتاری

برخی محافظت‌ها فقط به لحظه ورود نگاه نمی‌کنند، بلکه حرکت نشانگر و ثبات تعامل را در طول نشست می‌سنجند. این رویکرد نسل تازه را در نوشته Cloudflare Precursor با جزئیات شرح داده‌ایم.

علت‌ها و اقدام‌ها در یک جدول

علتنشانهاقدام مشروع
IP دیتاسنترنوع ASN، اعتبار IPپروکسی مسکونی یا ISP
سرعت بالابسامد درخواست، فاصله ثابتانتظار متغیر، هم‌زمانی کم
ردپای بدون رابطnavigator.webdriver، پنجره پیش‌فرضپنجره واقع‌گرایانه، حالت بدون رابط به‌روز
نمایه ناسازگارتناقض مکان IP با زبان یا منطقه زمانیتنظیم نمایه بر پایه IP
نشست بدون پیشینهکوکی خالی، نمایه تازهuserDataDir ماندگار
تغییر IP درون نشستکوکی یکسان، نشانی متفاوتIP با نشست ثابت یا IP ثابت

کپچا را از راه‌های مشروع چگونه کاهش دهیم؟

هدف مشترک اقدام‌های زیر پنهان کردن خودکارسازی شما نیست، بلکه جلوگیری از ایجاد بدگمانی بی‌جا است.

سرعت را کم کنید و درخواست‌ها را پخش کنید

مؤثرترین و ارزان‌ترین اقدام همین است. میان رفتن از صفحه‌ای به صفحه دیگر انتظار متغیر بگذارید، تعداد زبانه‌های باز هم‌زمان را محدود کنید و کار را در ساعت‌های مختلف روز پخش کنید. کم کردن بار سایت مقصد هم برای شما مانع کمتر است و هم برای طرف مقابل هزینه کمتر.

javascript
const pause = (min, max) =>
  new Promise((resolve) => setTimeout(resolve, min + Math.random() * (max - min)));

for (const url of urls) {
  await page.goto(url, { waitUntil: "domcontentloaded" });
  // process the page
  await pause(2000, 6000);
}

نشست‌ها و کوکی‌ها را حفظ کنید

به‌جای باز کردن نمایه تازه در هر اجرا، از یک پوشه ماندگار داده کاربر استفاده کنید. به این ترتیب کوکی‌ها و تأییدهایی که پیش‌تر گذرانده شده‌اند میان نشست‌ها حفظ می‌شوند:

javascript
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  userDataDir: "./profile-1",
});

اگر چند حساب یا کار را مدیریت می‌کنید، برای هرکدام پوشه جداگانه‌ای به کار ببرید؛ درآمیختن نمایه‌ها این نشانه را ایجاد می‌کند که حساب‌های زیادی از یک دستگاه مدیریت می‌شوند.

نمایه را سازگار نگه دارید

زبان، منطقه زمانی و اندازه پنجره مرورگر با مکان IP که به کار می‌برید همخوان باشد. این مقدارها را در طول یک نشست تغییر ندهید:

javascript
const page = await browser.newPage();
await page.setViewport({ width: 1366, height: 768 });
await page.emulateTimezone("Asia/Tehran");
await page.setExtraHTTPHeaders({ "Accept-Language": "fa-IR,fa;q=0.9" });

اندازه پنجره را از میان مقدارهای رایج در دستگاه‌های واقعی برگزینید؛ یک وضوح نامعمول به‌تنها جلب توجه می‌کند.

برای هر نشست یک IP به کار ببرید

در Puppeteer پروکسی با آرگومان راه‌اندازی Chrome داده می‌شود. اگر احراز هویت لازم باشد از page.authenticate استفاده می‌شود:

javascript
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.proxynet.io:8000"],
});

const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" });

await page.goto("https://httpbin.org/ip", { waitUntil: "networkidle2" });
console.log(await page.evaluate(() => document.body.innerText));

await browser.close();

نکته مهم در اینجا این است که IP در طول نشست تغییر نکند. تغییر نشانی IP در میانه نشستی که وارد حساب شده، از رایج‌ترین علت‌های کپچاست. در چنین جریان‌هایی پروکسی با نشست ثابت همان IP را برای مدتی معین حفظ می‌کند؛ در نشست‌های طولانی مبتنی بر حساب، پروکسی ISP پایدارتر است. در سایت‌های محافظت‌شده، پروکسی مسکونی که از نشانی‌های کاربران واقعی می‌آید، نسبت به IPهای دیتاسنتر بدگمانی کمتری ایجاد می‌کند.

منابع را بی‌دلیل مسدود نکنید

مسدود کردن تصویرها و فایل‌های سبک برای صرفه‌جویی در پهنای باند عادتی رایج است. اما «مرورگری» که هیچ تصویری بارگذاری نمی‌کند به کاربر واقعی شبیه نیست و برخی محافظت‌ها از همین کمبود به‌عنوان نشانه استفاده می‌کنند. اگر هزینه ترافیک مسئله است، مسدود کردن فقط فایل‌های رسانه‌ای حجیم (ویدئو، تصویرهای بزرگ) راه میانه متعادل‌تری است.

اگر شدنی است، سراغ منبع رسمی بروید

اگر سایت مقصد API ارائه می‌دهد، ممکن است اصلاً به خودکارسازی مرورگر نیازی نباشد. APIها سریع‌ترند، منابع کمتری مصرف می‌کنند و با کپچا روبه‌رو نمی‌شوید. اگر سایت خودتان یا سایت یکی از مشتریان را آزمون می‌کنید، درخواست از مالک سایت برای مستثنا کردن ترافیک آزمون شما از قاعده‌های محافظت، پاکیزه‌ترین راه‌حل است.

نمونه کامل: یک پیکربندی سازگار Puppeteer

پیکربندی‌ای که اقدام‌های بالا را گرد هم می‌آورد، با پروکسی کار می‌کند و میان صفحه‌ها انتظار متغیر دارد:

javascript
import puppeteer from "puppeteer";

const pause = (min, max) =>
  new Promise((resolve) => setTimeout(resolve, min + Math.random() * (max - min)));

const browser = await puppeteer.launch({
  userDataDir: "./profile-1",
  args: ["--proxy-server=http://pr.proxynet.io:8000", "--window-size=1366,768"],
  defaultViewport: { width: 1366, height: 768 },
});

const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" });
await page.emulateTimezone("Asia/Tehran");
await page.setExtraHTTPHeaders({ "Accept-Language": "fa-IR,fa;q=0.9" });

const urls = ["https://httpbin.org/ip", "https://httpbin.org/headers"];

for (const url of urls) {
  await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
  console.log(url, (await page.evaluate(() => document.body.innerText)).slice(0, 80));
  await pause(2000, 5000);
}

await browser.close();

در این پیکربندی IP در طول نشست ثابت است، نمایه ماندگار است، منطقه زمانی و زبان با مکان IP همخوان‌اند و میان صفحه‌ها انتظاری نامنظم و شبیه به انسان وجود دارد.

کارهایی که نباید انجام دهید

  • استفاده از سرویس‌های حل کپچا. سرویس‌هایی که کپچا را به انسان یا مدل می‌سپارند و نتیجه را به خودکارسازی می‌رسانند، محافظتی را هدف می‌گیرند که مالک سایت آگاهانه برپا کرده است. شرایط استفاده بسیاری از سایت‌ها را نقض می‌کنند و ممکن است خطر حقوقی ایجاد کنند.
  • تکیه بر افزونه‌های دور زدن تشخیص. افزونه‌هایی که ادعا می‌کنند ردپای خودکارسازی را پنهان می‌کنند، در تعقیب و گریزی همیشگی با ارائه‌دهندگان محافظت هستند. روشی که امروز کار می‌کند ممکن است فردا کار نکند؛ بنا کردن جریان کار بر آن پایدار نیست.
  • افزایش سرعت پس از مسدود شدن. کپچا یک هشدار است. پاسخ دادن با درخواست‌های بیشتر معمولاً به مسدود شدن کامل IP می‌انجامد.
  • تغییر IP در هر درخواست همراه با ادامه همان نشست. اینکه نشانی پیوسته تغییر کند در حالی که کوکی ثابت می‌ماند، الگویی مشکوک‌تر از صرفاً تغییر IP است.

اینکه کدام داده در چه شرایطی قابل جمع‌آوری است را در نوشته آیا اسکرپینگ وب قانونی است؟ توضیح داده‌ایم.

وقتی کپچا ظاهر شد چه باید کرد؟

اگر با وجود اقدام‌ها کپچا می‌بینید، از آن به‌عنوان فرصتی برای عیب‌یابی استفاده کنید:

  1. ثبت کنید در کدام صفحه ظاهر شد. در صفحه ورود، در فهرست یا در صفحه جزئیات؟ نشان می‌دهد محافظت کجا متمرکز است.
  2. IP را عوض کنید و همان جریان را تکرار کنید. اگر کپچا از بین برود، مشکل در اعتبار IP است؛ اگر بماند، از نمایه یا سرعت است.
  3. سرعت را نصف کنید. اگر نسبت پایین بیاید، آستانه به سرعت وابسته است.
  4. نمایه را با یک مرورگر واقعی مقایسه کنید. اگر با Chrome عادی وارد همان سایت شوید و کپچا نبینید، ردپای خودکارسازی تعیین‌کننده است.

این چهار گام در چند آزمایش نشان می‌دهند کدام نشانه امتیاز را بالا می‌برد. آزمون تک‌متغیره به‌جای آزمایش کورکورانه در زمان صرفه‌جویی می‌کند. اگر به‌جای کپچا پاسخ‌هایی مانند 403، 429 یا 503 می‌گیرید، معنای هر کد و اینکه در کدام باید دوباره تلاش کرد را در نوشته کدهای خطای HTTP در اسکرپینگ می‌یابید.

آیا ابزارهای دیگر از Puppeteer بهترند؟

وضعیت Selenium، Playwright و Puppeteer در برابر کپچا اساساً یکسان است؛ همه یک مرورگر واقعی را از کد کنترل می‌کنند و در معرض همان نشانه‌ها هستند. عوض کردن ابزار علت‌های بالا را از میان برنمی‌دارد. اغلب پرسش نخست این نیست که کدام ابزار را انتخاب کنیم، بلکه این است که آیا واقعاً به مرورگر نیاز داریم؛ چگونگی تشخیص آن را در نوشته صفحه‌های ایستا و پویا توضیح داده‌ایم. برای معادل‌ها در اکوسیستم Python نوشته‌های Selenium، پروکسی در SeleniumBase و Undetected ChromeDriver را ببینید. عوامل دیگری را که بر انتخاب زبان اثر می‌گذارند در نوشته اسکرپینگ وب: JavaScript یا Python؟ مقایسه کرده‌ایم.

پرسش‌های متداول

حالت بدون رابط کپچا را بیشتر می‌کند؟

حالت بدون رابط قدیمی ردپاهایی داشت که تشخیصشان از مرورگر عادی آسان‌تر بود. حالت بدون رابط کنونی Chrome به مرورگر عادی بسیار نزدیک‌تر است. با این حال، کار در حالت بدون رابط در ترکیب با نشانه‌های دیگر ممکن است بر امتیاز اثر بگذارد.

پروکسی جلوی کپچا را کاملاً می‌گیرد؟

نه. نوع درست IP امتیاز بدگمانی را پایین می‌آورد، اما نشانه‌های دیگری مانند سرعت درخواست، ناسازگاری نمایه و رفتار باقی می‌مانند. IP فقط بخشی از راه‌حل است.

تغییر IP در هر درخواست فکر خوبی است؟

در دریافت صفحه‌های مستقل که به نشست نیاز ندارند بله؛ پروکسی چرخشی برای این کار مناسب است. اما در جریان‌های چندمرحله‌ای یا دارای ورود به حساب، IP باید در طول نشست ثابت بماند؛ در غیر این صورت احتمال کپچا بیشتر می‌شود.

در Puppeteer می‌توانم از پروکسی SOCKS5 استفاده کنم؟

بله. به شکل --proxy-server=socks5://server:port داده می‌شود. اما Chrome برای SOCKS5 از احراز هویت با نام کاربری و رمز عبور پشتیبانی نمی‌کند؛ اگر SOCKS5 به کار می‌برید، باید از پنل پروکسی مجوز IP بدهید. برای تفاوت پروتکل‌ها نوشته تفاوت پروکسی SOCKS و HTTP را ببینید.

در خودکارسازی نسخه موبایل سایت کپچا کمتر ظاهر می‌شود؟

نمای موبایل به‌تنها امتیاز را پایین نمی‌آورد. اگر نمایه موبایل به کار می‌برید، موبایل بودن IP هم سازگاری ایجاد می‌کند؛ نمایه موبایل با IP رومیزی یک تناقض است. تفاوت ترافیک موبایل را در نوشته تفاوت پروکسی مسکونی و موبایل توضیح داده‌ایم.

استفاده از پروکسی در Puppeteer و Playwright متفاوت است؟

منطق یکسان است: نشانی پروکسی هنگام راه‌اندازی مرورگر داده می‌شود و اطلاعات ورود با فراخوانی جداگانه‌ای تعریف می‌شود. در Playwright این تنظیم در یک شیء به شکل launch({ proxy: { server, username, password } }) انجام می‌شود.

جمع‌بندی

پشت کپچا در Puppeteer اغلب نوع IP، سرعت درخواست، ردپای مرورگر بدون رابط و نمایه ناسازگار قرار دارد. کم کردن سرعت، حفظ نشست‌ها، همخوان نگه داشتن نمایه با مکان IP و استفاده از IP ثابت برای هر نشست، کپچا را در خودکارسازی‌های مشروع به‌طور محسوسی کاهش می‌دهد. وقتی کپچا ظاهر می‌شود، یافتن علت با آزمون تک‌متغیره بسیار سریع‌تر از آزمون و خطای کورکورانه است. تلاش برای دور زدن محافظت اما راهی شکننده و پرخطر است. انواع IP مناسب برای پروژه‌های خودکارسازی را در راهکارهای پروکسی ما می‌یابید.

پرسش از ChatGPTپرسش از Claude