خانه/مقالات/راهنمای سریع اسکریپ: مدیریت User-Agent در Node.js
وب اسکریپینگ
پروکسی و چرخش IP
ضد بلاک (Anti-bot)
برگشت به صفحه مقاله ها
راهنمای سریع اسکریپ: مدیریت User-Agent در Node.js

راهنمای سریع اسکریپ: مدیریت User-Agent در Node.js

این راهنمای عملی به شما نشان می‌دهد چگونه در Node.js مقدار User-Agent را به‌صورت دستی و چرخشی تنظیم کنید، چرا هدرهای مرورگر مهم‌اند، و چطور با استفاده از APIها یا لیست‌های به‌روز هزاران User-Agent و browser headers را مدیریت کنید. مثال‌های واقعی، نکات امنیتی و بهترین‌روش‌های production برای کاهش بلاک و افزایش پایداری اسکریپر پوشش داده شده‌اند.
امیر حسین حسینیان
امیر حسین حسینیان
1404-09-28

مقدمه

در وب اسکریپینگ یکی از دلایل اصلی بلاک شدن درخواست‌ها، استفاده از User-Agentهای ضعیف یا ناپایدار است. در این مقاله به صورت عملی و گام‌به‌گام یاد می‌گیرید چگونه در محیط Node.js برای کتابخانه‌های متداول user-agent تعریف، آن را بچرخانید (rotate) و در مقیاس بزرگ مدیریت کنید. مثال‌ها با node-fetch، axios، superagent، got و request-promise هستند و برای هر قطعه کد توضیح ورودی، خروجی و نکات مهم داده شده است.

چیستِ Fake User-Agent و چرا مهم است

User-Agent یک رشته در هدر HTTP است که مشخص می‌کند درخواست از چه مرورگر/سیستم‌عاملی می‌آید. وقتی از مقدار پیش‌فرض برخی کلاینت‌های Node.js استفاده کنید (مثلاً "node-fetch") سرور به‌راحتی تشخیص می‌دهد درخواست از یک کتابخانه است و ممکن است آن را مسدود کند. یک Fake User-Agent یعنی جایگزینی رشته‌ای واقع‌گرایانه که شبیه به مرورگرهای رایج است تا احتمال تشخیص ربات کاهش یابد.

نکته فنی: فقط تغییر User-Agent کافی نیست — هدرهای دیگر (Accept, Accept-Language, Sec-* و غیره) هم نقش دارند. در ادامه به این موارد هم می‌پردازیم.

چطور یک User-Agent ساده در درخواست‌ها قرار دهیم

ایده کلی: در گزینه‌های درخواست (options) یک شیء headers بسازید و کلید 'User-Agent' را ست کنید. خروجی هر درخواست معمولاً پاسخ سرور است و شما می‌توانید با endpointهایی مثل httpbin بررسی کنید هدر ارسال شده چیست.

مثال با node-fetch — ورودی: url و options با header؛ خروجی: پاسخ JSON از سرور:

import fetch from 'node-fetch';

(async () => {
  const url = 'https://httpbin.org/headers';
  const options = {
    method: 'GET',
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36'
    }
  };

  try {
    const res = await fetch(url, options);
    const data = await res.json();
    console.log(data); // مشاهده هدرهای ارسالی
  } catch (err) {
    console.error('request error', err);
  }
})();

توضیح: ما options.headers می‌سازیم و به تابع fetch پاس می‌دهیم. پاسخ JSON شامل هدرهای دریافتی سرور است تا مطمئن شویم مقدار ارسال شده درست است.

مثال خلاصه با axios (ورودی: url، options؛ خروجی: response.data):

const axios = require('axios');

(async () => {
  const url = 'https://httpbin.org/headers';
  const options = {
    headers: {
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36'
    }
  };

  try {
    const response = await axios.get(url, options);
    console.log(response.data);
  } catch (error) {
    console.error('error', error);
  }
})();

برای superagent از متد set استفاده کنید؛ برای got و request-promise نیز مشابه است: در options شیء headers قرار دهید.

چگونه User-Agent را بچرخانیم (Rotation)

ایده کلی: نگه داشتن یک لیست از UAهای معتبر و انتخاب تصادفی یا الگوریتمی از بین آن‌ها برای هر درخواست. این کار باعث می‌شود ترافیک شما شبیه کاربران مختلف به نظر برسد و احتمال بلاک شدن کاهش یابد.

نمونه اولیه: استفاده از پکیج random-useragent یا لیست محلی. ورودی: لیست UA؛ خروجی: UA انتخاب‌شده.

import fetch from 'node-fetch';
import randomUserAgent from 'random-useragent';

const userAgents = [
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/93.0.4577.82 Safari/537.36',
  'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2) AppleWebKit/605.1.15 Version/14.0.3 Mobile/15E148 Safari/604.1',
  'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)'
];

function getRandomUserAgent(list) {
  const idx = Math.floor(Math.random() * list.length);
  return list[idx];
}

(async () => {
  const url = 'https://httpbin.org/headers';
  const headers = { 'User-Agent': randomUserAgent.getRandom(userAgents) || getRandomUserAgent(userAgents) };
  const res = await fetch(url, { method: 'GET', headers });
  console.log(await res.json());
})();

