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

دلایل و راه‌حل‌های بازگشت داده‌های نادرست توسط پروکسی

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

📅۲۱ آذر ۱۴۰۴
```html

پروکسی داده‌های نادرست برمی‌گرداند: دلایل و راه‌حل‌ها

شما پارسر خود را تنظیم کرده‌اید، جمع‌آوری داده‌ها را شروع کرده‌اید، اما نتیجه کار قیمت‌های مربوط به منطقه دیگری، محتوای قدیمی یا حتی صفحه‌ای کاملاً متفاوت است. در اینجا بررسی می‌کنیم که چرا پروکسی ممکن است داده‌های نادرست برگرداند و چگونه می‌توان این مشکل را برطرف کرد.

۱. کشینگ (Caching) در سمت پروکسی

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

نحوه تشخیص مشکل

  • داده‌ها با درخواست‌های مکرر تغییر نمی‌کنند
  • قیمت‌ها یا موجودی کالا با واقعیت مطابقت ندارد
  • سرآیند (Header) Age در پاسخ، مقدار زیادی را نشان می‌دهد

راه‌حل

سرآیندهایی اضافه کنید که کشینگ را ممنوع می‌کنند:

import requests

headers = {
    'Cache-Control': 'no-cache, no-store, must-revalidate',
    'Pragma': 'no-cache',
    'Expires': '0'
}

response = requests.get(
    'https://example.com/prices',
    proxies={'http': proxy, 'https': proxy},
    headers=headers
)

اگر ارائه‌دهنده همچنان کش می‌کند، یک پارامتر تصادفی به URL اضافه کنید:

import time

url = f'https://example.com/prices?_nocache={int(time.time())}'

۲. عدم تطابق موقعیت جغرافیایی (Geolocation)

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

دلایل عدم تطابق موقعیت جغرافیایی

دلیل توضیحات
پایگاه‌های داده GeoIP قدیمی IP اخیراً به منطقه دیگری منتقل شده، اما پایگاه‌های داده هنوز به‌روز نشده‌اند
سایت از پایگاه داده خود استفاده می‌کند وب‌سایت هدف، موقعیت جغرافیایی را متفاوت از ارائه‌دهنده پروکسی تشخیص می‌دهد
کوکی‌های سشن قبلی سایت منطقه شما را از بازدید قبلی به خاطر سپرده است
Accept-Language سرآیند زبان با موقعیت جغرافیایی پروکسی مطابقت ندارد

راه‌حل

تمام پارامترهای درخواست را با موقعیت جغرافیایی مورد نظر همگام‌سازی کنید:

# برای اسکرپ کردن سایت آلمانی
headers = {
    'Accept-Language': 'de-DE,de;q=0.9,en;q=0.8',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
}

# سشن پاک بدون کوکی
session = requests.Session()
session.cookies.clear()

response = session.get(
    'https://example.de/preise',
    proxies={'http': german_proxy, 'https': german_proxy},
    headers=headers
)

قبل از اسکرپ کردن، موقعیت جغرافیایی واقعی IP را بررسی کنید:

def check_proxy_geo(proxy):
    response = requests.get(
        'http://ip-api.com/json/',
        proxies={'http': proxy, 'https': proxy},
        timeout=10
    )
    data = response.json()
    return data.get('country'), data.get('city')

۳. مشکلات مربوط به چرخش IP (IP Rotation)

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

علائم رایج

  • صفحه‌بندی (Pagination) تکراری برمی‌گرداند یا عناصر را رد می‌کند
  • سبد خرید بین درخواست‌ها پاک می‌شود
  • احراز هویت در میانه سشن از بین می‌رود
  • تست‌های A/B سایت نسخه‌های متفاوتی از صفحات را نشان می‌دهند

راه‌حل: سشن‌های چسبنده (Sticky Sessions)

بیشتر ارائه‌دهندگان پروکسی از سشن‌های «چسبنده» پشتیبانی می‌کنند — یعنی IP برای مدت زمان مشخصی ثابت می‌ماند. این معمولاً از طریق یک پارامتر در رشته اتصال تنظیم می‌شود:

# مثال فرمت با شناسه سشن (نحوه‌ی استفاده بستگی به ارائه‌دهنده دارد)
proxy = 'http://user-session-abc123:pass@gate.provider.com:7777'

# تمام درخواست‌ها با یک شناسه سشن از یک IP عبور می‌کنند
for page in range(1, 10):
    response = requests.get(
        f'https://example.com/catalog?page={page}',
        proxies={'http': proxy, 'https': proxy}
    )

نکته مهم: سشن چسبنده معمولاً ۱ تا ۳۰ دقیقه عمر می‌کند. جمع‌آوری داده‌ها را طوری برنامه‌ریزی کنید که درخواست‌های مرتبط در این بازه زمانی بگنجند.

۴. نقض سشن‌ها (Sessions) و کوکی‌ها (Cookies)

سایت‌های مدرن به شدت از کوکی‌ها برای شخصی‌سازی استفاده می‌کنند. اگر پارسر شما نتواند آن‌ها را به درستی مدیریت کند، داده‌های نادرست دریافت خواهید کرد — یا اصلاً مسدود خواهید شد.

خطاهای رایج

  1. نادیده گرفتن Set-Cookie — سایت نمی‌تواند سشن را دنبال کند
  2. استفاده مجدد از کوکی با IP متفاوت — رفتار مشکوک
  3. عدم وجود درخواست اولیه — رفتن مستقیم به صفحه داخلی بدون «ورود» از صفحه اصلی

رویکرد صحیح

import requests

def create_browser_session(proxy):
    session = requests.Session()
    session.proxies = {'http': proxy, 'https': proxy}
    
    # شبیه‌سازی بازدید اول — دریافت کوکی‌ها
    session.get('https://example.com/', headers={
        'User-Agent': 'Mozilla/5.0...',
        'Accept': 'text/html,application/xhtml+xml...',
        'Accept-Language': 'en-US,en;q=0.9'
    })
    
    # اکنون می‌توان با یک سشن معتبر اسکرپ کرد
    return session

session = create_browser_session(proxy)
data = session.get('https://example.com/api/prices').json()

۵. خطاهای کدگذاری و فشرده‌سازی

گاهی اوقات داده‌ها صحیح هستند، اما به دلیل مشکلات کدگذاری یا فشرده‌سازی به درستی نمایش داده نمی‌شوند. این امر به ویژه هنگام کار با زبان‌های سیریلیک (مانند فارسی و روسی) و زبان‌های آسیایی بسیار رایج است.

علائم

  • نمایش کاراکترهای نامفهوم به جای متن: Цена به جای «Цена»
  • پاسخ خالی هنگام فعال بودن gzip
  • داده‌های باینری نامفهوم به جای HTML

راه‌حل

import requests

response = requests.get(url, proxies=proxies)

# روش ۱: تشخیص خودکار کدگذاری
response.encoding = response.apparent_encoding
text = response.text

# روش ۲: تحمیل کدگذاری
text = response.content.decode('utf-8')

# روش ۳: غیرفعال کردن فشرده‌سازی (اگر پروکسی gzip را خراب می‌کند)
headers = {'Accept-Encoding': 'identity'}
response = requests.get(url, proxies=proxies, headers=headers)

۶. مسدودسازی‌های پنهان و کپچاها

همه مسدودسازی‌ها آشکار نیستند. سایت ممکن است کد وضعیت HTTP 200 را برگرداند، اما به جای داده‌های واقعی، یک صفحه جایگزین، کش قدیمی یا صفحه‌ای حاوی کپچا را در قالب HTML معمولی تزریق کند.

نشانه‌های مسدودسازی پنهان

  • اندازه پاسخ مشکوک به نظر کوچک یا یکسان برای صفحات مختلف است
  • کلمات captcha، challenge، blocked، access denied در HTML وجود دارد
  • عناصر مورد انتظار (قیمت‌ها، توضیحات، دکمه‌ها) وجود ندارند
  • تغییر مسیر (Redirect) جاوا اسکریپت به صفحه دیگر

اعتبارسنجی پاسخ

def is_valid_response(response, expected_markers):
    """بررسی می‌کند که آیا پاسخ حاوی داده‌های واقعی است"""
    
    text = response.text.lower()
    
    # بررسی مسدودسازی
    block_signals = ['captcha', 'blocked', 'access denied', 
                     'rate limit', 'try again later']
    for signal in block_signals:
        if signal in text:
            return False, f'Blocked: {signal}'
    
    # بررسی وجود داده‌های مورد انتظار
    for marker in expected_markers:
        if marker.lower() not in text:
            return False, f'Missing: {marker}'
    
    # بررسی اندازه (خیلی کوچک = صفحه جایگزین)
    if len(response.content) < 5000:
        return False, 'Response too small'
    
    return True, 'OK'

# استفاده
valid, reason = is_valid_response(response, ['price', 'add to cart'])
if not valid:
    print(f'Invalid response: {reason}')
    # پروکسی را عوض کنید، صبر کنید، تکرار کنید

برای سایت‌هایی با حفاظت جدی در برابر ربات‌ها، پروکسی‌های موبایل سطح اعتماد بالاتری نسبت به پروکسی‌های دیتاسنتر ارائه می‌دهند.

۷. عیب‌یابی گام به گام

هنگامی که پروکسی داده‌های نادرست برمی‌گرداند، از این الگوریتم برای یافتن دلیل استفاده کنید:

گام ۱: مشکل را ایزوله کنید

# مقایسه پاسخ‌ها: بدون پروکسی در مقابل با پروکسی
def compare_responses(url, proxy):
    direct = requests.get(url)
    proxied = requests.get(url, proxies={'http': proxy, 'https': proxy})
    
    print(f'Direct:  {len(direct.content)} bytes, status {direct.status_code}')
    print(f'Proxied: {len(proxied.content)} bytes, status {proxied.status_code}')
    
    # هر دو پاسخ را برای مقایسه ذخیره کنید
    with open('direct.html', 'w') as f:
        f.write(direct.text)
    with open('proxied.html', 'w') as f:
        f.write(proxied.text)

گام ۲: سرآیندهای پاسخ را بررسی کنید

response = requests.get(url, proxies=proxies)

# سرآیندهای کلیدی برای عیب‌یابی
important_headers = ['content-type', 'content-encoding', 
                     'cache-control', 'age', 'x-cache', 
                     'cf-ray', 'server']

for header in important_headers:
    value = response.headers.get(header, 'not set')
    print(f'{header}: {value}')

گام ۳: چک‌لیست بررسی‌ها

بررسی دستور/روش
IP واقعی پروکسی curl -x proxy:port ifconfig.me
موقعیت جغرافیایی IP ip-api.com/json
کشینگ سرآیندهای Age, X-Cache
مسدودسازی جستجوی «captcha»، «blocked» در HTML
کدگذاری Content-Type charset

گام ۴: اسکریپت کامل عیب‌یابی

import requests
import json

def diagnose_proxy(proxy, target_url):
    report = {}
    
    # ۱. بررسی عملکرد پروکسی
    try:
        r = requests.get('http://httpbin.org/ip', 
                        proxies={'http': proxy, 'https': proxy},
                        timeout=15)
        report['proxy_ip'] = r.json().get('origin')
        report['proxy_works'] = True
    except Exception as e:
        report['proxy_works'] = False
        report['error'] = str(e)
        return report
    
    # ۲. موقعیت جغرافیایی
    r = requests.get('http://ip-api.com/json/',
                    proxies={'http': proxy, 'https': proxy})
    geo = r.json()
    report['country'] = geo.get('country')
    report['city'] = geo.get('city')
    
    # ۳. درخواست به سایت هدف
    r = requests.get(target_url,
                    proxies={'http': proxy, 'https': proxy},
                    timeout=30)
    report['status_code'] = r.status_code
    report['content_length'] = len(r.content)
    report['cached'] = 'age' in r.headers or 'x-cache' in r.headers
    
    # ۴. بررسی مسدودسازی
    block_words = ['captcha', 'blocked', 'denied', 'cloudflare']
    report['possibly_blocked'] = any(w in r.text.lower() for w in block_words)
    
    return report

# استفاده
result = diagnose_proxy('http://user:pass@proxy:port', 'https://target-site.com')
print(json.dumps(result, indent=2))

نتیجه‌گیری

داده‌های نادرست دریافتی از پروکسی تقریباً همیشه قابل حل است. در اکثر موارد، دلیل آن کشینگ، عدم تطابق موقعیت جغرافیایی یا کارکرد نادرست با سشن‌ها است. از اسکریپت عیب‌یابی این مقاله برای یافتن سریع منبع مشکل استفاده کنید.

برای وظایفی که دقت موقعیت جغرافیایی و درصد پایین مسدودسازی در آن‌ها حیاتی است، پروکسی‌های رزیدنتال با پشتیبانی از سشن‌های چسبنده (sticky sessions) بهترین گزینه هستند — جزئیات بیشتر در proxycove.com.

```