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

API پنهان به جای تجزیه HTML: چگونه ترافیک و مصرف پروکسی را ۱۰ برابر کاهش دهیم

بررسی می‌کنیم که چرا پارس کردن صفحات HTML بودجه پروکسی را مصرف می‌کند و نشان می‌دهیم که چگونه به APIهای پنهان منتقل شویم - با کاهش واقعی ترافیک به ۱۰ برابر.

📅۵ مهر ۱۴۰۵

اگر شما Wildberries، Ozon یا هر وب‌سایت دیگری را از طریق بارگذاری صفحات کامل HTML پارس می‌کنید، به ازای ترافیک پروکسی 5-10 برابر بیشتر از آنچه که می‌توانستید پرداخت می‌کنید. هر صفحه کارت محصول بین 200-800 کیلوبایت شامل نشانه‌گذاری، اسکریپت‌ها و استایل‌ها است که شما به طور واقعی فقط به چند فیلد نیاز دارید: قیمت، موجودی، رتبه‌بندی. در این مقاله بررسی می‌کنیم که چگونه API پنهان وب‌سایت را پیدا کنیم و همان داده‌ها را به طور مستقیم و در فرمت JSON فشرده دریافت کنیم.

چرا پارسینگ HTML ترافیک پروکسی را می‌بلعد

وقتی پارسر صفحه‌ای را از طریق یک درخواست HTTP عادی یا از طریق مرورگر بدون سر (Selenium، Puppeteer، Playwright) بارگذاری می‌کند، سرور یک سند HTML کامل را ارائه می‌دهد: نشانه‌گذاری، اسکریپت‌های درون‌خط، استایل‌ها، گاهی تصاویر base64 و صدها خط JSON با داده‌های مربوط به ویجت‌های تبلیغاتی که به آن‌ها نیاز ندارید. میانگین کارت محصول در Wildberries حدود 300-600 کیلوبایت است و در Ozon تا 800 کیلوبایت، اگر تمام منابع مرتبط (CSS، فونت‌ها، ردیاب‌ها) را در نظر بگیریم.

اگر شما روزانه 10,000 محصول را از طریق 3 جلسه پروکسی نظارت می‌کنید، این به راحتی به ده‌ها گیگابایت ترافیک در ماه تبدیل می‌شود. پروکسی‌های مقیم و موبایل معمولاً بر اساس ترافیک فروخته می‌شوند، بنابراین هر مگابایت اضافی هزینه‌های مستقیم است. در عین حال، داده‌های واقعی که به آن‌ها نیاز دارید — قیمت، تخفیف، موجودی انبار، رتبه‌بندی — در پاسخ JSON تنها 1-5 کیلوبایت را اشغال می‌کنند. تفاوت در حجم به میزان 100-200 برابر برای هر محصول است و با توجه به هزینه‌های اضافی مربوط به رندرینگ مرورگر، صرفه‌جویی در زمان و CPU حتی بیشتر می‌شود.

مشکل اضافی پارسینگ HTML — شکنندگی آن است. وب‌سایت‌های بازار به طور مرتب ساختار، کلاس‌های CSS و ساختار DOM را تغییر می‌دهند. هر تغییر چنین پارسری را که بر اساس XPath یا انتخاب‌گرهای CSS ساخته شده است، خراب می‌کند. API داخلی به مراتب کمتر تغییر می‌کند، زیرا عملکرد برنامه موبایل و فرانت‌اند وب‌سایت به آن وابسته است.

API پنهان چیست و از کجا می‌آید

تقریباً هر وب‌سایت مدرن یک SPA (برنامه تک صفحه‌ای) یا برنامه ترکیبی است که در آن مرورگر ابتدا "اسکلت" صفحه را بارگذاری می‌کند و سپس از طریق JavaScript درخواست‌های اضافی به API داخلی برای داده‌های واقعی: قیمت‌ها، موجودی‌ها، نظرات، توصیه‌ها می‌فرستد. این درخواست‌ها به عنوان API‌های پنهان یا داخلی شناخته می‌شوند — آن‌ها به صورت عمومی مستند نشده‌اند، اما به طور کامل در ترافیک مرورگر باز هستند.

