

دانلود فایلها در پروژههای وب اسکریپینگ یکی از نیازهای رایج است—از گرفتن PDF و تصاویر تا آرشیو کردن پیوستها. در این مقاله عملی و فنی، از پایه تا تکنیکهای پیشرفته با Playwright و روشهای تکمیلی مثل axios را میبینیم. پس از خواندن این مطلب شما یاد میگیرید که چگونه لینکها را پیدا کنید، دانلود را آغاز و مدیریت کنید، چند فایل را همزمان دانلود کنید، پیشرفت دانلود را پایش کنید و مسائل عملی مثل احراز هویت، تغییر مسیر دانلود و مدیریت خطا را پیادهسازی نمایید.
اینجا یک اسکریپت فشرده که مراحل اصلی را نشان میدهد: ایجاد مرورگر، ایجاد 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();
})();توضیح سریع:
وقتی مرورگر «نمایش» یک منبع را انجام میدهد، پاسخ سرور در حافظه مرورگر رندر میشود. در حالت دانلود، همان پاسخ به فایل نوشته میشود. برای دانلود معمولاً از GET استفاده میشود؛ ولی نکته مهم هدرها (مثل content-disposition و content-type) و رفتار سرور است که تعیین میکند مرورگر فایل را دانلود کند یا داخل صفحه نمایش دهد.
ایده ساده است: پیدا کردن المان دانلود → شروع دانلود (کلیک یا رفتن به URL) → انتظار برای رویداد دانلود → ذخیرهسازی. هر مرحله را با جزئیات ببینیم:
مراحل عملیاتی:
برای مرتب نگه داشتن دانلودها معمولاً یک پوشه مخصوص ایجاد میکنیم و نام فایل را کنترل میکنیم. نکات مهم: پیش از ذخیرهسازی وجود مسیر را بررسی کنید و در صورت لزوم بسازید؛ از نام پیشنهادی سرور یا نام دلخواه استفاده کنید.
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() اجازه میدهد آنها را ببندید یا تایید کنید و سپس دانلود را ادامه دهید. اگر مودال روی صفحه باشد، ابتدا آن را ببندید و سپس عملیات دانلود را انجام دهید.
برای دانلود دستهای از فایلها، به جای اینکه هر فایل را با 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 یا صف ساده) کنترل کنید.
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 را ذخیره و در اجراهای بعدی بازیابی کنید تا دوباره لاگین نکنید.
ترکیب Playwright برای استخراج لینکها و رفتار مبتنی بر مرورگر و استفاده از axios یا سایر کلاینتهای HTTP برای خود دانلود اغلب بهترین ترکیب است: کنترل دقیق DOM، مدیریت احراز هویت و سپس دانلود سریع و مقیاسپذیر. در عمل بهینهسازیهایی مثل مدیریت concurrency، بررسی هدرها، پایش پیشرفت و سیاستهای retry را پیاده کنید تا سیستم دانلود مطمئن و پایدار داشته باشید.


