خانه/مقالات/آموزش سریع وب‌اسکریپینگ با Playwright: دانلود فایل از سایت‌ها
آموزش سریع وب‌اسکریپینگ با Playwright: دانلود فایل از سایت‌ها

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

این راهنمای جامع و عملی نشان می‌دهد چگونه با Playwright لینک‌های دانلود را پیدا کرده، با منتظر ماندن برای رویداد <strong>download</strong> فایل‌ها را ذخیره کنید، و برای بهبود سرعت یا دانلودهای هم‌زمان از axios استفاده کنید؛ همچنین نکات مهم در مورد احراز هویت، پایش پیشرفت، بررسی خطاها و بهترین‌روش‌های عملی برای پروژه‌های وب اسکریپینگ توضیح داده شده‌اند.
امیر حسین حسینیان
امیر حسین حسینیان
1404-10-11

مقدمه

دانلود فایل‌ها در پروژه‌های وب اسکریپینگ یکی از نیازهای رایج است—از گرفتن PDF و تصاویر تا آرشیو کردن پیوست‌ها. در این مقاله عملی و فنی، از پایه تا تکنیک‌های پیشرفته با Playwright و روش‌های تکمیلی مثل axios را می‌بینیم. پس از خواندن این مطلب شما یاد می‌گیرید که چگونه لینک‌ها را پیدا کنید، دانلود را آغاز و مدیریت کنید، چند فایل را هم‌زمان دانلود کنید، پیشرفت دانلود را پایش کنید و مسائل عملی مثل احراز هویت، تغییر مسیر دانلود و مدیریت خطا را پیاده‌سازی نمایید.

TLDR: مثال سریع دانلود با Playwright

اینجا یک اسکریپت فشرده که مراحل اصلی را نشان می‌دهد: ایجاد مرورگر، ایجاد context با اجازه دانلود، کلیک روی لینک، انتظار برای رویداد "download" و ذخیره فایل با نام پیشنهادی.

const playwright = require("playwright");
const path = require("path");

