خانه/مقالات/اسکریپینگ فرم‌ها با پایتون
برنامه نویسی
سلنیوم
beautifulsoup
برگشت به مقاله‌ها

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

اسکریپینگ فرم‌ها با پایتون
در این راهنمای جامع به چندین روش ارسال فرم با پایتون پرداخته‌ایم: از Requests و BeautifulSoup برای فرم‌های ساده تا Selenium/Playwright برای فرم‌های داینامیک و httpx برای همزمانی. هر روش با مثال کد، نکات امنیتی (مثل CSRF و توکن‌ها) و بهترین‌روش‌های عملی توضیح داده شده تا بتوانید بر اساس نیاز پروژهٔ خود انتخاب کنید.
آسان اسکریپ آسان اسکریپ
1405-05-02

مقدمه

در این راهنمای عملی می‌خوانید چگونه فرم‌های وب را با پایتون ارسال کنید و از روش‌های مختلف برای سناریوهای واقعی استفاده کنید: درخواست‌های ساده HTTP، پارس کردن فرم‌ها، اتوماسیون کامل مرورگر و راهکارهای همزمان. فرض می‌کنیم شما توسعه‌دهنده‌‌ای با دانش متوسط پایتون هستید و دنبال جزئیات فنی، نمونه کد و نکات عملی برای تولید اسکریپ‌های قابل‌اعتماد هستید.

در پایان این مقاله شما خواهید دانست چه زمانی از requests استفاده کنید، چه زمانی به سراغ ابزارهای مرورگر مانند Selenium یا Playwright بروید، و چطور با مشکلات رایج مثل CSRF، فایل‌آپلود، احراز هویت و همزمانی برخورد کنید.

درک کلی: ارسال فرم‌های HTML

فرم‌های HTML از عناصر مختلفی تشکیل شده‌اند: input، select، textarea و دکمه‌ها. معمولاً فرم‌ها با متد POST داده‌ها را در بدنهٔ درخواست می‌فرستند؛ این روش نسبت به GET امن‌تر و مناسب‌تر برای اطلاعات حساس است. بسیاری از فرم‌ها همچنین توکن‌های پنهان (مثلاً CSRF) یا هدرهای سفارشی انتظار دارند که بدون آن‌ها درخواست رد می‌شود.

برای اسکریپ کردن فرم‌ها باید سه کار اصلی انجام دهید: 1) شناسایی فیلدها و آدرس action فرم، 2) آماده‌سازی داده‌ها و هدرها، 3) ارسال درخواست و بررسی پاسخ.

روش‌های اصلی در پایتون

  • requests برای درخواست‌های HTTP ساده و کنترل کوکی‌ها با Session.

  • BeautifulSoup + requests برای استخراج فرم و توکن‌ها.

  • Selenium یا Playwright برای فرم‌های داینامیک که با جاوااسکریپت ساخته می‌شوند.

  • Mechanize برای تعامل شبیه به مرورگر با فرم‌های ساده (قدیمی‌تر اما سبک).

  • Scrapy برای پروژه‌های ساخت‌یافتهٔ اسکریپینگ با پشتیبانی از FormRequest و pipelines.

  • httpx و ترکیب آن با asyncio یا trio برای ارسال‌های همزمان و مقیاس‌پذیر.

Requests و مدیریت Session

برای فرم‌های ساده‌ای که نیاز به اجرأ جاوااسکریپت ندارند، requests سریع‌ترین و ساده‌ترین انتخاب است. با Session می‌توان کوکی‌ها و هدرها را بین درخواست‌ها حفظ کرد.

نمونه: ارسال POST با requests.Session و بررسی متن پاسخ.

import requests

url = 'https://example.com/login'
headers = {
    'User-Agent': 'Mozilla/5.0',
    'Content-Type': 'application/x-www-form-urlencoded'
}
form_data = {
    'username': 'student',
    'password': 'Password123'
}

session = requests.Session()
response = session.post(url, data=form_data, headers=headers, timeout=10)

if response.status_code == 200:
    if "Congratulations" in response.text:
        print("User logged in successfully!")
    else:
        print("Failed to log in: response OK but expected content missing")
else:
    print(f"Failed to submit form. Status code: {response.status_code}")

