مقدمه
در این راهنما عملی به موضوع دانلود فایلها هنگام انجام وب اسکریپینگ با Selenium در پایتون میپردازیم. هدف این است که پس از خواندن مقاله شما بتوانید لینکها یا دکمههای دانلود را پیدا کنید، آنها را کلیک کنید، وضعیت دانلود را تضمین کنید و برای فایلهای خاص (مثل PDF) تنظیمات مرورگر را بهدرستی انجام دهید. همچنین نکات مرتبط با همزمانی، خطایابی و امنیت را پوشش میدهیم.
مثال سریع (TLDR)
یک نسخهٔ ساده و سریع که جریان کلی کار را نشان میدهد: باز کردن مرورگر، پیدا کردن دکمه، کلیک و پایان کار. در کد زیر از یک متغیر جایگزین برای آدرس استفاده شده — آن را با آدرس صفحهٔ هدف خود جایگزین کنید.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep
# آدرس صفحهٔ دانلود را به متغیر زیر اختصاص بدهید
url = "DOWNLOAD_PAGE_URL"
# شروع یک نمونهٔ Chrome WebDriver (باید chromedriver در PATH باشد)
driver = webdriver.Chrome()
# باز کردن صفحه
driver.get(url)
# xpath دکمهٔ دانلود را پیدا کنید (مثال: xpath = "...")
xpath = "XPATH_TO_DOWNLOAD_BUTTON"
button = driver.find_element(By.XPATH, xpath)
# استفاده از ActionChains برای حرکت و کلیک روی دکمه
action = ActionChains(driver)
action.move_to_element(button).click().perform()
# منتظر بمانید تا دانلود شروع شود (در نمونهٔ ساده از sleep استفاده شده)
sleep(10)
driver.quit()توضیح کلی کد:
- ورودیها: url و xpath مطابق صفحهٔ هدف.
- خروجی: مرورگر را باز و بعد از کلیک بسته میشود؛ فایل در پوشهٔ دانلود محلی قرار میگیرد (با تنظیمات پیشفرض مرورگر).
- بخشهای مهم: ایجاد webdriver.Chrome()، یافتن عنصر با find_element و اجرای حرکت و کلیک با ActionChains.
پیدا کردن لینک یا دکمهٔ دانلود
پایهٔ هر اتوماسیون موفق، پیدا کردن درست عنصر است. Selenium چندین locator دارد: By.XPATH, By.ID, By.CLASS_NAME, By.TAG_NAME و غیره. بهطور معمول اولویت بدهید به شناسهها (ID)، سپس کلاس، و در نهایت XPath در صورتی که گزینهٔ بهتری موجود نیست.
from selenium import webdriver
from selenium.webdriver.common.by import By
url = "TARGET_PAGE_URL"
driver = webdriver.Chrome()
driver.get(url)
# مثال: پیدا کردن عنوان صفحه با tag name
header = driver.find_element(By.TAG_NAME, "h1")
print(f"header: {header.text}")
# مثال: پیدا کردن مجموعهای از عناصر با class name
tags = driver.find_elements(By.CLASS_NAME, "tags")
for tag in tags:
print(f"Tag: {tag.text}")
# توجه: اگر قرار است عناصر بلندمدت استفاده شوند، مقادیر href را در یک لیست ذخیره کنید
# تا از stale element جلوگیری شود.
driver.quit()نکات مهم:
- برای استخراج لینکهای دانلود از get_attribute('href') استفاده کنید و آنها را در لیستی جدا ذخیره کنید تا اگر DOM تغییر کرد، ارجاعات منقضی نشوند.
- همیشه خروجی text و attributes را بررسی کنید تا مطمئن شوید عنصر درست انتخاب شده است.
کلیک کردن روی لینک یا دکمه
در بسیاری از صفحات، عنصر دانلود مستقیماً قابل کلیک نیست یا باید ابتدا روی منوی جانبی کلیک کنید. در این موارد ActionChains جایگزین مفیدی است زیرا میتوانید سلسله اعمال (حرکت، کلیک راست، ارسال کلید و…) را زنجیره کنید.
# نمونهٔ استفاده از ActionChains
from selenium.webdriver.common.action_chains import ActionChains
# فرض کنیم قبلاً button را با find_element پیدا کردهایم
action = ActionChains(driver)
action.move_to_element(button).click().perform()شرح: move_to_element مکان موس را روی عنصر قرار میدهد، سپس click را زنجیره و در پایان perform اجرا میکند. این روش بهویژه زمانی که عنصر مخفی یا خارج از دید است مفید است.
انتظار برای تکمیل دانلود
مشکل اصلی پس از کلیک، مشخص کردن زمان پایان دانلود است. رویکردهای معمول:
- استفاده از یک توقف ساده مثل sleep() (ساده اما ناپایدار)
- پایش فایل سیستم (بررسی وجود فایل در پوشهٔ دانلود)
- در محیط Chrome: بازدید از صفحهٔ دانلودِ مرورگر برای کنترل وضعیت (در محیطهای تعاملی)
نمونهٔ بهتر با استفاده از چک فایل سیستم (بهجای استفاده از URLهای خاص):
import os
from time import sleep
# path را به پوشهٔ دانلود محلیتان تنظیم کنید
download_path = "/path/to/downloads"
expected_name = "package_name" # بخشی از نام فایل مورد انتظار
def wait_for_file(path, name, timeout=30):
"""منتظر بمانید تا فایلی که شامل `name` است در `path` ظاهر شود."""
elapsed = 0
while elapsed < timeout:
for f in os.listdir(path):
if name in f:
return os.path.join(path, f)
sleep(1)
elapsed += 1
return None
# بعد از کلیک
file_path = wait_for_file(download_path, expected_name, timeout=60)
if file_path:
print("Found downloaded file:", file_path)
else:
print("Download did not complete in time")در این مثال:
- ورودیها: مسیر پوشهٔ دانلود و رشتهای که در نام فایل انتظار میرود.
- خروجی: مسیر فایل اگر پیدا شود یا None در صورت timeout.
- مزیت: قابل استفاده در محیط سرور که دسترسی به صفحهٔ مرورگر داخلی ممکن نیست.
مدیریت دیالوگها و منوهای دانلود
گاهی دیالوگهای مرورگر یا منوهای HTML مانع دانلود مستقیم میشوند. راهکارها:
- استفاده از ActionChains یا اجرای کلیکهای پشت سر هم برای باز کردن منوها.
- در صورت امکان تنظیم دیگران (مثلاً انتخاب فرمت یا گزینهٔ ارائه شده) از طریق find_element و ارسال کلیدها.
# نمونهٔ کلیک روی تب فایل سپس کلیک روی لینک دانلود
button = driver.find_element(By.ID, "files-tab")
button.click()
# سپس لینک دانلود (با xpath یا locator مناسب) را پیدا و کلیک میکنیم
link = driver.find_element(By.XPATH, "XPATH_TO_FILE_LINK")
link.click()بررسی و تایید دانلود
تأیید دانلود ضروری است تا مطمئن شوید فایل کامل و سالم است. روشهای معمول:
- بررسی وجود فایل و اندازهٔ آن با os.stat.
- استفاده از الگوریتمهای checksum (مانند sha256) اگر هش اصلی در دسترس باشد.
- برای فایلهای باینری مانند PDF یا تصاویر بررسی باز شدن یا parse شدن با ابزارهای مناسب.
import hashlib
# محاسبهٔ sha256 برای تایید یک فایل
def sha256_of_file(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
# مثال استفاده:
# expected_hash = "..."
# actual = sha256_of_file(file_path)
# assert actual == expected_hash
بهترین روشها
چند توصیهٔ عملی برای تولید اسکریپتهای قابل اتکا:
- به جای sleep() از implicitly_wait یا WebDriverWait استفاده کنید.
- مقادیر حساس و مسیرها را بهعنوان تنظیمات (config) نگه دارید، نه درون کد ثابت.
- از لاگینگ برای ضبط خطاها و رویدادها استفاده کنید تا اسکریپت در محیط تولید قابلنگهداری باشد.
- عنصرها را پیش از استفاده در متغیرهای بلندمدت ذخیره کنید (برای جلوگیری از stale element).
# مثال: implicit و explicit wait
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# implicit
driver.implicitly_wait(10)
# explicit
wait = WebDriverWait(driver, 15)
link = wait.until(EC.presence_of_element_located((By.XPATH, "XPATH_TO_FILE_LINK")))
link.click()مدیریت خطا و لاگینگ
در اجراهای طولانیمدت لازم است خطاها را بگیرم و لاگ کنیم تا فرآیند قبل از پاکسازی منابع ادامه دهد یا حداقل وضعیت ثبت شود.
import logging
from selenium import webdriver
from selenium.webdriver.common.by import By
logging.basicConfig(filename='errors.log', level=logging.INFO)
driver = webdriver.Chrome()
try:
driver.get("TARGET_PAGE_URL")
link = driver.find_element(By.XPATH, "XPATH_TO_FILE_LINK")
link.click()
except Exception as e:
logging.exception("Download failed: %s", e)
finally:
driver.quit()
کار با فایلهای خاص (مثلاً PDF)
برای دانلود خودکار فایلهایی که مرورگر آنها را باز میکند (مثل PDF)، باید تنظیمات مرورگر را تغییر دهید تا دانلود بهطور خودکار صورت گیرد و نمایش داخلی غیرفعال شود. در Chrome از گزینههای تجربی استفاده میکنیم.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import os
# مسیر پوشهٔ دانلود را تعیین کنید
download_dir = os.getcwd()
options = Options()
prefs = {
"download.default_directory": download_dir,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"plugins.always_open_pdf_externally": True,
"plugins.plugins_list": [{"enabled": False, "name": "Chrome PDF Viewer"}]
}
options.add_experimental_option("prefs", prefs)
driver = webdriver.Chrome(options=options)
# سپس به url فایل PDF یا صفحهای که لینک PDF دارد بروید
# driver.get("PDF_FILE_PAGE_OR_DIRECT_LINK")
نکته: در محیطهای headless یا سرور ممکن است لازم باشد تنظیمات اضافی یا نسخهٔ مناسب chromedriver را تنظیم کنید.
دانلود موازی و استخراج گروهی
برای دانلود چند فایل، رویکرد ایمن این است که ابتدا همهٔ لینکها را جمعآوری کنید و سپس آنها را بهصورت ترتیبی یا چندنخی (با احتیاط) پردازش کنید. در مثال زیر لینکهای PDF را جمعآوری و سپس هر URL را با WebDriver باز میکنیم تا دانلود آغاز شود.
# ابتدا لینکها را ذخیره میکنیم تا از stale element جلوگیری شود
links = driver.find_elements(By.XPATH, "//a[contains(@href, '.pdf')]")
list_of_links = [l.get_attribute('href') for l in links]
for href in list_of_links:
try:
driver.get(href) # باز کردن مستقیم URL معمولاً دانلود را آغاز میکند
except Exception:
print("dead link:", href)
# سپس میتوانیم پوشهٔ دانلود را پایش کنیم تا دانلودها کامل شوند
هشدارها و نکات عملکردی:
- اگر تعداد فایلها زیاد است، به جای باز کردن هر لینک با یک مرورگر، بهتر است از دانلود مستقیم (مثلاً requests یا session با cookieهای لازم) استفاده کنید تا مصرف منابع کاهش یابد.
- باز کردن صفحات زیاد با Selenium منابع زیادی مصرف میکند و ممکن است با محدودیتهای حافظه و پردازنده مواجه شوید.
ملاحظات امنیتی
فایلهایی که دانلود میکنید ممکن است حاوی دادهٔ حساس یا کد مخرب باشند. نکات ضروری:
- فایلهای حساس را رمزنگاری کنید یا با دسترسی محدود ذخیره کنید.
- برای بررسی صحت فایلها از checksum استفاده کنید و از اجرای خودکار فایلهای دانلود شده خودداری کنید.
- در صورت نیاز به ورود یا توکن، کلیدها و رمزعبورها را در متغیرهای محیطی یا مدیریتکنندهٔ اسرار (secret manager) نگهداری کنید.
جمعبندی
دانلود فایل با Selenium ممکن است ساده بهنظر برسد اما برای تولید یک اسکریپت قابلاطمینان باید به محلیابی صحیح عناصر، مدیریت دیالوگها، انتظار برای تکمیل دانلود، بررسی فایل در فایلسیستم و نکات امنیتی توجه کنید. هرگاه امکان دارد برای خودِ دانلود از روشهای مستقیم HTTP (با احراز هویت و کوکیها) استفاده کنید تا مصرف منابع کاهش یابد؛ اما وقتی صفحهٔ هدف پیچیده یا جاوااسکریپتی است، Selenium ابزار مناسبی برای شبیهسازی رفتار کاربر و شروع دانلود است.