از نظر فنی، این معمولاً نقاط پایانی REST یا GraphQL هستند که داده‌ها را در فرمت JSON ارائه می‌دهند. به عنوان مثال، در Wildberries کارت محصول از طریق درخواست‌هایی مانند card.wb.ru و wbx-content-v2.wbstatic.net بارگذاری می‌شود و قیمت‌ها و موجودی‌ها از طریق یک درخواست جداگانه به basket-01.wb.ru و دامنه‌های مشابه می‌آیند. در Ozon منطق مشابهی وجود دارد: فرانت‌اند به API داخلی composer مراجعه می‌کند که داده‌ها را از میکروسرویس‌ها تجمیع می‌کند.

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

چگونه API پنهان را از طریق DevTools پیدا کنیم

پیدا کردن API داخلی بدون یک خط کد میسر است، با استفاده از ابزارهای داخلی مرورگر Chrome یا Firefox. در اینجا الگوریتم مرحله به مرحله آمده است:

  1. صفحه محصول مورد نظر را در Chrome باز کنید، کلید F12 را فشار دهید و به برگه Network بروید.
  2. در فیلتر درخواست‌ها نوع Fetch/XHR را انتخاب کنید — این کار بارگذاری تصاویر، فونت‌ها و استاتیک را حذف می‌کند.
  3. صفحه را به‌روزرسانی کنید (F5) و لیست درخواست‌هایی را که پس از بارگذاری اسکلت صفحه ظاهر شده‌اند، مشاهده کنید.
  4. درخواستی را پیدا کنید که در پاسخ آن (برگه Response) قیمت محصول، نام یا سایر فیلدهای مورد نیاز در فرمت JSON قابل مشاهده باشد.
  5. روی این درخواست کلیک کنید و آن را به عنوان cURL کپی کنید (با کلیک راست → Copy → Copy as cURL) — این به شما مجموعه کاملی از هدرها، کوکی‌ها و پارامترها را می‌دهد.
  6. بررسی کنید که کدام پارامترها در URL الزامی هستند (کد محصول، منطقه، نسخه API) و کدام‌ها را می‌توان بدون از دست دادن داده‌ها حذف کرد.

پس از آن کافی است این درخواست را از طریق یک کتابخانه HTTP عادی تکرار کنید و کد محصول یا ID محصول مورد نظر را جایگزین کنید به جای اینکه کل صفحه را به طور کامل رندر کنید. این کار برای اکثر بازارها — Wildberries، Ozon، Avito و همچنین برای بسیاری از پلتفرم‌های خارجی مانند Amazon و eBay کار می‌کند.

مقایسه ترافیک: HTML در مقابل JSON API

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

روش پارسینگ اندازه متوسط پاسخ زمان بارگذاری نیاز به رندرینگ JS
HTML کامل از طریق Selenium 400-800 کیلوبایت 1.5-4 ثانیه بله
درخواست HTTP ساده (requests) 150-300 کیلوبایت 0.3-0.8 ثانیه خیر
API JSON پنهان 3-15 کیلوبایت 0.1-0.3 ثانیه خیر

هنگام نظارت بر 50,000 محصول در روز، انتقال از مرورگر بدون سر به درخواست‌های مستقیم به API ترافیک را از حدود 30-40 گیگابایت به 300-700 مگابایت در ماه کاهش می‌دهد. این نه تنها صرفه‌جویی در ترافیک پروکسی است، بلکه بار روی زیرساخت سرور پارسر را نیز کاهش می‌دهد — CPU کمتری برای رندرینگ، حافظه کمتر، و جمع‌آوری داده‌ها سریع‌تر.

مثال عملی در Python

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

import requests