توضیح کد: ورودی‌ها آدرس url، دیکشنری form_data و headers است؛ خروجی یک Response است که وضعیت و متن پاسخ را دارد. نکات خط‌به‌خط: ایجاد Session برای مدیریت کوکی، ارسال post(...)، تعیین timeout جهت جلوگیری از بلوکه شدن اسکریپت، و بررسی کد وضعیت و محتوای پاسخ.

احراز هویت و ارسال فایل با Requests

برای Basic Auth از پارامتر auth استفاده کنید و برای توکن‌ها، آن‌ها را در هدر Authorization: Bearer ... قرار دهید. برای فایل‌ها باید از پارامتر files استفاده کنید تا multipart/form-data ساخته شود.

# Basic Auth
from requests.auth import HTTPBasicAuth
resp = requests.post('https://example.com/secure-form', data=form_data,
                     auth=HTTPBasicAuth('user', 'pass'))

# Token-based
headers['Authorization'] = f"Bearer {os.getenv('AUTH_TOKEN')}"
resp = requests.post('https://example.com/api', data=form_data, headers=headers)

# File upload
files = {'file': open('invoice.xlsx', 'rb')}
resp = requests.post('https://example.com/upload', files=files, data={'field1':'value1'})

نکات: همیشه فایل‌ها را با حالت باینری باز کنید، و پس از ارسال آن‌ها را ببندید یا از ساختار with استفاده کنید. توکن‌ها را در متغیرهای محیطی نگه دارید، نه در کد.

پارس کردن فرم با BeautifulSoup

اگر فرم شامل توکن‌های پنهان است یا آدرس action نسبی دارد، ابتدا صفحه را با requests.get بگیرید و سپس با BeautifulSoup فیلدها را استخراج کنید.

import requests
from bs4 import BeautifulSoup

url = 'https://example.com/login'
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
form = soup.find('form')
form_data = {}
for input_tag in form.find_all('input'):
    name = input_tag.get('name')
    if not name:
        continue
    value = input_tag.get('value', '')
    form_data[name] = value
# پر کردن فیلدها
form_data['username'] = 'practice'
form_data['password'] = 'SuperSecretPassword!'
# محاسبهٔ آدرس action کامل
action = form.get('action')
if not action.startswith('http'):
    action = requests.compat.urljoin(url, action)
resp2 = requests.post(action, data=form_data)
print(resp2.status_code)

توضیح: این الگو به شما اجازه می‌دهد همهٔ فیلدهای پنهان (مثل CSRF) را استخراج کنید و مطمئن شوید همهٔ پارامترهای مورد نیاز ارسال می‌شوند.

اتوماسیون مرورگر با Selenium

برای فرم‌هایی که با جاوااسکریپت ساخته یا وِریفای می‌شوند، از Selenium استفاده کنید. Selenium عناصر را همانند کاربر واقعی پیدا می‌کند و برای تعامل با popupها، iframeها و انتظارها مناسب است.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
try:
    driver.get('https://practice.example.com/login')
    WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, 'username')))
    driver.find_element(By.ID, 'username').send_keys('practice')
    driver.find_element(By.ID, 'password').send_keys('SuperSecretPassword!')
    driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click()
    # Wait for a success element
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.dashboard')))
finally:
    driver.quit()

توضیح: از WebDriverWait و شرایط صریح استفاده کنید تا به جای sleepهای خام، به صورت ایمن روی وجود یا کلیک‌پذیری عناصر صبر کنید. برای اجرای headless، پارامترهای مربوطه را به options اضافه کنید.

Playwright برای headless و چند-مرورگری

Playwright مدرن است و پشتیبانی از Chromium/Firefox/WebKit را با API یکپارچه می‌دهد؛ برای اسکریپینگ فرم‌های پیچیده و تست کراس-براوزر گزینهٔ مناسبی است.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://demo.example.com/login')
    page.fill("input[name='username']", 'demo')
    page.fill("input[name='password']", 'demo')
    page.click("button[type='submit']")
    # منتظر یک سلکتور موفقیت باشید
    page.wait_for_selector('.welcome')
    browser.close()

نکته: Playwright سریع و قابل‌اعتماد است؛ برای اشکال‌زدایی می‌توانید headless=False و slow_mo استفاده کنید.

Mechanize برای فرم‌های ساده