نکات عملی:

  • هیچ‌گاه تنها به یک UA تکیه نکنید؛ لیست را به‌صورت دوره‌ای به‌روزرسانی کنید.
  • می‌توانید انتخاب را هوشمند کنید: بر اساس target site، پراکندگی OS و مرورگر، یا نرخ موفقیت UAها.
  • برای هر دامنه تا حد ممکن توزیع UAها را متفاوت نگه دارید تا الگوهای واضح ندهید.

مدیریت هزاران User-Agent: استفاده از API

در پروژه‌های بزرگ ساخت و به‌روزرسانی دستی لیست UA مقیاس‌پذیر نیست. یک روش بهتر استفاده از یک API مرکزی است که فهرستی از UAهای به‌روز را برمی‌گرداند. در متن منبع از یک سرویس یاد شده که دو endpoint دارد: دریافت لیست User-Agents و دریافت مجموعه هدرهای کامل (browser headers).

مثال ساده برای دریافت لیست UA از یک API (ورودی: API key؛ خروجی: آرایه UA):

// دریافت لیست User-Agent از API
import fetch from 'node-fetch';

const SCRAPEOPS_API_KEY = 'YOUR_API_KEY';
const apiUrl = `http://headers.scrapeops.io/v1/user-agents?api_key=${SCRAPEOPS_API_KEY}`;

async function getUserAgentList() {
  const res = await fetch(apiUrl);
  const json = await res.json();
  return json.result || [];
}

// استفاده
(async () => {
  const list = await getUserAgentList();
  console.log('fetched', list.length, 'user-agents');
})();

توضیح: تابع getUserAgentList یک درخواست ساده GET می‌زند و آرایه‌ای از رشته‌های UA برمی‌گرداند. در صورت خطا باید retry یا fallback داشته باشید (مثلاً لیست محلی).

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

  1. در زمان شروع اسکریپر، از API یک batch دریافت کنید.
  2. آن را در حافظه یا کش محلی (Redis/Memory) قرار دهید.
  3. برای هر دامنه/گروه درخواست، یک UA به‌صورت تصادفی یا طبق وزن‌بندی انتخاب کنید.
  4. نسبت موفقیت هر UA را ثبت کنید و UAهای بد را کنار بگذارید.

Fake Browser Headers: چرا فقط User-Agent کافی نیست

بسیاری از سیستم‌های ضدربات به دنبال تناقض بین User-Agent و سایر هدرها هستند؛ مثلاً اگر User-Agent مربوط به Chrome روی macOS باشد ولی sec-ch-ua-platform یا Accept-Language مناسب نباشد، رفتار مشکوک می‌شود. بنابراین برای افزایش موفقیت بهتر است مجموعه کامل هدرهای مرورگر را شبیه‌سازی کنید.

مثال هدرهای معمول یک Chrome روی macOS (نمونه خوانا):

sec-ch-ua: "Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/99.0.4844.83 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Sec-Fetch-Site: none
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br

نکات امنیتی و بهترین روش‌ها:

  • هماهنگی بین هدرها: رشته User-Agent باید با sec-ch-ua-platform و Accept-Language سازگار باشد.
  • از ارسال هدرهای غیرضروری یا متناقض پرهیز کنید (مثلاً هدرهای مخصوص fetch از یک مرورگر که با UA دیگر سازگار نیست).
  • در صورت امکان از کوکی‌ها و جلسات (sessions) نیز برای پایداری استفاده کنید؛ اما مدیریت کوکی و هویت باید با دقت انجام شود.

دریافت هدرهای کامل از API و استفاده در درخواست‌ها

به‌جای ساخت دستی مجموعه هدرها می‌توانید از یک endpoint مخصوص هدرهای مرورگر استفاده کنید. نمونه کد برای دریافت و استفاده از یک هدر تصادفی:

// دریافت هدرهای آماده (browser headers)
import fetch from 'node-fetch';

const SCRAPEOPS_API_KEY = 'YOUR_API_KEY';
const headersUrl = `http://headers.scrapeops.io/v1/browser-headers?api_key=${SCRAPEOPS_API_KEY}`;

async function getHeadersList() {
  const res = await fetch(headersUrl);
  const json = await res.json();
  return json.result || [];
}

function pickRandom(list) {
  return list[Math.floor(Math.random() * list.length)];
}

(async () => {
  const headerList = await getHeadersList();
  const headers = pickRandom(headerList); // headers یک شیء کامل مانند { 'user-agent': '...', 'accept': '...', ... }

  // استفاده در درخواست
  const target = 'https://httpbin.org/headers';
  const res = await fetch(target, { method: 'GET', headers });
  console.log(await res.json());
})();

توضیح: این رویکرد باعث می‌شود هدرها نه تنها realistic باشند بلکه به‌روزرسانی‌شده نیز باشند. همیشه مراقب محدودیت درخواست (rate limit) برای endpoint API باشید و کلید API را امن نگه دارید.

بهینه‌سازی و نکات production

