خانه/مقالات/راهنمای قدم‌به‌قدم اسکریپ فرم‌ها با NodeJS
راهنمای قدم‌به‌قدم اسکریپ فرم‌ها با NodeJS

راهنمای قدم‌به‌قدم اسکریپ فرم‌ها با NodeJS

این مقاله یک راهنمای عملی و فنی برای ارسال فرم‌ها در NodeJS ارائه می‌دهد: از درخواست‌های HTTP با Axios و node-fetch تا اتوماسیون مرورگر با Puppeteer/Playwright و پارسینگ با Cheerio. به‌علاوه بهترین‌روش‌ها برای احراز هویت، آپلود فایل، مدیریت خطا، و نکات امنیتی و عملکردی گفته شده است.
امیر حسین حسینیان
امیر حسین حسینیان
1404-09-30

مقدمه

ارسال فرم‌ها (form submission) یکی از پایه‌ای‌ترین عملیات در وب است و در اتوماسیون، تست و وب اسکریپینگ کاربرد فراوان دارد. در این مقاله قدم‌به‌قدم روش‌های مختلف ارسال فرم در محیط NodeJS را بررسی می‌کنیم: از درخواست‌های ساده HTTP با Axios و node-fetch تا اتوماسیون مرورگر با Puppeteer و Playwright و روش‌های اسکریپ با Cheerio. در پایان باید بتوانید برای هر سناریو روش مناسب، نکات امنیتی، و تکنیک‌های افزایش پایداری را انتخاب کنید.

درک کلی ارسال فرم در وب

یک فرم HTML به طور معمول شامل فیلدهای ورودی، دکمه‌ها و گاهی تگ‌های خاص برای آپلود فایل و CSRF token است. سرور معمولاً داده‌ها را از طریق HTTP POST یا GET دریافت می‌کند — POST برای ارسال داده در body مناسب‌تر و امن‌تر است.

مثال ساده HTML فرم (برای فهم ساختار):

<form action='/submit' method='post'>
  <label for='username'>Username:</label>
  <input type='text' id='username' name='username' />
  <label for='password'>Password:</label>
  <input type='password' id='password' name='password' />
  <input type='submit' value='Submit' />
</form>

نکته: وقتی فرم به‌صورت multipart/form-data ارسال می‌شود (مثلاً آپلود فایل)، باید headerها و بادی را به شکل صحیح تنظیم کنید تا سرور فایل را دریافت کند.

ارسال فرم با Axios

Axios یک کتابخانهٔ محبوب برای HTTP در NodeJS است که API ساده و امکاناتی مثل interceptors، timeout و پشتیبانی از JSON را فراهم می‌کند.

مثال پایه: ارسال POST با Axios

const axios = require('axios');

const url = 'https://example.com/api/login';
const formData = { username: 'user123', password: 'secret' };

axios.post(url, formData)
  .then(response => {
    console.log('Status:', response.status);
    console.log('Body:', response.data);
  })
  .catch(err => {
    console.error('Request failed:', err.message);
  });

توضیح: ورودی این تابع url و formData است. خروجی در response قرار دارد. هر خط کد به طور خلاصه:

  • وارد کردن axios.
  • تعریف آدرس و شیٔ دادهٔ فرم (JSON).
  • ارسال درخواست با axios.post و پردازش پاسخ در then یا خطا در catch.

استفاده از Axios interceptors برای اضافه کردن headerها

axios.interceptors.request.use(config => {
  // اینجا می‌توانید توکن احراز هویت یا user-agent را به همهٔ درخواست‌ها اضافه کنید
  config.headers.Authorization = `Bearer ${getToken()}`;
  config.headers['User-Agent'] = 'MyScraper/1.0';
  return config;
}, err => Promise.reject(err));

// سپس همان axios.post را فراخوانی کنید

نکتهٔ عملی: از interceptor برای مرکزی کردن مدیریت توکن، تلاش مجدد روی خطاهای 401 یا اضافه کردن اطلاعات لاگ استفاده کنید.

