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

چرا پارسر منطقه نادرست سایت را نشان می‌دهد: Accept-Language، ارز و کوکی‌های جغرافیایی

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

📅۳۰ مهر ۱۴۰۵

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

علائم: چگونه بفهمیم که پارسر نسخه نادرست را می‌بیند

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

  • زبان محتوا مطابقت ندارد — شما انتظار متن روسی را دارید، اما نسخه انگلیسی یا اوکراینی صفحه می‌آید.
  • ارز و قیمت‌ها متفاوت هستند — سایت قیمت را به USD یا EUR نشان می‌دهد، در حالی که در مرورگر از روسیه شما روبل می‌بینید، یا اعداد به سادگی متفاوت هستند (قیمت‌گذاری منطقه‌ای).
  • فهرست کالاها/محتوا متفاوت است — در دسترس بودن کالاها، تخفیف‌ها، ویترین به طور کامل به منطقه‌ای که سایت «دید» شما بستگی دارد.

دلیل تقریباً همیشه یکی از سه مورد (یا ترکیبی از آن‌ها) است: هدر Accept-Language, کوکی‌های گم‌شده یا نادرست، یا جغرافیایی IP-آدرس که از آن درخواست ارسال می‌شود. هر یک را به ترتیب بررسی می‌کنیم.

Accept-Language: چگونه سرور زبان پاسخ را انتخاب می‌کند

مرورگر در هر درخواست هدر Accept-Language را ارسال می‌کند، که در آن زبان‌های مورد علاقه کاربر با وزن‌ها (مقادیر q) فهرست شده‌اند. مثالی از آنچه که واقعاً Chrome با محلی روسی ارسال می‌کند:

Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7