نکات کلیدی برای تولید (production):

  • کش کردن لیست‌های UA/Headers در حافظه یا Redis تا از فراخوانی مکرر API جلوگیری شود.
  • استفاده از پروکسی‌های چرخشی برای پخش ترافیک و کاهش نرخ بلوک شدن از سمت IP.
  • افزودن لایه retry با backoff و اعمال محدودیت نرخ (rate limiting) در سمت کلاینت.
  • جمع‌آوری متریک: نرخ موفقیت هر UA/Header، زمان پاسخ و خطاها؛ این داده‌ها برای انتخاب هوشمند UA مفید است.
  • همیشه قوانین robots.txt و ملاحظات قانونی/اخلاقی را رعایت کنید؛ وب اسکریپینگ بدون اجازه در برخی موارد مجاز نیست.

مثال ترکیبی: جریان کامل اسکریپر با چرخش UA

یک الگوی ساده برای جریان اجرا در ابتدای فرآیند:

  1. بارگذاری یا دریافت از API لیست هدرها/UAها.
  2. ذخیره در کش محلی با زمان انقضای مناسب.
  3. برای هر درخواست انتخاب تصادفی هدر و ارسال درخواست همراه با مدیریت خطا، retry و تاخیر تصادفی.
// الگوی کلی (pseudo-production)
import fetch from 'node-fetch';

async function bootstrap() {
  const headerList = await getHeadersList(); // از قبل تعریف شده
  for (const url of ['https://example.com/a', 'https://example.com/b']) {
    const headers = pickRandom(headerList);
    try {
      const res = await fetch(url, { method: 'GET', headers });
      if (res.status === 200) {
        const data = await res.text();
        // پردازش داده
      } else if (res.status === 429) {
        // rate limited — backoff
      } else {
        // لاگ خطا و تحلیل
      }
    } catch (err) {
      // retry logic یا لاگ
    }
    // تأخیر تصادفی بین درخواست‌ها
    await new Promise(r => setTimeout(r, 500 + Math.random() * 1500));
  }
}

bootstrap();

توضیح: در این الگو مدیریت وضعیت‌ها (200, 429, 5xx) و تاخیر بین درخواست‌ها اهمیت بالایی دارد تا الگوهای ربات‌مانند ایجاد نشود.

جمع‌بندی

مدیریت User-Agentها و هدرهای مرورگر بخش حیاتی از طراحی یک اسکریپر قابل اعتماد است. مراحل عملی خلاصه شده:

  • همیشه User-Agent را ست کنید و به مقدار پیش‌فرض کتابخانه‌ها اتکا نکنید.
  • برای جلوگیری از تشخیص، User-Agentها را بچرخانید و در صورت امکان مجموعه کامل هدرهای مرورگر را شبیه‌سازی کنید.
  • در پروژه‌های بزرگ از APIها یا سرویس‌های به‌روز برای دریافت UA/Headers استفاده کنید و لیست‌ها را کش کنید.
  • بهترین‌روش‌ها مانند پروکسی، rate-limiting، retry با backoff و لاگ متریک را پیاده‌سازی کنید.

با رعایت این اصول، احتمال بلاک شدن به‌شدت کاهش پیدا می‌کند و پایداری اسکریپر شما بهتر خواهد شد.

مقاله‌های مرتبط
بهینه‌سازی درخواست‌ها و جلوگیری از بلاک‌شدن
1404-10-04
راهنمای سریع وب اسکریپینگ: Retry در Node.js
در این مقاله دو روش متداول برای Retry در وب اسکریپینگ با Node.js بررسی شده: استفاده از کتابخانه <strong>retry</strong> و ساخت wrapper اختصاصی. مثال‌های عملی برای Got، node‑fetch و Axios همراه با نکات backoff، تشخیص صفحه بن و بهترین‌روش‌های امنیتی و عملکردی ارائه شده‌اند.
بهینه‌سازی درخواست‌ها و جلوگیری از بلاک‌شدن
1404-10-02
راهنمای سریع POST در NodeJS برای وب اسکریپینگ
این راهنمای جامع نشان می‌دهد چگونه با کتابخانه‌های مختلف NodeJS (Got، SuperAgent، node-fetch، Axios، request-promise) درخواست‌های POST برای ارسال JSON و فرم بسازید، و نکات عملی‌ مثل هدرها، مدیریت خطا، retry، همزمانی و حفاظت در برابر مسدودسازی را برای وب اسکریپینگ توضیح می‌دهد.
بهینه‌سازی درخواست‌ها و جلوگیری از بلاک‌شدن
1404-10-01
راهنمای سریع کاهش هزینه وب اسکریپینگ با Node.js
این مقاله یک راهنمای عملی برای کاهش هزینه‌های وب اسکریپینگ با Node.js است: انتخاب بین HTTP requests و headless، انتخاب نوع و مدل قیمت‌گذاری پروکسی، کاهش تعداد درخواست و پهنای‌باند، استفاده از سرویس‌های ارزان‌تر و مانیتورینگ هزینه. همراه با مثال‌های Node.js و توضیحات فنی برای پیاده‌سازی عملی.