Skip to content
Advanced / 4 min read

وباسکرپینگ ProxyScrape با Playwright: مسیریابی Chromium هدلس از طریق پروکسیهای HTTP و SOCKS5

Playwright را طوری پیکربندی کنید که Chromium هدلس از طریق ProxyScrape خارج شود، قبل از وباسکرپینگ IP خروجی را تأیید کنید، اندپوینتهای HTTP یا SOCKS5 را برای هر کانتکست مرورگر بچرخانید و خطاهای پروکسی را بهدرستی دستهبندی کنید.

لینوکس HTTP(S) وب اسکرپینگ

مرور کلی

مرورگرهای هدلس پیکربندی پروکسی را درون خود مرورگر قرار میدهند، نه درون کتابخانه HTTP شما. این موضوع راهاندازی را تغییر میدهد: شما پروکسی را هنگام اجرای مرورگر یا ایجاد کانتکست مرورگر پیکربندی میکنید، باید احراز هویت پروکسی را مدیریت کنید، و پیکربندی بهازای هر کانتکست همان چیزی است که چرخش را بدون راهاندازی مجدد Chromium برای هر درخواست ممکن میسازد.

این آموزش از Playwright با Chromium و اندپوینتهای ProxyScrape (HTTP یا SOCKS5) استفاده میکند. این موارد را پوشش میدهد: اجرا با یک اندپوینت، بررسی IP خروجی، چرخش بهازای هر کانتکست میان چندین اندپوینت، مدیریت خطای پروکسی، و اجرای آن روی CI لینوکس بدون تمام کردن حافظه.

چه زمانی مرورگر هدلس ابزار مناسب است

  • هدف محتوا را سمت کلاینت رندر میکند، بنابراین یک درخواست HTTP ساده یک پوسته خالی برمیگرداند.
  • نیاز به تعامل دارید: پر کردن فرمها، کلیک، اسکرول، یا انتظار برای بیکاری شبکه.
  • به رفتار سطح مرورگر مانند کوکیها، localStorage، یا service workerها نیاز دارید.
  • در حال اعتبارسنجی نحوه رفتار یک سایت برای یک منطقه خاص هستید.

وقتی صفحه سمت سرور رندر میشود، به یک کلاینت HTTP ساده بچسبید؛ بسیار ارزانتر و سریعتر است.

پیشنیازها

  • Node.js 18 یا جدیدتر و npm.
  • Playwright نصبشده همراه با Chromium.
  • اعتبارنامههای ProxyScrape بههمراه میزبان و پورت برای http و/یا socks5. پروکسیهای مسکونی، دیتاسنتری و موبایل همگی اینجا کار میکنند؛ نوعی را انتخاب کنید که با دفاعهای هدف مطابقت دارد.
  • لینوکس، macOS، یا Windows. نکات CI فرض را بر اجراکنندههای Debian یا Ubuntu میگذارد.

مراحل

مرحله ۱ — نصب Playwright و Chromium

mkdir proxyscrape-playwright
cd proxyscrape-playwright
npm init -y
npm install playwright
npx playwright install --with-deps chromium

--with-deps کتابخانههای سیستمعاملی را که Chromium روی ایمیجهای لینوکس و اجراکنندههای CI نیاز دارد نصب میکند.

مرحله ۲ — تصمیمگیری درباره اینکه پروکسی به مرورگر اعمال میشود یا به کانتکست

دامنه محل تنظیم کاربرد ملاحظات
مرورگر chromium.launch({ proxy }) یک اندپوینت واحد برای کل اجرا هر کانتکست یک IP خروجی مشترک دارد
کانتکست browser.newContext({ proxy }) چرخش: یک اندپوینت بهازای هر کانتکست، بهصورت موازی پشتیبانی موتور متفاوت است؛ Chromium پیشفرض ایمن است — پیش از اتکا در Firefox یا WebKit، پشتیبانی پروکسی بهازای هر کانتکست را برای نسخه Playwright خود تأیید کنید

دو قاعده عملی:

  • اعتبارنامهها را بهصورت فیلدهای جداگانه username و password بفرستید. Chromium اعتبارنامههای جاسازیشده در URL سرور پروکسی را نادیده میگیرد.
  • مقدار server یک اسکیم میگیرد: http://host:port برای اندپوینتهای HTTP پروکسیاسکرپ و socks5://host:port برای SOCKS5.

مرحله ۳ — اجرا از طریق یک اندپوینت و بررسی IP خروجی

// launch-proxy.mjs
import { chromium } from 'playwright';

const proxy = {
  server: process.env.PROXYSCRAPE_SERVER, // http://HOST:PORT or socks5://HOST:PORT
  username: process.env.PROXYSCRAPE_USERNAME,
  password: process.env.PROXYSCRAPE_PASSWORD,
};