کتابخانه‌های پارسینگ (requests، httpx، scrapy) به طور پیش‌فرض یا اصلاً این هدر را ارسال نمی‌کنند، یا چیزی شبیه به */* ارسال می‌کنند. در این صورت، سرور یا زبان پیش‌فرض را ارائه می‌دهد (معمولاً انگلیسی)، یا زبان را بر اساس IP تعیین می‌کند — و در اینجا بخش جغرافیایی وارد می‌شود که در زیر توضیح داده شده است.

نکته مهم: بسیاری از سایت‌ها از ترکیب «Accept-Language + جغرافیایی IP» استفاده می‌کنند، نه فقط یکی. یعنی حتی اگر شما هدر زبان صحیح را ارسال کنید، اما درخواست از IP کشور دیگری باشد، سایت ممکن است شما را به نسخه انگلیسی یا محلی برای آن منطقه که واقعاً درخواست از آنجا آمده است، منتقل کند.

ارز و محلی: سایت از کجا قیمت‌ها را می‌گیرد

ارز تقریباً هرگز به طور مستقیم با هدر Accept-Language تعیین نمی‌شود. معمولاً یکی از سه مکانیزم (یا ترکیبی از آن‌ها) استفاده می‌شود:

  1. پایگاه GeoIP — سرور نگاه می‌کند که IP از کدام کشور آمده است و به طور خودکار ارز و محلی مناسب را در اولین بازدید قرار می‌دهد.
  2. کوکی با منطقه انتخاب شده قبلی — اگر کاربر قبلاً به صورت دستی کشور و ارز را انتخاب کرده باشد، سایت این را در کوکی ذخیره می‌کند و دیگر IP را بررسی نمی‌کند.
  3. پارامتر در URL یا زیر دامنه — برخی از سایت‌ها از نشانگرهای صریح مانند ?country=ru یا ru.example.com استفاده می‌کنند.

در عمل، اگر شما یک فروشگاه اینترنتی را پارس می‌کنید و قیمت‌ها را به دلار به جای روبل می‌بینید، در 80% موارد مشکل در هدرها نیست، بلکه در آدرس IP درخواست است. سایت به سادگی «ندید» که IP روسی است و ویترین بین‌المللی پیش‌فرض را قرار داده است.

پس از اینکه سایت یک بار منطقه شما را تعیین کرد (از طریق IP یا انتخاب دستی در بنر «شهر خود را انتخاب کنید»)، این تصمیم را در کوکی ذخیره می‌کند — معمولاً با نام‌هایی مانند region, geo_city, currency یا locale. سپس همه درخواست‌ها با این کوکی بر اساس منطقه ذخیره شده پردازش می‌شوند — حتی اگر IP ناگهان تغییر کند.

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

طرف دیگر همان مکانیک مفید است: اگر شما نام کوکی مورد نظر و مقدار صحیح آن را برای منطقه هدف می‌دانید (به عنوان مثال، آن را در DevTools مرورگر در مکان مورد نظر مشاهده کرده‌اید)، می‌توانید به طور اجباری این مقدار را در درخواست قرار دهید بدون اینکه IP را تغییر دهید. این برای سایت‌هایی که منطقه تنها از طریق پرچم کوکی تعیین می‌شود کار می‌کند، اما اگر سرور در هر درخواست دوباره IP را با پایگاه GeoIP بررسی کند، کار نمی‌کند.

نقش آدرس IP و پروکسی در تعیین منطقه

تعیین GeoIP — مطمئن‌ترین راه برای سایت است که بفهمد درخواست واقعاً از کجا آمده است، زیرا هدرها و کوکی‌ها می‌توانند جعل شوند، اما آدرس IP نمی‌تواند جعل شود (فقط می‌توان آن را با یک آدرس واقعی دیگر جایگزین کرد). به همین دلیل برای دریافت پایدار «نسخه صحیح» منطقه‌ای صفحه کافی نیست که فقط هدرهای HTTP وجود داشته باشد — نیاز به IP است که واقعاً متعلق به کشور مورد نظر باشد.

در اینجا تفاوت قابل توجهی بین انواع آدرس‌های IP وجود دارد:

  • IP مراکز داده — پایگاه‌های GeoIP اغلب آن‌ها را به درستی بر اساس کشور شناسایی می‌کنند، اما بسیاری از سایت‌های بزرگ (به ویژه بازارها و خدماتی که از ربات‌ها محافظت می‌کنند) دامنه‌های مراکز داده را به عنوان مشکوک علامت‌گذاری می‌کنند و ممکن است نسخه‌ای کاهش‌یافته یا پیش‌فرض از محتوا را نشان دهند، صرف نظر از جغرافیا.
  • IP‌های مقیم — متعلق به ارائه‌دهندگان واقعی و آدرس‌های فیزیکی هستند، بنابراین GeoIP منطقه را دقیق‌تر شناسایی می‌کند و سایت درخواست را به عنوان یک کاربر معمولی از این کشور/شهر درک می‌کند.
  • IP‌های موبایل — به اپراتورهای موبایل کشور خاصی متصل هستند، که به ویژه برای سایت‌ها و برنامه‌هایی که به طور متفاوتی با ترافیک موبایل و دسکتاپ کار می‌کنند، مهم است.

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

عملی: جمع‌آوری درخواست صحیح در Python

بیایید درخواستی را جمع‌آوری کنیم که همزمان به همه سه مشکل پاسخ می‌دهد: هدر Accept-Language صحیح را ارسال می‌کند، از یک جلسه تمیز با کوکی‌های مورد نیاز استفاده می‌کند و از IP منطقه مورد نظر می‌گذرد.

import requests

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

headers = {
    "Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/124.0.0.0 Safari/537.36",
}

cookies = {
    "region": "ru",
    "currency": "RUB",
}

session = requests.Session()
response = session.get(
    "https://example.com/catalog",
    headers=headers,
    cookies=cookies,
    proxies=proxies,
    timeout=15,
)

print(response.status_code)
print(response.text[:500])

چند نکته مهم در این کد:

  • از requests.Session() استفاده می‌شود، نه یک requests.get() تکی، تا در صورت لزوم سرور بتواند کوکی‌های اضافی را تنظیم کند و آن‌ها بین درخواست‌های یک جلسه پارسینگ ذخیره شوند.
  • مقادیر کوکی region و currency — شرطی هستند، نام‌ها و مقادیر واقعی آن‌ها باید در DevTools مرورگر (زیرمجموعه Application → Cookies) در سایت هدف با مکان مورد نظر مشاهده شوند.
  • پروکسی باید واقعاً در کشور مورد نظر باشد — در غیر این صورت حتی هدرها و کوکی‌های صحیح نیز ممکن است توسط سرور در هنگام تعیین مجدد جغرافیا بازنویسی شوند.

سایت‌های JS: Playwright و مدیریت هدرها/کوکی‌ها

اگر سایت محتوا را از طریق JavaScript رندر می‌کند (قیمت‌ها پس از بارگذاری صفحه با یک درخواست جداگانه بارگذاری می‌شوند)، یک درخواست ساده requests کافی نیست — به یک مرورگر headless نیاز است. در Playwright، محلی، زبان و پروکسی در سطح زمینه مرورگر تعیین می‌شوند:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    context = browser.new_context(
        locale="ru-RU",
        timezone_id="Europe/Moscow",
        proxy={
            "server": "http://gate.proxycove.com:8080",
            "username": "user",
            "password": "pass",
        },
    )
    context.add_cookies([
        {"name": "region", "value": "ru", "domain": "example.com", "path": "/"},
        {"name": "currency", "value": "RUB", "domain": "example.com", "path": "/"},
    ])

    page = context.new_page()
    page.goto("https://example.com/catalog")
    page.wait_for_selector(".price")
    print(page.content()[:500])

    browser.close()

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

اشتباهات رایج و چک‌لیست قبل از راه‌اندازی

قبل از اینکه پارسر را در تولید راه‌اندازی کنید، از یک چک‌لیست کوتاه عبور کنید — این ساعت‌ها عیب‌یابی را صرفه‌جویی می‌کند:

  • ✅ هدر Accept-Language تنظیم شده و با منطقه مورد انتظار مطابقت دارد.
  • ✅ جلسه تمیز است — کوکی‌ها از درخواست ناموفق قبلی به ارث نرسیده‌اند.
  • ✅ IP پروکسی به طور فیزیکی در کشور مورد نظر قرار دارد (قبل از پارسینگ از هر چک‌کننده geo-IP بررسی کنید).
  • ✅ User-Agent با مرورگر واقعی مطابقت دارد و نشانه‌هایی از حالت headless ندارد.
  • ✅ منطقه زمانی و محلی مرورگر (برای Playwright/Selenium) با کشور IP مطابقت دارد.
  • ✅ اگر سایت از انتخاب دستی منطقه در بنر استفاده می‌کند — کوکی این انتخاب به صورت دستی منتقل می‌شود.

به طور جداگانه باید در مورد چرخش پروکسی گفت: اگر شما IP را برای هر درخواست تغییر می‌دهید، اما کوکی‌های جلسه همان باقی می‌مانند، سایت ممکن است عدم تطابق «کوکی منطقه A، اما IP منطقه B» را ببیند و جلسه را بازنشانی کند. برای وظایف نظارت بر قیمت‌ها بهتر است یک IP را برای کل جلسه پارسینگ منطقه خاصی ثابت کنید، نه اینکه آن را در وسط جمع‌آوری داده‌ها بچرخانید.

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

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

نوع پروکسی دقت جغرافیایی کی استفاده کنیم
پروکسی مراکز داده متوسط، کشور صحیح است، اما IP ممکن است به عنوان «غیر مسکونی» علامت‌گذاری شود پارسینگ سایت‌ها بدون حفاظت شدید ضد ربات، سرعت بالا و حجم درخواست‌ها
پروکسی‌های مقیم بالا، حتی تا شهر نظارت بر قیمت‌ها در بازارها، محتوای منطقه‌ای، دور زدن بررسی‌های GeoIP
پروکسی‌های موبایل بالا، به علاوه علامت اپراتور موبایل سایت‌ها و برنامه‌هایی که به طور متفاوتی با ترافیک موبایل کشور خاص کار می‌کنند

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

نتیجه‌گیری

مشکل «پارسر نسخه نادرست صفحه را می‌بیند» تقریباً همیشه از سه عامل تشکیل می‌شود: هدر Accept-Language، که زبان مورد نظر پاسخ را تعیین می‌کند؛ کوکی‌ها، که منطقه و ارز انتخاب شده قبلی را به یاد می‌آورند؛ و جغرافیایی IP-آدرس، که سرور کشور درخواست را در اولین بازدید تعیین می‌کند. برای دریافت دقیقاً نسخه‌ای از سایت که کاربر زنده می‌بیند، باید همه سه پارامتر را به طور همزمان همگام‌سازی کنید، نه اینکه آن‌ها را به صورت جداگانه تعمیر کنید.

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