Mechanize رابطی شبیه مرورگر فراهم می‌کند و برای فرم‌های ساده و سایت‌های بدون جاوااسکریپت مناسب است. اگر نیاز دارید خیلی سبک و بدون درگیری WebDriver باشید، مناسب است.

import mechanize

br = mechanize.Browser()
br.open('https://practice.example.com/login')
br.select_form(nr=0)
br['username'] = 'practice'
br['password'] = 'SuperSecretPassword!'
resp = br.submit()
print(resp.read().decode('utf-8'))

محدودیت: Mechanize جاوااسکریپت را اجرا نمی‌کند و در مواجهه با فرم‌های داینامیک یا محافظت‌شده مناسب نیست.

Scrapy: فرم‌ها در پروژه‌های اسکریپینگ ساختاریافته

برای پروژه‌های بزرگ با نیاز به pipeline و مدیریت درخواست‌ها، از Scrapy و FormRequest.from_response استفاده کنید.

import scrapy

class FormSpider(scrapy.Spider):
    name = 'form_spider'
    start_urls = ['https://practice.example.com/login']

    def parse(self, response):
        formdata = {'username': 'practice', 'password': 'SuperSecretPassword!'}
        yield scrapy.FormRequest.from_response(response, formdata=formdata, callback=self.after_login)

    def after_login(self, response):
        if b'authentication failed' in response.body:
            self.logger.error('Login failed')
            return
        self.logger.info('Successfully logged in and submitted the form')

مزیت Scrapy مدیریت خودکار نرخ درخواست، میانجی‌ها (middlewares) و pipelines برای پردازش داده است.

HTTPX و کار با همزمانی (asyncio / trio)

برای مقیاس‌پذیری و همزمانی از httpx استفاده کنید. این کتابخانه هم synchronous و هم async با API شبیه requests ارائه می‌دهد. ترکیب با trio یا asyncio برای ارسال‌های همزمان مفید است.

# نمونه با httpx + asyncio
import httpx
import asyncio

async def submit_form():
    async with httpx.AsyncClient() as client:
        resp = await client.post('https://example.com/login', data={'username':'student','password':'Password123'}, timeout=10)
        print(resp.status_code)

asyncio.run(submit_form())

برای حجم بالای همزمانی، trio می‌تواند کارآمدتر باشد اما پیچیدگی کدنویسی افزایش می‌یابد. همیشه از محدودیت‌های سرور و سیاست‌های سایت آگاه باشید.

نکات عملی، امنیت و بهترین‌روش‌ها

  • همیشه از هدر مناسب مثل User-Agent استفاده کنید و توکن‌ها را در محیط اجرا (env vars) نگهدارید.

  • از زمان‌سنجی‌های هوشمند (exponential backoff) و retry برای خطاهای موقتی استفاده کنید؛ از بسته‌هایی مثل tenacity می‌توان بهره برد.

  • برای ارسال فایل‌ها و داده‌های حساس از TLS/HTTPS استفاده کنید و گواهی‌ها را اعتبارسنجی نمایید.

  • برای سایت‌هایی با محدودیت درخواست یا CAPTCHA از پراکسی‌ها، مدیرت نرخ (rate limiting) و سرویس‌های خبره استفاده کنید، اما همیشه سیاست‌های سایت را مراعات کنید.

  • اگر فرم‌ها شامل CSRF هستند، ابتدا صفحه را GET کنید و توکن را استخراج کنید؛ سپس توکن را همراه فرم ارسال کنید.

  • برای اشکال‌زدایی: لاگینگ دقیق، ذخیرهٔ هدرها و پاسخ‌ها و اجرای headful (non-headless) برای مشاهدهٔ رفتار مرورگر مفید است.

جمع‌بندی

انتخاب ابزار وابسته به پیچیدگی فرم و نیازهای پروژه است: برای فرم‌های ساده requests و BeautifulSoup کفایت می‌کنند؛ برای فرم‌های جاوااسکریپتی به Selenium یا Playwright نیاز دارید؛ برای پروژه‌های مقیاس‌پذیر و همزمان httpx و ترکیب با asyncio یا trio مناسب‌اند. همیشه به امنیت، مدیریت خطا و احترام به قوانین سایت توجه کنید تا اسکریپ‌های پایدار و اخلاقی بسازید.

مطالب مرتبط

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