بارگذاری فایل (multipart/form-data) با Axios

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const url = 'https://example.com/upload';
const form = new FormData();
form.append('file', fs.createReadStream('/path/to/file'));
form.append('description', 'my file');

axios.post(url, form, { headers: form.getHeaders(), maxContentLength: Infinity })
  .then(res => console.log('Uploaded:', res.status))
  .catch(err => console.error('Upload error:', err.message));

توضیح: FormData یک استریم می‌سازد و form.getHeaders() هدرهای مناسب multipart را فراهم می‌کند. برای فایل‌های بزرگ از stream و تنظیم مناسب maxContentLength استفاده کنید.

استفاده از node-fetch برای ارسال فرم و اجرای موازی

node-fetch پیاده‌سازی‌ای شبیه fetch مرورگر در NodeJS است؛ سبک و مناسب برای کارهای ساده و همزمانی با Promise.all.

const fetch = require('node-fetch');

async function submitForm(username, password) {
  const res = await fetch('https://example.com/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password })
  });

  if (!res.ok) throw new Error(`Status ${res.status}`);
  return res.text();
}

async function main() {
  const users = [ ['u1','p1'], ['u2','p2'], ['u3','p3'] ];
  const promises = users.map(([u,p]) => submitForm(u,p));
  const results = await Promise.allSettled(promises);
  console.log(results);
}

main();

نکته‌ها: باید وضعیت HTTP را دستی بررسی کنید چون node-fetch به‌طور پیش‌فرض خطا برای 4xx/5xx پرتاب نمی‌کند. برای بارهای بالاتر، محدودیت همزمانی (pooling) یا صف‌بندی لازم است.

اتوماسیون مرورگر با Puppeteer برای فرم‌های دینامیک

برای فرم‌هایی که با جاوااسکریپت به‌طور داینامیک ساخته می‌شوند یا دارای کنترل‌های پیچیده‌اند، بهتر است مرورگر را شبیه‌سازی کنید. Puppeteer به شما اجازه می‌دهد صفحه را بارگذاری، المان‌ها را پیدا، پر و فرم را submit کنید.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://example.com/login', { waitUntil: 'networkidle2' });

  await page.type("input[name='username']", 'myuser');
  await page.type("input[name='password']", 'mypassword');
  await page.click("button[type='submit']");

  // اگر فرم با AJAX ارسال می‌شود، منتظر پاسخ سرور یا تغییر DOM باشید
  await page.waitForSelector('#welcome', { timeout: 5000 }).catch(()=>{});

  await browser.close();
})();

توضیح: در این مثال ورودی‌ها و کلیک شبیه رفتار کاربر انجام می‌شود. از waitForSelector برای همگام‌سازی با المان‌هایی که بعداً بارگذاری می‌شوند استفاده کنید.

ارسال AJAX داخل صفحه با Puppeteer.evaluate

// داخل context صفحه اجرا می‌شود
await page.evaluate(async () => {
  const formData = new FormData(document.querySelector('form'));
  const res = await fetch('/api/submit', { method: 'POST', body: formData });
  return res.json();
});

مزیت: اجرای کد در فضای صفحه باعث می‌شود تمام کوکی‌ها، توکن‌ها و هدرهای مورد نیاز به‌طور خودکار لحاظ شوند. معایب: پیدا کردن خطاها کمی سخت‌تر است و نیاز به serialize کردن نتایج دارید.

Playwright: مرورگرهای چندگانه و دیباگ

Playwright مشابه Puppeteer است اما پشتیبانی رسمی از Chromium، Firefox و WebKit را دارد — برای تست و اسکریپینگ cross-browser مناسب است.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://example.com/login');
  await page.fill("input[name='username']", 'demo');
  await page.fill("input[name='password']", 'demo');
  await page.click("button[type='submit']");
  await browser.close();
})();

برای دیباگ می‌توانید headless: false یا slowMo را فعال کنید تا گام‌ها آهسته اجرا شوند و رفتار را ببینید.

پارسه کردن فرم‌ها با Cheerio و Axios (وب اسکریپینگ بدون مرورگر)