def get_product_data(product_id: str, proxies: dict = None) -> dict:
    """
    داده‌های محصول را از طریق API داخلی به جای HTML کامل دریافت می‌کند.
    proxies — دیکشنری با پروکسی به فرمت requests: {"http": "...", "https": "..."}
    """
    url = f"https://card.example-marketplace.ru/v2/detail"
    params = {
        "nm": product_id,
        "dest": "-1257786",  # منطقه، از طریق DevTools تعیین می‌شود
        "spp": "0"
    }
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                       "AppleWebKit/537.36 (KHTML, like Gecko) "
                       "Chrome/120.0 Safari/537.36",
        "Accept": "application/json",
        "Referer": f"https://www.example-marketplace.ru/catalog/{product_id}/detail.aspx"
    }

    response = requests.get(
        url,
        params=params,
        headers=headers,
        proxies=proxies,
        timeout=10
    )
    response.raise_for_status()
    data = response.json()

    product = data["products"][0]
    return {
        "id": product["id"],
        "name": product["name"],
        "price": product["salePriceU"] / 100,
        "stock": product.get("totalQuantity", 0),
        "rating": product.get("reviewRating", None)
    }


if __name__ == "__main__":
    proxy = {
        "http": "http://user:pass@proxy-host:port",
        "https": "http://user:pass@proxy-host:port"
    }
    result = get_product_data("123456789", proxies=proxy)
    print(result)

به سه نکته در این مثال توجه کنید. اولاً، ما هدر Referer را مشخص می‌کنیم، زیرا بسیاری از API‌ها بررسی می‌کنند که آیا درخواست "از مرورگر" آمده است یا مستقیماً از طریق URL. ثانیاً، ما از یک User-Agent واقعی استفاده می‌کنیم، نه پیش‌فرضی که از کتابخانه requests می‌آید و به راحتی شناسایی می‌شود. ثالثاً، کل درخواست در یک فراخوانی HTTP بدون رندرینگ قرار می‌گیرد — این چیزی است که صرفه‌جویی چند برابری در ترافیک و سرعت را به ارمغان می‌آورد.

برای نقاط پایانی GraphQL منطق مشابهی وجود دارد، اما به جای پارامترهای GET، شما یک درخواست POST با بدنه درخواست در فرمت JSON ارسال می‌کنید که در آن به وضوح فیلدهای مورد نیاز را ذکر می‌کنید — این حتی بیشتر حجم پاسخ را کاهش می‌دهد، زیرا سرور فقط داده‌های درخواست شده را ارائه می‌دهد.

کار با پروکسی در درخواست‌های API

حتی با انتقال به فرمت JSON فشرده، شما هنوز هم به پروکسی نیاز دارید — بازارها تعداد درخواست‌ها از یک IP را محدود می‌کنند و در صورت فعالیت غیرعادی مسدود می‌کنند. انتخاب صحیح نوع پروکسی در اینجا به طور مستقیم بر ثبات پارسر تأثیر می‌گذارد.

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

برای API‌هایی که به برنامه‌های موبایل وابسته هستند (برخی نسخه‌های نقاط پایانی Avito یا بازارها فقط داده‌ها را از طریق ترافیک موبایل ارائه می‌دهند)، ممکن است نیاز به اتصال از طریق پروکسی‌های موبایل باشد — آن‌ها ترافیک واقعی اپراتورهای تلفن همراه را شبیه‌سازی می‌کنند و از بررسی‌هایی که IP‌های عادی را مسدود می‌کنند عبور می‌کنند.

هنگام تنظیم پروکسی در پارسر، مهم است که درخواست‌ها را در زمان‌های مختلف توزیع کنید و از چرخش IP استفاده کنید — حتی یک درخواست JSON فشرده که 1000 بار در دقیقه از یک آدرس تکرار شود، می‌تواند سیستم حفاظت را مشکوک کند. یک استخر از چندین جلسه پروکسی تنظیم کنید و بار را بین آن‌ها توزیع کنید، با افزودن تأخیرهای تصادفی 1-3 ثانیه بین درخواست‌ها.

موانع: توکن‌ها، امضاها، ضدربات