const browser = await chromium.launch({ proxy, headless: true });
try {
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://api.ipify.org?format=json', { waitUntil: 'domcontentloaded' });
  console.log('exit ip:', await page.textContent('body'));
} finally {
  await browser.close();
}
export PROXYSCRAPE_SERVER="http://host-from-your-plan:port-from-your-plan"
export PROXYSCRAPE_USERNAME="your-username"
export PROXYSCRAPE_PASSWORD="your-password"
node launch-proxy.mjs

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

مرحله ۴ — سریع شکست بخورید وقتی ترافیک از پروکسی عبور نمیکند

یک پیکربندی اشتباه خاموش — برای مثال، یک غلط تایپی در رشته سرور — میتواند باعث شود از IP خودتان وباسکرپینگ کنید. IP مستقیم خود را یکبار ثبت کنید، سپس آن را بررسی کنید.

// exit-ip.mjs
export async function getExitIp(page) {
  await page.goto('https://api.ipify.org?format=json', { waitUntil: 'domcontentloaded' });
  const body = await page.textContent('body');
  return JSON.parse(body).ip;
}

export function assertProxied(exitIp, directIp) {
  if (directIp && exitIp === directIp) {
    throw new Error(`Traffic was not proxied: exit IP ${exitIp} equals the direct IP`);
  }
}

رویه ترتیبی:

  1. یک اسکریپت یکباره بدون پروکسی پیکربندیشده اجرا کنید و نتیجه را بهعنوان DIRECT_IP ثبت کنید.
  2. اسکریپت پروکسیشده را دوباره اجرا کنید.
  3. قبل از هر وباسکرپینگ واقعی، assertProxied(exitIp, process.env.DIRECT_IP) را فراخوانی کنید، و وقتی ادعا برقرار نشد، اجرا را شکست دهید.

مرحله ۵ — چرخش اندپوینتها میان کانتکستهای مرورگر

یک مرورگر، چندین کانتکست، هر کدام یک اندپوینت.

// rotate-contexts.mjs
import { chromium } from 'playwright';

const ENDPOINTS = (process.env.PROXYSCRAPE_ENDPOINTS ?? '')
  .split(',')
  .map((entry) => entry.trim())
  .filter(Boolean)
  .map((entry) => {
    const [scheme, host, port, username, password] = entry.split('|');
    return { server: `${scheme}://${host}:${port}`, username, password };
  });

if (ENDPOINTS.length === 0) {
  throw new Error('Set PROXYSCRAPE_ENDPOINTS to http|HOST|PORT|USER|PASS,socks5|HOST|PORT|USER|PASS');
}

const TARGETS = (process.env.TARGETS ?? 'https://example.com,https://example.org').split(',');
const CONCURRENCY = Number(process.env.CONCURRENCY ?? 3);
const USER_AGENT =
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36';

function isProxyError(error) {
  const message = String(error && error.message ? error.message : '');
  return /ERR_PROXY|ERR_TUNNEL|ERR_PROXY_AUTH/i.test(message);
}

async function runBatch(browser, proxy, urls) {
  const context = await browser.newContext({ proxy, userAgent: USER_AGENT });
  context.setDefaultTimeout(30000);
  const results = [];
  try {
    const page = await context.newPage();
    for (const url of urls) {
      try {
        const response = await page.goto(url, { waitUntil: 'domcontentloaded' });
        results.push({ url, endpoint: proxy.server, status: response ? response.status() : null });
      } catch (error) {
        results.push({
          url,
          endpoint: proxy.server,
          error: isProxyError(error) ? `proxy: ${error.message}` : error.message,
        });
      }
    }
  } finally {
    await context.close();
  }
  return results;
}

const browser = await chromium.launch({ headless: true });
try {
  const queue = ENDPOINTS.map((proxy, index) => ({
    proxy,
    urls: TARGETS.filter((_, targetIndex) => targetIndex % ENDPOINTS.length === index),
  }));
  const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {
    const collected = [];
    while (queue.length > 0) {
      const job = queue.shift();
      collected.push(...(await runBatch(browser, job.proxy, job.urls)));
    }
    return collected;
  });
  const results = (await Promise.all(workers)).flat();
  for (const result of results) {
    console.log(JSON.stringify(result));
  }
} finally {
  await browser.close();
}
export PROXYSCRAPE_ENDPOINTS="http|HOST|PORT|USER|PASS,socks5|HOST|PORT|USER|PASS"
export TARGETS="https://example.com,https://example.org,https://example.net"
export CONCURRENCY=3
node rotate-contexts.mjs

چرا این ساختار کار میکند:

  • مرورگر یکبار راهاندازی میشود؛ کانتکستها ارزان هستند و هر کدام اندپوینت خود را حمل میکند.
  • استخر کارگر تعداد کانتکستهایی که همزمان وجود دارند را محدود میکند، که اهرم اصلی حافظه در Playwright است.
  • هر نتیجه اندپوینتی را که آن را سرو کرده ثبت میکند، بنابراین شکستها را میتوان بدون حدسزدن نسبت داد.