اگر هدف فقط خواندن و ارسال فرم (بدون اجرای JS پیچیده) باشد، ترکیب Axios و Cheerio بسیار سبک و سریع است.

const axios = require('axios');
const cheerio = require('cheerio');

axios.get('https://example.com/login')
  .then(res => {
    const $ = cheerio.load(res.data);
    const form = $('form').first();
    const data = {};
    form.find('input').each((i, el) => {
      const name = $(el).attr('name');
      if (name) data[name] = $(el).val() || '';
    });
    console.log('Parsed form data:', data);
  });

کاربرد: گرفتن مقادیر hidden مانند CSRF token و سپس ارسال آن‌ها با یک POST ساده.

NightmareJS برای اتوماسیون سبک بر پایه Electron

NightmareJS برای اتوماسیون‌هایی که به render واقعی صفحه و تعاملات ساده نیاز دارند مناسب است؛ سینتکس آن زنجیروار (chain) است و استفادهٔ سریعی دارد.

const Nightmare = require('nightmare');
const nightmare = Nightmare({ show: false });

nightmare
  .goto('https://example.com/login')
  .type('#username', 'practice')
  .type('#password', 'pass')
  .click('#login button')
  .wait('#welcome')
  .end()
  .then(() => console.log('Submitted'))
  .catch(err => console.error(err));

نکته CSRF: ابتدا صفحه را بارگذاری کرده و مقدار توکن را استخراج کنید، سپس آن را در درخواست ارسال قرار دهید.

نکات پیشرفته و بهترین‌ روش‌ها

  • مدیریت خطا و Retry: برای درخواست‌های شبکه از اجرای مجدد با backoff نمایی استفاده کنید (مثال: 500ms → 1000ms → 2000ms).
  • Timeout و Cancellation: همیشه timeout مشخص کنید تا درخواست‌ها روی منابع سرور قفل نمانند.
  • سرعت و نرخ‌ محدودسازی: برای جلوگیری از بلاک شدن، همزمانی را کنترل و نرخ (rate) را محدود کنید.
  • هدرها و User-Agent: هدرهای مناسب (User-Agent, Accept-Language) ارسال کنید و در صورت نیاز روتیشن UA و پراکسی داشته باشید.
  • امنیت و حریم خصوصی: هرگز اعتبارنامه‌ها را در لاگ‌های عمومی ثبت نکنید و از HTTPS استفاده کنید. مراقب CSRF و XSS باشید.
  • مدیریت نشست‌ها: برای فرم‌های وابسته به جلسه از cookie-jar یا context مرورگر استفاده کنید.
  • تشخیص CAPTCHA و بلوک‌ها: اگر سایت از CAPTCHA استفاده می‌کند، باید راهکارهای جایگزین (API رسمی یا تعامل انسانی) در نظر بگیرید.
  • قوانین و احترام به قوانین سایت: قبل از اسکریپ کردن، robots.txt و شرایط استفاده را بررسی کنید تا مسائل حقوقی پیش نیاید.

جمع‌بندی

برای فرم‌های ساده و APIها، Axios یا node-fetch کافی و سریع است. برای فرم‌های دینامیک یا نیاز به تعامل با جاوااسکریپت صفحه، از Puppeteer یا Playwright استفاده کنید. برای اسکریپینگ سبکِ HTML به‌صورت سرور-به-سرور، ترکیب Axios و Cheerio مناسب است. در نهایت، همیشه مدیریت خطا، زمان‌بندی مناسب، و رعایت قوانین سایت را در طراحی اسکریپ خود لحاظ کنید.

منابع و گام‌های بعدی

برای پیاده‌سازی عملی: 1) با یک فرم ساده شروع کنید، 2) خطاها و timeout را اضافه کنید، 3) اگر نیاز به اجرای جاوااسکریپت صفحه دارید به سراغ Puppeteer/Playwright بروید، و 4) در مراحل بعد پراکسی، لاگ و مانیتورینگ را اضافه کنید.

مقاله‌های مرتبط