خانه/مقالات/اسکریپینگ با HTTPX: ارسال POST در پایتون
برنامه نویسی
API
برگشت به مقاله‌ها

اسکریپینگ با HTTPX: ارسال POST در پایتون

اسکریپینگ با HTTPX: ارسال POST در پایتون
این مقاله به توسعه‌دهندگان پایتون توضیح می‌دهد چطور با HTTPX درخواست‌های POST بفرستند: ارسال JSON و فرم، تنظیم هدرها، استفاده از Client برای بهبود کارایی، و الگوهای عملی برای retry، timeout و مدیریت خطا. با مثال‌های کد و نکات امنیتی و عملکردی می‌توانید کدهای اسکریپینگ قابل‌اطمینان بسازید.
آسان اسکریپ آسان اسکریپ
1405-05-09

مقدمه

در این راهنما یاد می‌گیرید چگونه با کتابخانهٔ HTTPX در پایتون درخواست‌های POST بفرستید. این مقاله برای توسعه‌دهندهٔ پایتون در سطح متوسط تهیه شده و تمرکز بر روی ارسال JSON، فرم‌داده، تنظیم Content-Type، استفاده از Session/Client، و روش‌های عملی برای خطایابی، امنیت و بهینه‌سازی است. در پایان قادر خواهید بود توابع قابل‌اعتماد برای اسکریپینگ و تعامل با APIها بسازید.

ارسال JSON با HTTPX

روش رایج برای فرستادن داده‌ها به API، ارسال JSON در بدنهٔ درخواست POST است. HTTPX یک پارامتر راحت به نام json دارد که دیکشنری پایتون را به JSON تبدیل کرده و هدر Content-Type: application/json را خودکار اضافه می‌کند.

import httpx

url = 'https://httpbin.org/post'
data = {'key': 'value'}

# ارسال POST با دادهٔ JSON به‌صورت ساده
response = httpx.post(url, json=data)

# دریافت و چاپ JSON پاسخ (در صورت معتبر بودن JSON بازگشتی)
print(response.json())

توضیح: ورودی‌ها: url (رشته)، data (دیکشنری). خروجی: شیء Response از HTTPX. خط‌به‌خط:

  • وارد کردن ماژول httpx.
  • تعریف آدرس و دادهٔ دیکشنری.
  • فراخوانی httpx.post با پارامتر json که باعث می‌شود HTTPX داده را JSON encode کند و هدر مناسب را اضافه کند.
  • استفاده از response.json() برای تبدیل بدنِ پاسخ به آبجکت پایتون (توجه: ممکن است خطا بیفتد اگر پاسخ JSON نباشد).

نکتهٔ عملی: از پارامتر json استفاده کنید تا از double-encoding جلوگیری شود و کار ساده‌تر باشد.

ارسال فرم (Form Data) با HTTPX

برای ارسال داده به‌صورت فرم (application/x-www-form-urlencoded) یا فرم چندبخشی (multipart/form-data)، از پارامتر data و files استفاده کنید.

import httpx

url = 'https://httpbin.org/post'
form = {'field1': 'value1', 'field2': 'value2'}

# ارسال فرم ساده (application/x-www-form-urlencoded)
response = httpx.post(url, data=form)
print(response.text)

# ارسال فایل به‌صورت multipart/form-data
with open('example.txt', 'rb') as f:
    files = {'file': ('example.txt', f, 'text/plain')}
    resp2 = httpx.post(url, files=files)
    print(resp2.status_code)

توضیح: پارامتر data برای فرم‌های ساده، و files برای آپلود فایل‌هاست. HTTPX به‌طور خودکار هدر مناسب را تنظیم می‌کند. اگر فایل بزرگ است، از استریم و گزینهٔ chunked استفاده کنید تا از مصرف زیاد حافظه جلوگیری شود.

تنظیم دستی Content-Type و ارسال بایتی

گاهی لازم است کنترل دقیق‌تری روی هدرها یا نوع داده داشته باشیم؛ مثلاً وقتی می‌خواهیم JSON را با فرمت خاص یا به‌صورت بایت ارسال کنیم. در این حالت می‌توانید داده را خودتان encode کنید و هدر Content-Type را تنظیم کنید.

import httpx
import json

url = 'https://httpbin.org/post'
data = {'key': 'value'}
json_data = json.dumps(data)  # تبدیل دستی به رشتهٔ JSON
headers = {'Content-Type': 'application/json'}

response = httpx.post(url, data=json_data, headers=headers)
print(response.json())

توضیح: اگر از پارامتر json استفاده کنید نیازی به این کار نیست؛ اما در مواردی که باید کنترل کامل بر encoding یا هدرها داشته باشید، این الگو مفید است. مواظب باشید که داده را دوبار JSON کنید (double encoding) یا هدر را ناسازگار تعیین نکنید.

استفاده از Session/Client برای درخواست‌های متعدد

برای ارسال چندین درخواست به یک سرویس، استفاده از httpx.Client() یا httpx.AsyncClient() به جای فراخوانی تابع سطح بالا مزایای زیادی دارد: مدیریت connection pooling، نگهداری کوکی‌ها، و تنظیم هدرها به‌صورت مرکزی.

import httpx

url = 'https://httpbin.org/post'
data = {'key': 'value'}

# استفاده از Client برای چندین درخواست و بهبود کارایی
with httpx.Client() as client:
    client.headers.update({'User-Agent': 'my-scraper/1.0', 'Content-Type': 'application/json'})
    resp = client.post(url, json=data)
    print(resp.json())

