بازگشت به وبلاگ

چگونه پروکسی را در درخواست‌های پایتون برای پارسینگ، API و اتوماسیون تنظیم کنیم: راهنمای کامل با مثال‌های کد

راهنمای کامل اتصال پروکسی در کتابخانه Python requests - از تنظیمات پایه تا چرخش IP و دور زدن مسدودیت‌ها در هنگام پارس کردن و اتوماسیون.

📅۱۴ فروردین ۱۴۰۵
```html

اگر اسکریپت شما در پایتون خطای 403، CAPTCHA یا مسدودیت IP دریافت می‌کند — به این معنی است که وب‌سایت هدف شما را شناسایی کرده است. اتصال پروکسی به کتابخانه requests این مشکل را حل می‌کند: شما IP خود را تغییر می‌دهید، محدودیت‌های جغرافیایی را دور می‌زنید و بار را بین چندین آدرس توزیع می‌کنید. در این راهنما — همه چیز از اتصال پایه تا چرخش پیشرفته با مثال‌های واقعی کد.

چرا پروکسی در اسکریپت‌های پایتون ضروری است

اکثر وب‌سایت‌ها و APIها IP آدرس‌های درخواست‌های ورودی را ردیابی می‌کنند. اگر یک آدرس بیش از 100 درخواست در دقیقه ارسال کند — آن را مسدود می‌کنند. این یک روش استاندارد برای محافظت در برابر ربات‌ها است که توسط Wildberries، Ozon، Avito، Google، Instagram و صدها پلتفرم دیگر استفاده می‌شود. پروکسی به شما اجازه می‌دهد درخواست‌ها را از طریق یک سرور میانی با IP متفاوت ارسال کنید و شما را برای سیستم‌های حفاظتی نامرئی می‌کند.

در اینجا وظایف اصلی که در آن پروکسی در پایتون به شدت ضروری است آورده شده است:

  • پارسینگ بازارها — جمع‌آوری قیمت‌ها از Wildberries، Ozon، Yandex.Market بدون مسدودیت IP
  • نظارت بر رقبا — ارسال درخواست‌های منظم به وب‌سایت‌های رقبا هر 5–15 دقیقه
  • کار با API با محدودیت‌ها — توزیع درخواست‌ها بین چندین IP برای عدم تجاوز به محدودیت نرخ
  • تست جغرافیایی — بررسی اینکه وب‌سایت از کشورهای مختلف و مناطق چگونه به نظر می‌رسد
  • اتوماسیون فرم‌ها و ثبت‌نام‌ها — ایجاد حساب‌ها یا پر کردن فرم‌ها از IPهای مختلف
  • نظارت SEO — دریافت موقعیت‌ها از مناطق مختلف روسیه و کشورهای دیگر

بدون پروکسی حتی یک پارسر خوب نوشته شده نیز پس از چند ساعت کار به مسدودیت برخورد می‌کند. با چرخش IP به درستی تنظیم شده، همان اسکریپت هفته‌ها بدون توقف کار می‌کند.

تنظیمات پایه پروکسی در درخواست‌ها

کتابخانه requests به طور بومی از پروکسی پشتیبانی می‌کند — نیازی به بسته‌های اضافی نیست. پروکسی از طریق دیکشنری proxies به پارامترهای درخواست منتقل می‌شود.

ساده‌ترین مثال — پروکسی HTTP برای یک درخواست:

import requests

proxies = {
    "http": "http://123.45.67.89:8080",
    "https": "http://123.45.67.89:8080",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
# {'origin': '123.45.67.89'}
  

توجه داشته باشید: در دیکشنری proxies باید هر دو کلید — http و https را مشخص کنید. اگر فقط یکی را مشخص کنید، درخواست‌ها با پروتکل دوم به طور مستقیم بدون پروکسی ارسال می‌شوند. این یک اشتباه رایج در بین مبتدیان است که باعث می‌شود IP واقعی همچنان نشت کند.

برای اطمینان از اینکه پروکسی کار می‌کند، از سرویس httpbin.org/ip استفاده کنید — این سرویس IP آدرسی را که درخواست از آن آمده است برمی‌گرداند. اگر در پاسخ IP پروکسی سرور را مشاهده کنید و نه IP خودتان — همه چیز به درستی تنظیم شده است.

پروکسی‌های HTTP، HTTPS و SOCKS5: تفاوت‌ها و مثال‌های کد

پروکسی‌ها انواع مختلفی دارند و هر کدام برای وظایف خاصی مناسب هستند. در زمینه درخواست‌های پایتون، مهم است که تفاوت بین سه پروتکل اصلی را درک کنید:

نوع پروتکل در URL سرعت پشتیبانی از UDP بهترین سناریو
HTTP http:// بالا خیر پارسینگ وب‌سایت‌های HTTP
HTTPS https:// بالا خیر پارسینگ وب‌سایت‌های امن
SOCKS5 socks5:// متوسط بله ناشناس کامل، هر پروتکلی

برای کار با SOCKS5 در پایتون باید یک بسته اضافی نصب کنید:

pip install requests[socks]
# یا جداگانه:
pip install PySocks
  

پس از نصب، اتصال پروکسی SOCKS5 به این صورت است:

import requests

# پروکسی SOCKS5
proxies = {
    "http": "socks5://123.45.67.89:1080",
    "https": "socks5://123.45.67.89:1080",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
  

SOCKS5 — پروتکل ترجیحی برای وظایفی است که ناشناس بودن اهمیت دارد. بر خلاف پروکسی HTTP، SOCKS5 هدرهای X-Forwarded-For را اضافه نمی‌کند که می‌تواند IP واقعی شما را فاش کند.

پروکسی با احراز هویت با نام کاربری و رمز عبور

اکثر خدمات پروکسی پولی از احراز هویت با نام کاربری و رمز عبور استفاده می‌کنند. این یک روش استاندارد است — بدون احراز هویت، پروکسی درخواست شما را نمی‌پذیرد. در کتابخانه requests، اطلاعات احراز هویت مستقیماً در URL پروکسی منتقل می‌شود.

import requests

# فرمت: پروتکل://نام کاربری:رمز عبور@میزبان:پورت
proxy_url = "http://myuser:[email protected]:8080"

proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.status_code)
print(response.json())
  

اگر در رمز عبور یا نام کاربری نمادهای خاصی وجود داشته باشد (مانند @، #، %)، باید آن‌ها را URL-کدگذاری کنید. برای این کار از ماژول urllib.parse استفاده کنید:

import requests
from urllib.parse import quote

username = "myuser"
password = "p@ss#word!"  # نمادهای خاص

# URL-کدگذاری نام کاربری و رمز عبور
encoded_user = quote(username, safe="")
encoded_pass = quote(password, safe="")

proxy_url = f"http://{encoded_user}:{encoded_pass}@123.45.67.89:8080"

proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
  

💡 نکته امنیتی

هرگز نام کاربری و رمز عبور را مستقیماً در کد اسکریپت هاردکد نکنید. از متغیرهای محیطی یا فایل .env با کتابخانه python-dotenv استفاده کنید. این کار به شما کمک می‌کند تا از نشت تصادفی اطلاعات کاربری هنگام انتشار کد در GitHub جلوگیری کنید.

چرخش پروکسی: تغییر خودکار IP برای پارسینگ

یک پروکسی — هنوز یک IP آدرس است که می‌تواند مسدود شود. محافظت واقعی در برابر مسدودیت‌ها — چرخش است: هر درخواست (یا هر N درخواست) با یک IP جدید ارسال می‌شود. در زیر — چندین رویکرد برای پیاده‌سازی چرخش آورده شده است.

روش 1: انتخاب تصادفی از لیست

import requests
import random

# لیست پروکسی‌ها
proxy_list = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]

def get_random_proxy():
    proxy = random.choice(proxy_list)
    return {"http": proxy, "https": proxy}

# پارسینگ 10 صفحه با چرخش IP
urls = [f"https://example.com/page/{i}" for i in range(1, 11)]

for url in urls:
    proxies = get_random_proxy()
    try:
        response = requests.get(url, proxies=proxies, timeout=10)
        print(f"URL: {url} | IP: {proxies['http'].split('@')[1]} | Status: {response.status_code}")
    except requests.RequestException as e:
        print(f"خطا: {e}")
  

روش 2: چرخش دایره‌ای از طریق itertools

import requests
import itertools

proxy_list = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]

# ایجاد یک حلقه بی‌نهایت از لیست پروکسی‌ها
proxy_cycle = itertools.cycle(proxy_list)

def get_next_proxy():
    proxy = next(proxy_cycle)
    return {"http": proxy, "https": proxy}

# هر درخواست از پروکسی بعدی به صورت دایره‌ای استفاده می‌کند
for i in range(9):
    proxies = get_next_proxy()
    response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
    print(f"درخواست {i+1}: {response.json()['origin']}")
  

برای وظایف صنعتی با هزاران درخواست در ساعت، توصیه می‌شود از پروکسی‌های مسکونی با چرخش خودکار داخلی استفاده کنید — ارائه‌دهنده به طور خودکار IP را برای هر درخواست از طریق یک نقطه پایانی واحد تغییر می‌دهد و شما نیازی به مدیریت لیست آدرس‌ها به صورت دستی ندارید.

پروکسی از طریق Session: اتصالات دائمی و کوکی‌ها

زمانی که نیاز به ارسال چندین درخواست در یک جلسه دارید (به عنوان مثال، ورود و سپس ارسال درخواست‌ها)، از شیء requests.Session() استفاده کنید. این شیء کوکی‌ها، هدرها و تنظیمات پروکسی را بین درخواست‌ها ذخیره می‌کند — نیازی به ارسال پروکسی در هر فراخوانی به صورت جداگانه نیست.

import requests

# ایجاد یک جلسه با پروکسی
session = requests.Session()
session.proxies = {
    "http": "http://user:[email protected]:8080",
    "https": "http://user:[email protected]:8080",
}

# افزودن هدرها برای شبیه‌سازی مرورگر
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Accept-Language": "ru-RU,ru;q=0.9",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
})

# مرحله 1: احراز هویت
login_data = {"username": "myuser", "password": "mypass"}
session.post("https://example.com/login", data=login_data)

# مرحله 2: درخواست‌ها با کوکی‌ها و از طریق پروکسی
response = session.get("https://example.com/dashboard")
print(response.status_code)

# مرحله 3: بستن جلسه
session.close()
  

استفاده از Session همچنین از نظر عملکرد مؤثرتر است: اتصال TCP دوباره استفاده می‌شود و برای هر درخواست دوباره باز نمی‌شود. هنگام پارسینگ 1000+ صفحه، این باعث افزایش قابل توجهی در سرعت می‌شود.

مدیریت خطاها، زمان‌های تایم‌اوت و تکرارهای خودکار

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

import requests
import random
import time

proxy_list = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]

def fetch_with_retry(url, max_retries=3, timeout=10):
    """
    درخواست را با تغییر خودکار پروکسی در صورت بروز خطا انجام می‌دهد.
    شیء Response را برمی‌گرداند یا None را در صورت اتمام تلاش‌ها.
    """
    available_proxies = proxy_list.copy()
    random.shuffle(available_proxies)

    for attempt, proxy_url in enumerate(available_proxies[:max_retries], 1):
        proxies = {"http": proxy_url, "https": proxy_url}
        try:
            response = requests.get(
                url,
                proxies=proxies,
                timeout=timeout,
                headers={"User-Agent": "Mozilla/5.0"}
            )
            response.raise_for_status()  # در صورت 4xx/5xx استثنا پرتاب می‌کند
            print(f"✓ موفقیت در تلاش {attempt}")
            return response

        except requests.exceptions.ProxyError:
            print(f"✗ تلاش {attempt}: پروکسی در دسترس نیست — {proxy_url}")
        except requests.exceptions.Timeout:
            print(f"✗ تلاش {attempt}: زمان تایم‌اوت — {proxy_url}")
        except requests.exceptions.HTTPError as e:
            print(f"✗ تلاش {attempt}: خطای HTTP {e.response.status_code}")
            if e.response.status_code == 403:
                print("  → مسدودیت دریافت شد، پروکسی بعدی را امتحان می‌کنیم...")
        except requests.exceptions.RequestException as e:
            print(f"✗ تلاش {attempt}: خطای عمومی — {e}")

        time.sleep(1)  # وقفه بین تلاش‌ها

    print(f"✗ همه {max_retries} تلاش‌ها برای {url} به پایان رسید")
    return None

# استفاده
result = fetch_with_retry("https://httpbin.org/ip")
if result:
    print(result.json())
  

به raise_for_status() توجه داشته باشید — این متد به طور خودکار در صورت وضعیت‌های HTTP 4xx و 5xx استثنا پرتاب می‌کند. بدون آن، اسکریپت حتی پاسخ با کد 403 (مسدودیت) یا 429 (تجاوز به محدودیت درخواست‌ها) را نیز موفقیت‌آمیز می‌داند.

پروکسی از طریق متغیرهای محیطی: ذخیره امن داده‌ها

کتابخانه requests به طور خودکار متغیرهای محیطی HTTP_PROXY و HTTPS_PROXY را می‌خواند. این کار به شما اجازه می‌دهد تا اطلاعات کاربری را در کد ذخیره نکنید و به راحتی بین پروکسی‌ها بدون تغییر اسکریپت سوئیچ کنید.

تنظیم متغیرها در ترمینال (Linux/macOS):

export HTTP_PROXY="http://user:[email protected]:8080"
export HTTPS_PROXY="http://user:[email protected]:8080"
export NO_PROXY="localhost,127.0.0.1"
  

یا از طریق فایل .env با کتابخانه python-dotenv:

# فایل .env (به .gitignore اضافه کنید!)
HTTP_PROXY=http://user:[email protected]:8080
HTTPS_PROXY=http://user:[email protected]:8080
  
# اسکریپت پایتون
from dotenv import load_dotenv
import requests
import os

load_dotenv()  # بارگذاری متغیرها از .env

# requests به طور خودکار از HTTP_PROXY و HTTPS_PROXY استفاده می‌کند
response = requests.get("https://httpbin.org/ip")
print(response.json())

# یا به طور صریح از متغیرهای محیطی:
proxies = {
    "http": os.getenv("HTTP_PROXY"),
    "https": os.getenv("HTTPS_PROXY"),
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
  

⚠️ مهم: متغیر NO_PROXY

متغیر NO_PROXY به شما اجازه می‌دهد تا آدرس‌های خاصی را از پروکسی‌کردن مستثنی کنید. حتماً localhost و 127.0.0.1 را به آن اضافه کنید تا درخواست‌های محلی از طریق پروکسی نروند.

سناریوهای واقعی: پارسینگ بازارها، کار با API و اتوماسیون

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

سناریو 1: پارسینگ قیمت‌ها از بازار

هنگام نظارت بر قیمت‌ها در Wildberries یا Ozon، مهم است که رفتار یک کاربر واقعی را شبیه‌سازی کنید: هدرهای مرورگر صحیح را ارسال کنید، بین درخواست‌ها تأخیر اضافه کنید و IP را بچرخانید. برای این کار، پروکسی‌های دیتاسنتر گزینه‌های خوبی هستند — آن‌ها سریع و ارزان هستند و هنگام کار با حجم‌های بزرگ داده‌ها مناسب‌اند.

import requests
import time
import random

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept": "application/json, text/plain, */*",
    "Accept-Language": "ru-RU,ru;q=0.9",
    "Referer": "https://www.wildberries.ru/",
}

PROXIES = [
    {"http": "http://user:[email protected]:8080",
     "https": "http://user:[email protected]:8080"},
    {"http": "http://user:[email protected]:8080",
     "https": "http://user:[email protected]:8080"},
]

def get_product_price(article_id: int) -> dict:
    """قیمت محصول را بر اساس آرتیکل از Wildberries دریافت می‌کند."""
    url = f"https://card.wb.ru/cards/v1/detail?appType=1&curr=rub&nm={article_id}"
    proxies = random.choice(PROXIES)

    try:
        resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
        resp.raise_for_status()
        data = resp.json()
        product = data["data"]["products"][0]
        return {
            "id": product["id"],
            "name": product["name"],
            "price": product["salePriceU"] / 100,  # قیمت به روبل
        }
    except (requests.RequestException, KeyError, IndexError) as e:
        return {"error": str(e)}

# پارسینگ چندین آرتیکل با تأخیر
articles = [12345678, 87654321, 11223344]
for article in articles:
    result = get_product_price(article)
    print(result)
    time.sleep(random.uniform(1.5, 3.0))  # تأخیر تصادفی 1.5-3 ثانیه
  

سناریو 2: کار با API از طریق پروکسی

برخی APIها تعداد درخواست‌ها از یک IP را محدود می‌کنند (محدودیت نرخ). توزیع درخواست‌ها بین چندین پروکسی این محدودیت را دور می‌زند:

import requests
import itertools
from typing import Optional

class ProxyAPIClient:
    """کلاینتی برای کار با API از طریق چرخش پروکسی."""

    def __init__(self, api_key: str, proxy_list: list):
        self.api_key = api_key
        self.proxy_cycle = itertools.cycle(proxy_list)
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        })

    def _get_proxy(self) -> dict:
        proxy = next(self.proxy_cycle)
        return {"http": proxy, "https": proxy}

    def get(self, url: str, **kwargs) -> Optional[dict]:
        proxies = self._get_proxy()
        try:
            resp = self.session.get(url, proxies=proxies, timeout=10, **kwargs)
            resp.raise_for_status()
            return resp.json()
        except requests.RequestException as e:
            print(f"درخواست API ناموفق بود: {e}")
            return None

# استفاده
proxy_list = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]

client = ProxyAPIClient(api_key="your_api_key", proxy_list=proxy_list)
data = client.get("https://api.example.com/products")
  

سناریو 3: تست جغرافیایی

بازاریاب‌ها و متخصصان SEO اغلب بررسی می‌کنند که وب‌سایت از مناطق مختلف چگونه به نظر می‌رسد. با استفاده از پروکسی‌های موجود در مکان‌های مورد نظر می‌توان این فرایند را خودکار کرد:

import requests

# پروکسی از مناطق مختلف
regional_proxies = {
    "مسکو":        "http://user:[email protected]:8080",
    "سن‌پترزبورگ": "http://user:[email protected]:8080",
    "نووسیبیرسک":   "http://user:[email protected]:8080",
    "آمریکا":           "http://user:[email protected]:8080",
}

url = "https://example.com/prices"

for region, proxy_url in regional_proxies.items():
    proxies = {"http": proxy_url, "https": proxy_url}
    try:
        resp = requests.get(url, proxies=proxies, timeout=15)
        print(f"[{region}] وضعیت: {resp.status_code} | "
              f"اندازه: {len(resp.content)} بایت")
    except requests.RequestException as e:
        print(f"[{region}] خطا: {e}")
  

کدام نوع پروکسی را برای کار خود انتخاب کنیم

انتخاب نوع پروکسی به طور مستقیم بر موفقیت پروژه شما تأثیر می‌گذارد. پروکسی دیتاسنتر ارزان ممکن است برای برخی وظایف به خوبی کار کند و برای دیگران کاملاً ناموفق باشد. در اینجا یک راهنمای عملی برای انتخاب آورده شده است:

وظیفه نوع پروکسی چرا
پارسینگ بازارها (Wildberries، Ozon) مسکونی به عنوان کاربران عادی به نظر می‌رسند، کمتر مسدود می‌شوند
پارسینگ داده‌های عمومی، اخبار دیتاسنترها سریع، ارزان، به اندازه کافی ناشناس
کار با API فیسبوک، اینستاگرام موبایل شبکه‌های اجتماعی به IPهای موبایل بیشتر اعتماد دارند
تست جغرافیایی مسکونی با جغرافیای هدف جغرافیای دقیق، IPهای واقعی از منطقه مورد نظر
پارسینگ با بار بالا (10k+ درخواست در ساعت) دیتاسنترها (پول) سرعت و هزینه در حجم‌های بالا
احراز هویت و کار با حساب‌ها مسکونی یا موبایل کمتر تحریک‌کننده سیستم‌های ضد تقلب

برای وظایفی که حداکثر قابلیت اطمینان و حداقل خطر مسدودیت در کار با وب‌سایت‌های امن اهمیت دارد، توسعه‌دهندگان بیشتر از پروکسی‌های موبایل استفاده می‌کنند — آن‌ها از IP آدرس‌های واقعی اپراتورهای موبایل (MTS، Beeline، MegaFon) استفاده می‌کنند که به ندرت در لیست‌های مسدود قرار می‌گیرند.

چک‌لیست بررسی پروکسی قبل از استفاده

  • ✅ IP را از طریق httpbin.org/ip بررسی کنید — آیا آدرس واقعی شما قابل مشاهده است؟
  • ✅ سرعت را بررسی کنید — زمان پاسخ نباید بیش از 2-3 ثانیه باشد
  • ✅ اطمینان حاصل کنید که پروکسی در لیست‌های مسدود نیست از طریق blocklist.de یا ipqualityscore.com
  • ✅ جغرافیای IP را از طریق ipinfo.io بررسی کنید — آیا با منطقه مورد انتظار مطابقت دارد؟
  • ✅ قبل از اجرای اسکریپت کامل، در وب‌سایت هدف با یک درخواست آزمایش کنید
  • ✅ اطمینان حاصل کنید که ترافیک HTTPS نیز از طریق پروکسی می‌رود (هر دو کلید در دیکشنری)

نتیجه‌گیری

تنظیم پروکسی در درخواست‌های پایتون کار سختی نیست، اما نیاز به توجه به جزئیات دارد. اصول اصلی که باید به خاطر بسپارید: همیشه هر دو کلید (http و https) را در دیکشنری پروکسی مشخص کنید، از Session برای سناریوهای چند مرحله‌ای استفاده کنید، حتماً خطاها و زمان‌های تایم‌اوت را مدیریت کنید و اطلاعات کاربری را در متغیرهای محیطی ذخیره کنید، نه در کد.

برای پارسینگ صنعتی با هزاران درخواست در روز، یک لیست دستی پروکسی کافی نیست — نیاز به چرخش است. اگر شما در حال پارسینگ بازارهای امن مانند Wildberries یا Ozon هستید، با شبکه‌های اجتماعی کار می‌کنید یا تست جغرافیایی انجام می‌دهید، توصیه می‌کنیم از پروکسی‌های مسکونی استفاده کنید — آن‌ها سطح بالایی از اعتماد را از طرف سیستم‌های ضد ربات فراهم می‌کنند و چرخش خودکار IP را از طریق یک نقطه پایانی واحد پشتیبانی می‌کنند که کد اسکریپت شما را به طور قابل توجهی ساده می‌کند.

```