API‌های پنهان همیشه به راحتی در دسترس نیستند. برخی وب‌سایت‌ها نقاط پایانی خود را با مکانیزم‌های اضافی محافظت می‌کنند که باید در هنگام ساخت پارسر در نظر گرفته شوند.

  • توکن‌های موقتی جلسه — برخی API‌ها نیاز به درخواست اولیه برای دریافت توکن دارند که سپس در هدر درخواست‌های بعدی ارسال می‌شود و زمان محدودی (معمولاً 5-30 دقیقه) معتبر است.
  • امضای درخواست (signature) — پارامترهای درخواست در سمت کلاینت با کلید مخفی از کد JS صفحه هش می‌شوند. این امضا باید یا به صورت دستی تولید شود، با تجزیه الگوریتم، یا از طریق مرورگر بدون سر فقط در مرحله دریافت توکن انجام شود و سپس درخواست‌های سبک به طور مستقیم ارسال شوند.
  • محدودیت نرخ بر اساس IP و User-Agent — در صورت تجاوز از فرکانس درخواست‌ها، وب‌سایت به طور موقت دسترسی را مسدود می‌کند. این مشکل با چرخش پروکسی و تأخیرهای معقول حل می‌شود.
  • فینگرپرینتینگ هدرها — برخی سیستم‌ها مجموعه کامل هدرها (ترتیب، وجود Accept-Language، Sec-Fetch-*) را بررسی می‌کنند و درخواست‌هایی را که با مجموعه "ناقص" خاصی که برای اسکریپت‌ها است، مسدود می‌کنند.
  • وابستگی داده‌ها به جغرافیا — قیمت‌ها و موجودی‌ها در بازارها ممکن است بسته به مناطق متفاوت باشند، بنابراین مهم است که پارامتر منطقه/انبار صحیح را در درخواست ارسال کنید، در غیر این صورت داده‌ها غیر مرتبط خواهند بود.

اگر API با امضای درخواست بسته شده باشد که بازتولید آن دشوار است، گزینه‌ای که می‌توان در نظر گرفت این است که از مرورگر بدون سر (Playwright، Puppeteer) فقط برای ضبط درخواست‌های شبکه و استخراج پاسخ JSON آماده استفاده کنید، بدون پارس کردن DOM. این روش کندتر از یک درخواست HTTP مستقیم است، اما هنوز هم سریع‌تر و آسان‌تر از پارسینگ کامل ساختار صفحه است.

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

  • نقطه پایانی از طریق DevTools پیدا شده، به عنوان cURL کپی شده و در Postman یا از طریق requests آزمایش شده است.
  • پارامترهای الزامی درخواست (ID محصول، منطقه، نسخه API) مشخص شده و پارامترهای اضافی حذف شده‌اند.
  • هدرهای User-Agent، Referer و Accept-Language به طور واقعی تنظیم شده‌اند.
  • بررسی شده است که آیا توکن جلسه یا امضای درخواست نیاز است و راهی برای دریافت آن‌ها در نظر گرفته شده است.
  • چرخش پروکسی و تأخیرهای تصادفی بین درخواست‌ها تنظیم شده است.
  • نوع مناسب پروکسی برای حفاظت خاص وب‌سایت انتخاب شده است — داده‌مرکز، مقیم یا موبایل.
  • مدیریت خطاهای 429 و 403 با سوئیچ خودکار به پروکسی دیگر اضافه شده است.
  • لاگ‌گیری حجم ترافیک برای کنترل صرفه‌جویی واقعی تنظیم شده است.

نتیجه‌گیری

انتقال از پارسینگ HTML کامل به کار با API پنهان نه تنها یک بهینه‌سازی فنی است، بلکه کاهش مستقیم هزینه‌ها برای ترافیک پروکسی و زیرساخت را به همراه دارد. به جای بارگذاری صدها کیلوبایت نشانه‌گذاری اضافی، شما یک JSON فشرده با دقیقاً همان فیلدهایی که برای نظارت بر قیمت‌ها، موجودی‌ها یا رتبه‌بندی‌ها نیاز دارید، دریافت می‌کنید. مزیت اضافی — پایداری پارسر در برابر تغییرات ساختار وب‌سایت، زیرا API‌های داخلی کمتر از فرانت‌اند تغییر می‌کنند.

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