توضیح: Client یک جلسهٔ هم‌زمان (sync) ایجاد می‌کند. با به‌کارگیری بلوک with مطمئن می‌شویم منابع (کانکشن‌ها) به‌درستی بسته می‌شوند. برای عملیات غیرهم‌زمان، از httpx.AsyncClient() استفاده کنید که در پایین نمونه‌ای آمده است.

import asyncio
import httpx

async def async_post():
    url = 'https://httpbin.org/post'
    data = {'key': 'value'}

    async with httpx.AsyncClient() as client:
        resp = await client.post(url, json=data)
        print(resp.json())

# اجرا در حلقهٔ رویداد
# asyncio.run(async_post())

الگوی تابع کمکی با retry و timeout

در اسکریپینگ واقعی باید به خطاهای شبکه و محدودیت‌های سرور رسیدگی کنید. الگوی زیر یک تابع ساده با تلاش مجدد (exponential backoff)، تایم‌اوت و مدیریت استثنا را نشان می‌دهد.

import time
import httpx

def post_with_retries(url, json_data, headers=None, attempts=3, timeout=10.0):
    """سعی می‌کند درخواست POST را ارسال کند و در صورت خطا دوبار تلاش می‌کند.

    ورودی‌ها:
    - url: رشتهٔ آدرس
    - json_data: دیکشنری یا ساختار قابل JSON شدن
    - headers: دیکشنری هدرها (اختیاری)
    - attempts: تعداد تلاش‌ها
    - timeout: تایم‌اوت هر درخواست به ثانیه

    خروجی: شیء Response در موفقیت، یا پرتاب استثنا در صورت شکست نهایی
    """
    backoff = 1
    for i in range(attempts):
        try:
            resp = httpx.post(url, json=json_data, headers=headers, timeout=timeout)
            resp.raise_for_status()  # اگر وضعیت HTTP نشان‌دهنده خطا بود استثنا پرتاب می‌کند
            return resp
        except (httpx.RequestError, httpx.HTTPStatusError, httpx.TimeoutException) as exc:
            # لاگ کردن یا چاپ خطا در اینجا مفید است
            print(f"Attempt {i+1} failed: {exc}")
            if i == attempts - 1:
                raise
            time.sleep(backoff)
            backoff *= 2  # exponential backoff

# مثال استفاده
# response = post_with_retries('https://httpbin.org/post', {'k': 'v'})
# print(response.json())

توضیح: این تابع ورودی‌ها و خروجی را مستند کرده و در هر تلاش خطاها را مدیریت می‌کند. از resp.raise_for_status() برای تبدیل کدهای خطا به استثنا استفاده شده و با timeout جلوی انتظار بی‌نهایت گرفته می‌شود.

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

  • تنظیم تایم‌اوت: همیشه از تایم‌اوت استفاده کنید تا نخ‌ها یا پردازش‌ها معلق نمانند.
  • اعتبارسنجی TLS: مقدار پیش‌فرض verify باید True باشد تا TLS بررسی شود؛ فقط در محیط‌های تستی آن را غیرفعال کنید.
  • هدر User-Agent: هدر مناسب تنظیم کنید تا قابل‌ردیابی یا مسدود نشوید؛ برخی سایت‌ها به User-Agent توجه می‌کنند.
  • احترام به قوانین: قبل از اسکریپینگ شرایط سرویس و فایل robots.txt را بررسی کنید.
  • پولینگ و Client: برای تعداد زیاد درخواست‌ها از Client استفاده کنید تا کانکشن‌ها دوباره استفاده شوند و کارایی افزایش یابد.
  • محدودیت نرخ و backoff: از تاخیر بین درخواست‌ها و الگوریتم backoff استفاده کنید تا سرورها را تحت فشار قرار ندهید.
  • عدم لاگینگ اطلاعات حساس: توکن‌ها و اطلاعات حساس را در لاگ ننویسید یا آن‌ها را ماسک کنید.
  • استریم پاسخ‌های بزرگ: برای دانلود فایل‌های بزرگ از پارامتر stream و خواندن بخش‌بخشی استفاده کنید تا مصرف حافظه کنترل شود.

خطایابی و مدیریت استثناء‌ها

برخی استثناهای مهم در HTTPX که باید کنترل شوند:

  • httpx.ConnectError: خطاهای مرتبط با اتصال شبکه.
  • httpx.TimeoutException: زمانی که تایم‌اوت رخ می‌دهد.
  • httpx.HTTPStatusError: زمانی که raise_for_status() فراخوانی شده و پاسخ کد خطا دارد.

هنگام خواندن پاسخ JSON از response.json() از try/except استفاده کنید تا خطاهای پارس شدن را کنترل کنید:

try:
    data = response.json()
except ValueError:
    print('Response is not valid JSON')
    data = None

جمع‌بندی

HTTPX ابزار قدرتمندی برای ارسال POST در اسکریپینگ پایتون است. برای کارهای روزمره از پارامتر json یا data استفاده کنید، هنگامی که نیاز به عملکرد و حالت نگهداری دارید از Client بهره ببرید، و همیشه تایم‌اوت، مدیریت خطا و احترام به محدودیت سرور را در نظر داشته باشید. نمونه‌های ارائه‌شده الگوهای عملی برای تولید کد پایدار در پروژه‌های وب اسکریپینگ هستند.

مطالب مرتبط

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