مرحله ۶ — شکستهای پروکسی را بهعنوان شکست اندپوینت در نظر بگیرید، نه شکست صفحه

  • net::ERR_PROXY_CONNECTION_FAILED — میزبان یا پورت پروکسی اشتباه است یا قابل دسترسی نیست.
  • net::ERR_TUNNEL_CONNECTION_FAILED — تونل CONNECT به یک هدف HTTPS رد شد؛ تأیید کنید پروتکل پیکربندیشده با اندپوینت مطابقت دارد.
  • net::ERR_PROXY_AUTH_REQUESTED — اعتبارنامهها رد شدند؛ نام کاربری و رمز عبور را دوباره بررسی کنید، و آنها را بهعنوان فیلد بفرستید نه درون URL سرور.
  • TimeoutError — اندپوینت کند است یا هدف سنگین است؛ مهلت زمانی را افزایش دهید و همزمانی را کاهش دهید.

وقتی یکی از اینها ظاهر شد، آن اندپوینت را برای ادامه اجرا از صف حذف کنید و با بقیه ادامه دهید. تلاش مکرر برای همان اندپوینت خراب فقط زمان را تلف میکند.

مرحله ۷ — اجرای آن روی CI لینوکس بدون تمام کردن منابع

  1. وابستگیهای مرورگر را در خط لوله با npx playwright install --with-deps chromium نصب کنید.
  2. همزمانی را روی اجراکننده CI به دو یا سه کانتکست بهازای هر vCPU محدود نگه دارید؛ هر کانتکست یک صفحه رندرشده حمل میکند.
  3. مرورگر را یکبار بهازای هر فرایند راهاندازی کنید و کانتکستها را در بلوکهای finally ببندید.
  4. برای اهداف حجیم و کمدفاع در کارهای زمانبندیشده، پروکسیهای دیتاسنتری را ترجیح دهید، و اندپوینتهای مسکونی یا موبایل را برای اهداف سختتر نگه دارید.
  5. اقتصاد واحد را زیر نظر داشته باشید: صورت حساب پرداخت بهازای مصرف شروع اجراهای CI انفجاری را آسان میکند، در حالی که یک طرح ماهانه ثابت معمولاً برای یک کار شبانه ثابت ارزانتر است.

عیبیابی

نشانه علت احتمالی راهحل
صفحات بارگیری میشوند، اما از IP خودتان پروکسی نادیده گرفته شده (غلط تایپی در server، یا گزینه به کانتکست داده نشده است) IP خروجی را با کمککننده مرحله ۴ ثبت کنید و وقتی با IP مستقیم مطابقت داشت هشدار بدهید
ERR_PROXY_CONNECTION_FAILED میزبان یا پورت اشتباه، یا خروجی CI مسدود است همان اندپوینت را با curl تست کنید، سپس سیاست شبکه را بررسی کنید
ERR_PROXY_AUTH_REQUESTED اعتبارنامهها اشتباه، یا در URL جاسازی شدهاند فیلدهای username و password را بفرستید، هرگز user:pass@host
ERR_TUNNEL_CONNECTION_FAILED روی HTTPS عدم تطابق پروتکل بین اندپوینت و اسکیمی که پیکربندی کردهاید اندپوینت HTTP را امتحان کنید، سپس اندپوینت SOCKS5، و آن که کار میکند را نگه دارید
هر کانتکست همان IP خروجی را گزارش میکند پروکسی سطح مرورگر بهجای سطح کانتکست استفاده شده، یا موتور از پروکسی بهازای هر کانتکست پشتیبانی نمیکند از chromium.launch() استفاده کنید و proxy را روی هر newContext() تنظیم کنید
اجرا اجراکننده CI را از کار میاندازد کانتکستهای همزمان بیش از حد CONCURRENCY را به ۲ کاهش دهید و حافظه را دوباره اندازهگیری کنید

خلاصه

  • در Playwright، پروکسی به فراخوانی راهاندازی یا به کانتکست مرورگر تعلق دارد، نه به درخواستهای تکی.
  • قبل از وباسکرپینگ IP خروجی را تأیید کنید؛ یک پروکسی پیکربندیاشتباه میتواند بیصدا شما را روی IP خودتان رها کند.
  • با دادن اندپوینت HTTP یا SOCKS5 اختصاصی ProxyScrape به هر کانتکست و محدود کردن تعداد کانتکستهایی که همزمان اجرا میشوند، بچرخانید.
  • شکستهای ERR_PROXY* و ERR_TUNNEL* را بهعنوان مشکلات اندپوینت دستهبندی کنید، اندپوینت را حذف کنید، و اجرا را ادامه دهید.