(async () => {
  const browser = await playwright.chromium.launch();
  const context = await browser.newContext({ acceptDownloads: true });
  const page = await context.newPage();

  await page.goto("https://example.com/page-with-pdf");

  // آماده‌سازی Promise برای شنیدن رویداد دانلود
  const downloadPromise = page.waitForEvent("download");

  // کلیک یا هر عملی که دانلود را شروع می‌کند
  await page.locator("a.download-link").click();

  // وقتی دانلود رخ داد، شی دانلود را بگیرید و آن را ذخیره کنید
  const download = await downloadPromise;
  const filePath = path.join(process.cwd(), download.suggestedFilename());
  await download.saveAs(filePath);

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

توضیح سریع:

  • ورودی: آدرس صفحه‌ای که لینک دانلود دارد یا خود URL فایل.
  • خروجی: فایل ذخیره‌شده در مسیر محلی.
  • نقش توابع: page.waitForEvent("download") یک Promise می‌سازد که تا رخداد دانلود منتظر می‌ماند، و download.saveAs() فایل را در دیسک ذخیره می‌کند.

درک کلی: چرا GET و تفاوت نمایش/دانلود مهم است

وقتی مرورگر «نمایش» یک منبع را انجام می‌دهد، پاسخ سرور در حافظه مرورگر رندر می‌شود. در حالت دانلود، همان پاسخ به فایل نوشته می‌شود. برای دانلود معمولاً از GET استفاده می‌شود؛ ولی نکته مهم هدرها (مثل content-disposition و content-type) و رفتار سرور است که تعیین می‌کند مرورگر فایل را دانلود کند یا داخل صفحه نمایش دهد.

دانلود یک فایل با Playwright: گام‌به‌گام

ایده ساده است: پیدا کردن المان دانلود → شروع دانلود (کلیک یا رفتن به URL) → انتظار برای رویداد دانلود → ذخیره‌سازی. هر مرحله را با جزئیات ببینیم:

مراحل عملیاتی:

  1. پیدا کردن لینک/دکمه (با CSS selector، XPath یا locator های Playwright)
  2. ساختن downloadPromise با page.waitForEvent("download")
  3. شروع دانلود با click() یا page.goto(url)
  4. گرفتن شی download و ذخیره آن با saveAs()

تنظیم مسیر و نام فایل سفارشی

برای مرتب نگه داشتن دانلودها معمولاً یک پوشه مخصوص ایجاد می‌کنیم و نام فایل را کنترل می‌کنیم. نکات مهم: پیش از ذخیره‌سازی وجود مسیر را بررسی کنید و در صورت لزوم بسازید؛ از نام پیشنهادی سرور یا نام دلخواه استفاده کنید.

const fs = require("fs");
const path = require("path");

// مثال: ساخت فولدر دانلود
const downloadsFolder = path.join(process.cwd(), "pdfDownloads");
if (!fs.existsSync(downloadsFolder)) fs.mkdirSync(downloadsFolder);

// هنگام دریافت download object:
const filePath = path.join(downloadsFolder, "MY_CUSTOM_NAME.pdf");
await download.saveAs(filePath);

توضیح: این الگو برای اسکریپ‌هایی مناسب است که نیاز به نام‌گذاری منظم یا ذخیره در دایرکتوری‌های مجزا دارند.

مدیریت مودال‌ها و دیالوگ‌های دانلود

گاهی قبل از دانلود یک پنجرهٔ پاپ‌آپ یا مودال ظاهر می‌شود. Playwright با locator و click() اجازه می‌دهد آن‌ها را ببندید یا تایید کنید و سپس دانلود را ادامه دهید. اگر مودال روی صفحه باشد، ابتدا آن را ببندید و سپس عملیات دانلود را انجام دهید.

دانلود چند فایل: استفاده از توابع و Promise.all

برای دانلود دسته‌ای از فایل‌ها، به جای اینکه هر فایل را با Playwright دریافت کنید، معمولاً آدرس دانلودها را با Playwright اسکریپ می‌کنیم و خود دانلودها را با یک HTTP client (مثل axios) به‌صورت هم‌زمان انجام می‌دهیم. الگوی رایج:

// آرایه‌ای از لینک‌های فایل
const links = await page.$$eval("a[href$='.pdf']", els => els.map(a => a.href));

// تابع کمکی برای دانلود یک فایل با context (یا با axios)
async function downloadFile(context, linkHref) {
  const page = await context.newPage();
  try {
    const [download] = await Promise.all([
      page.waitForEvent("download"),
      page.goto(linkHref),
    ]);
    const downloadPath = path.join(process.cwd(), download.suggestedFilename());
    await download.saveAs(downloadPath);
  } catch (err) {
    console.error(`Failed to download ${linkHref}:`, err);
  } finally {
    await page.close();
  }
}

// اجرای هم‌زمان
await Promise.all(links.map(l => downloadFile(context, l)));

نکته: اجرای هم‌زمان زیاد می‌تواند منجر به فشار روی سرور یا بلوکه شدن شود؛ آن را با محدودسازی concurrency (مثلاً با p-limit یا صف ساده) کنترل کنید.

وقتی سرعت مهم است: استفاده از axios برای دانلودهای سنگین یا تعداد بالا

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

const axios = require("axios");
const fs = require("fs");

async function downloadImage(url, filepath) {
  const response = await axios({ url, method: "GET", responseType: "stream" });
  const writer = fs.createWriteStream(filepath);
  response.data.pipe(writer);
  return new Promise((resolve, reject) => {
    writer.on("finish", resolve);
    writer.on("error", reject);
  });
}

// استفاده‌ی هم‌زمان با Promise.all
await Promise.all(imageUrls.map(url => {
  const name = path.basename(new URL(url).pathname);
  const fp = path.join(downloadDir, `${name}.png`);
  return downloadImage(url, fp).then(() => console.log("Downloaded:", name));
}));

مزیت: اتصال مستقیم به سرور بدون سربار یک مرورگر؛ امکان اعمال headerها، retry و مدیریت استریم‌ها آسان‌تر است.

پایش پیشرفت دانلود

برای نمایش درصد دانلود وقتی از استریم استفاده می‌کنید، هدر content-length را می‌خوانیم و با هر chunk دریافتی مقدار پیشرفت را به‌روز می‌کنیم:

const response = await axios({ url, method: "GET", responseType: "stream" });
const total = Number(response.headers['content-length']);
let received = 0;
response.data.on("data", chunk => {
  received += chunk.length;
  console.log(`Received ${received} of ${total} bytes (${((received/total)*100).toFixed(2)}%)`);
});

// سپس pipe به فایل و بازگرداندن Promise مانند قبل

نکته: بعضی سرورها content-length نمی‌دهند، در آن صورت نمایش درصد دقیق غیرممکن است و باید از نشانگرهای دیگر (مثلاً ETA مبتنی بر سرعت فعلی) استفاده کنید.

تایید دانلود (مستقل و خودکار)

برای اطمینان از موفقیت دانلود می‌توانید به‌صورت دستی پوشه را بررسی کنید یا در کد با fs.existsSync(path) و بررسی سایز فایل یا محتوای آن اعتبارسنجی کنید:

const exists = fs.existsSync(downloadPath);
if (exists) console.log(`File found at: ${downloadPath}`);
else console.error(`Failed to find file at: ${downloadPath}`);

احراز هویت برای دانلودهای محافظت‌شده

برای دانلود از صفحات محافظت‌شده ابتدا با Playwright لاگین کنید، سپس از همان context برای دسترسی به لینک‌های محافظت‌شده استفاده نمایید—کوکی و session ذخیره شده اجازهٔ دانلود می‌دهد.

// مثال ساده پر کردن فرم
await page.goto("https://example.com/login");
await page.locator("#username").fill("myuser");
await page.locator("#password").fill("mypassword");
await page.locator("button[type='submit']").click();
// اکنون context احراز هویت شده را برای دانلود استفاده کنید

نکته: برای اجرای بدون UI می‌توانید state یا cookies را ذخیره و در اجراهای بعدی بازیابی کنید تا دوباره لاگین نکنید.

خطاها، تایم‌اوت‌ها و بهترین‌روش‌ها

  • استفاده از header مناسب (مثل User-Agent) و مدیریت ریترای هوشمند برای مواجهه با خطاهای موقتی.
  • محدودسازی concurrency هنگام دانلود تعداد زیادی فایل تا از بلوکه شدن جلوگیری شود.
  • برای فایل‌های خیلی بزرگ، به جای نگه داشتن اتصال مرورگر بهتر است URL را به یک دانلود منیجر یا کلاینت چندرشته‌ای بدهید.
  • استفاده از پروکسی و رِیت-لیمیت برای عبور از دفاع‌های سادهٔ سرورها—ولی همیشه قوانین و سیاست‌های سایت را رعایت کنید.
  • اضافه کردن بررسی فرمت فایل (پسوند یا header content-type) تا از اشتباهات جلوگیری شود.
  • تنظیم timeoutها: گاهی لازم است page.goto را با { timeout: 60000 } اجرا کنید یا آن را غیرفعال نمایید.

جمع‌بندی

ترکیب Playwright برای استخراج لینک‌ها و رفتار مبتنی بر مرورگر و استفاده از axios یا سایر کلاینت‌های HTTP برای خود دانلود اغلب بهترین ترکیب است: کنترل دقیق DOM، مدیریت احراز هویت و سپس دانلود سریع و مقیاس‌پذیر. در عمل بهینه‌سازی‌هایی مثل مدیریت concurrency، بررسی هدرها، پایش پیشرفت و سیاست‌های retry را پیاده کنید تا سیستم دانلود مطمئن و پایدار داشته باشید.

پیشنهاد عملی

  • ابتدا اسکریپ جمع‌آوری لینک‌ها را بنویسید؛ سپس دانلود را با محدودیت concurrency انجام دهید.
  • برای پروژه‌های تولیدی، لاگ کامل، متریک‌های موفق/ناموفق و مکانیزم ری‌تری را اضافه کنید.
  • قبل از اسکریپ کردن مقیاس بزرگ، سیاست‌های سایت را بررسی کنید و از راهکارهای اخلاقی و قانونی تبعیت نمایید.
مقاله‌های مرتبط
اسکرپینگ با Selenium و Playwright
1404-11-23
اجرای Playwright در Jupyter برای وب اسکریپینگ
این مقاله گام‌به‌گام نشان می‌دهد چگونه Playwright را در Jupyter Notebook اجرا کنید تا وب اسکریپینگ تعاملی و سریع انجام دهید؛ شامل نصب کرنل، مثال‌های کد، روش‌های اشکال‌زدایی، الگوهای retry، مدیریت حافظه و نگهداری امن اعتبارنامه‌ها.
اسکرپینگ با Selenium و Playwright
1404-11-20
اسکریپ با Playwright: گرفتن اسکرین‌شات
این راهنمای عملی نشان می‌دهد چگونه با Playwright انواع اسکرین‌شات (صفحه کامل، ناحیه‌ای، المنتی، سفارشی با کیفیت) بگیریم، خروجی را به بافر منتقل کنیم، PDF بسازیم و خطاهای رایج را رفع کنیم. مثال‌های کد محور، نکات بهینه‌سازی و الگوهای retry/pattern برای پروژه‌های وب اسکریپینگ ارائه شده‌اند.
اسکرپینگ با Selenium و Playwright
1404-11-18
اسکریپ فرم‌ها با Playwright برای توسعه‌دهنده‌های پایتون
این راهنمای عملی به توسعه‌دهنده‌های پایتون نشان می‌دهد چگونه با Playwright فرم‌های وب را برای اسکریپینگ و تست اتوماتیک کنند؛ شامل انتخابگرها، نمونه‌کدهای پایتون برای انواع ورودی‌ها، مدیریت پاسخ‌ها، روش‌های حل کپچا و نکات رفع اشکال و بهترین‌شیوه‌ها است.