شما یک IP مقیم گرانقیمت گرفتهاید، چرخش را تنظیم کردهاید، یک User-Agent واقعی قرار دادهاید — اما پارسر همچنان به CAPTCHA میرود یا پاسخ خالی میگیرد. مشکل تقریباً همیشه در IP نیست، بلکه در اثر انگشت TLS است: کتابخانهای که شما درخواست HTTPS را ارسال میکنید، «صدای» آن مانند یک مرورگر واقعی نیست. سیستمهای ضد ربات Wildberries، Ozon، Cloudflare و Akamai این را قبل از بررسی آدرس IP شما میبینند.
اثر انگشت TLS چیست و چرا مهمتر از IP است
زمانی که مشتری یک اتصال HTTPS برقرار میکند، یک بسته ClientHello به سرور ارسال میکند — بخشی از دست دادن TLS. در آن لیست نسخههای پشتیبانی شده TLS، مجموعه رمزها (cipher suites)، ترتیب گسترشها (extensions)، منحنیهای بیضوی و الگوریتمهای فشردهسازی رمزگذاری شده است. این مجموعه پارامترها برای هر ترکیب «کتابخانه + سیستمعامل + نسخه استک TLS» منحصر به فرد است.
Chrome، Firefox و Safari ClientHello را به شیوه خود تشکیل میدهند و این مجموعه تقریباً از یک درخواست به درخواست دیگر تغییر نمیکند — بر خلاف IP یا User-Agent که به راحتی میتوان آنها را جعل کرد. اما کتابخانههای HTTP استاندارد — requests، urllib3، HttpClient استاندارد در Java، استک TLS داخلی Node.js — ClientHello کاملاً متفاوتی را تشکیل میدهند، زیرا از OpenSSL یا کتابخانه دیگری به طور متفاوتی نسبت به مرورگر استفاده میکنند.
به همین دلیل است که شما میتوانید یک IP مقیم کاملاً «پاک» متصل کنید، یک User-Agent جدید از Chrome واقعی قرار دهید — و همچنان مسدود شوید. سرور IP کاربر را از یک خانه مسکونی میبیند، عنوان «Chrome 124» را میبیند، اما دست دادن TLS میگوید: «این یک اسکریپت Python است». عدم تطابق — سیگنال مستقیم برای ضد ربات است.
چگونه سیستمهای ضد ربات پارسر را با JA3/JA4 شناسایی میکنند
برای تبدیل پارامترهای ClientHello به یک شناسایی فشرده، از الگوریتم JA3 (و نسخه جدیدتر آن JA4) استفاده میشود. این الگوریتم نسخه TLS، لیست رمزها، گسترشها و منحنیها را میگیرد، آنها را در یک رشته ترکیب میکند و از طریق MD5 هش میکند. نتیجه یک هش کوتاه به شکل 769,47-53-5-10...,0-23-65281...,29-23-24,0 است که به وضوح «اثر انگشت» مشتری را شناسایی میکند.
تأمینکنندگان ضد ربات (Cloudflare، Akamai، PerimeterX، DataDome — و معادلهای آنها که از Wildberries و Ozon استفاده میکنند) پایگاههای دادهای از JA3/JA4 هشهای معروف کتابخانههای HTTP محبوب دارند: requests، aiohttp، Scrapy، Node fetch، Java HttpClient، Go net/http. اگر هش با یک امضای شناخته شده «اسکریپت» مطابقت داشته باشد و نه با امضای Chrome/Firefox/Safari — درخواست به عنوان مشکوک علامتگذاری میشود حتی قبل از تجزیه و تحلیل رفتار.
سپس سیستم به تطابق اثر انگشت TLS با User-Agent اعلام شده نگاه میکند. اگر در هدرها نوشته شده باشد «Chrome 124 در ویندوز»، و اثر انگشت TLS با OpenSSL 1.1.1 از کتابخانه استاندارد Python مطابقت داشته باشد — این عدم تطابق TLS/HTTP نامیده میشود، یکی از مطمئنترین سیگنالهای شناسایی خودکار. اینگونه پارسرها حتی با IP مقیم ایدهآل و هدرهای صحیح شناسایی میشوند.
چگونه اثر انگشت TLS خود را بررسی کنیم: ابزارها
قبل از رفع مشکل، باید ببینید که سرور چه چیزی میبیند. چندین سرویس عمومی وجود دارد که هش JA3/JA4 شما و مجموعه کامل پارامترهای ClientHello را نشان میدهند:
- tls.peet.ws — هش JA3، JA4، لیست رمزها و گسترشها را در فرمت JSON نشان میدهد، که برای بررسی خودکار با اسکریپت مناسب است.
- ja3er.com — پایگاه دادهای از هشهای JA3 شناخته شده با پیوند به کتابخانهها و مرورگرهای خاص.
- browserleaks.com/tls — مقایسه بصری اثر انگشت شما با اثر انگشتهای معمولی مرورگرها.
- Wireshark به صورت محلی — اگر میخواهید بسته خام ClientHello را هنگام ارسال درخواست از اسکریپت خود ببینید.
تست عملی ساده است: tls.peet.ws را در Chrome معمولی باز کنید و هش JA4 را یادداشت کنید. سپس یک درخواست GET به همان آدرس از پارسر خود ارسال کنید (از طریق requests، curl_cffi یا هر کتابخانه دیگری) از طریق همان پروکسی و هشها را مقایسه کنید. اگر آنها متفاوت باشند — سرور تفاوت بین «مرورگر» و «اسکریپت» را در هر درخواست میبیند، صرف نظر از اینکه IP چقدر پاک است.
بررسی با Python: requests، httpx، curl_cffi
بیایید به طور عملی بررسی کنیم که چرا کتابخانههای استاندارد Python پارسر را شناسایی میکنند. یک درخواست معمولی از طریق requests:
import requests
resp = requests.get("https://tls.peet.ws/api/all", proxies={
"https": "http://user:pass@proxy_host:port"
})
print(resp.json()["tls"]["ja4"])
# نتیجه با JA4 Chrome واقعی متفاوت خواهد بود،
# زیرا requests از ماژول ssl استاندارد Python استفاده میکند
مشکل این است که requests و httpx از OpenSSL سیستمی از طریق ماژول ssl استفاده میکنند، و ترتیب و مجموعه گسترشهای TLS در آن به شدت ثابت شده و با Chrome/Firefox مطابقت ندارد. راه حل — کتابخانه curl_cffi است که از curl اصلاح شده با پروفایلهای واقعی TLS مرورگرها استفاده میکند:
from curl_cffi import requests as cffi_requests
resp = cffi_requests.get(
"https://tls.peet.ws/api/all",
impersonate="chrome124",
proxies={"https": "http://user:pass@proxy_host:port"}
)
print(resp.json()["tls"]["ja4"])
# هش با Chrome واقعی 124 در دسکتاپ یکسان خواهد بود
پارامتر impersonate باعث میشود curl_cffi نه تنها ClientHello را بازتولید کند، بلکه ترتیب هدرهای HTTP/2 (ترتیب فریم) را نیز که در اثر انگشت نیز وارد میشود، بازتولید کند. رویکرد مشابهی توسط کتابخانههای tls-client برای Go و undetected-chromedriver برای کسانی که از طریق مرورگر واقعی و نه از طریق کلاینت HTTP پارس میکنند، استفاده میشود.
اگر پارس از طریق مرورگر headless (Playwright، Puppeteer، Selenium) انجام شود، اثر انگشت TLS توسط موتور Chromium/Firefox تشکیل میشود و به طور پیشفرض با مرورگر واقعی مطابقت دارد. اما در اینجا یک مشکل دیگر به وجود میآید — امضاهای خودکار در سطح JS (پرچمهای webdriver، اثر انگشت canvas)، بنابراین برای سناریوهای headless به پچهای اضافی مانند playwright-stealth نیاز است.
TLS + HTTP/2 + هدرها: چرا این ترکیب مهم است
اثر انگشت TLS فقط یک لایه شناسایی است. سیستمهای ضد ربات چندین سطح را به طور همزمان بررسی میکنند:
- TLS ClientHello (JA3/JA4) — مجموعه رمزها و گسترشها.
- اثر انگشت HTTP/2 — ترتیب هدرهای مجازی (:method، :path، :authority)، تنظیمات فریم SETTINGS، اندازه پنجره.
- هدرهای HTTP — ترتیب و مجموعه هدرهای معمولی (Accept-Language، Sec-Ch-Ua، Sec-Fetch-*).
- User-Agent — باید با نسخه پروفایل TLS مطابقت داشته باشد: اگر UA میگوید «Chrome 124»، و TLS با Chrome 110 مطابقت دارد، این نیز مشکوک است.
یک اشتباه رایج — بهروزرسانی User-Agent به آخرین نسخه Chrome، فراموش کردن بهروزرسانی پروفایل TLS در curl_cffi یا کتابخانه دیگر. این عدم تطابق نسخهها به وضوح برای ضد ربات قابل مشاهده است، مانند عدم وجود کامل پوشش. بررسی کنید که نسخه impersonate و نسخه در User-Agent مطابقت دارند و هر دو پارامتر را به صورت همزمان هنگام انتشار نسخههای جدید مرورگر بهروزرسانی کنید.
یک نکته دیگر — ترتیب هدرها. مرورگرها هدرها را در یک ترتیب مشخص ارسال میکنند، و بسیاری از کتابخانههای HTTP آنها را به صورت الفبایی یا بر اساس ترتیب اضافه شدن در کد مرتب میکنند. حتی اگر مجموعه هدرها با مرورگر یکسان باشد، ترتیب نادرست — سیگنال اضافی برای سیستمهای ضد ربات پیشرفته مانند DataDome است.
نقش پروکسی: چرا IP خالص نجات نمیدهد
IP مقیم یک وظیفه خاص را حل میکند — مشکوک بودن را از نظر جغرافیا، ASN و شهرت آدرس کاهش میدهد. IPهای مرکز داده اغلب در لیستهای سیاه قرار دارند، زیرا ترافیک خودکار به طور انبوه از آنها میآید، در حالی که IPهای مقیم متعلق به ارائهدهندگان واقعی و کاربران عادی هستند. برای پارس Wildberries، Ozon یا Avito این موضوع حیاتی است: بدون IP خالص، درخواست به دلیل همین نشانه مسدود میشود، حتی بدون بررسی TLS.
اما IP و اثر انگشت TLS دو لایه مستقل از حفاظت هستند و مشکلات مختلفی را حل میکنند. IP به سرور میگوید «از کجا» درخواست آمده است، اثر انگشت TLS میگوید «با چه چیزی» ارسال شده است. بنابراین ترکیب IP خالص و پروفایل TLS صحیح حداقل مجموعهای برای پارس پایدار است. برای وظایفی با فرکانس بالای درخواستها و ضد ربات تهاجمی بهتر است از پروکسیهای مقیم استفاده کنید، زیرا آنها درصد پایینی از مسدود شدن را بر اساس شهرت IP ارائه میدهند، اما حتماً آنها را با کتابخانهای ترکیب کنید که پروفایل TLS مرورگر واقعی را به درستی بازتولید میکند.
برای نظارت بر قیمتها در بازارها، جایی که سرعت و حجم درخواستها مهم است، اغلب از پروکسیهای مرکز داده به همراه پوشش TLS از طریق curl_cffi استفاده میشود — این ارزانتر از پروکسیهای مقیم است و به اندازه کافی مؤثر است، اگر سیستم ضد ربات سایت خیلی تهاجمی نباشد. و برای وظایفی که سایت به طور فعال شبکههای موبایل را بررسی میکند (به عنوان مثال، پارس نسخههای موبایل برنامهها از طریق API)، از پروکسیهای موبایل استفاده میشود — آنها سطح اضافی از اعتماد را به دلیل شهرت شبکههای اپراتوری ارائه میدهند.
چکلیست تنظیم پارسر بدون شناسایی
بررسی را در یک فرآیند واحد قبل از راهاندازی پارسر در تولید جمعآوری کنید:
- هش JA4 اسکریپت خود را از طریق tls.peet.ws اندازهگیری کنید و با مرورگر واقعی از همان نسخه مقایسه کنید.
- از کتابخانهای با پشتیبانی از جعل TLS استفاده کنید: curl_cffi (Python)، tls-client (Go)، CycleTLS (Node.js).
- نسخه پروفایل TLS (impersonate) را با نسخه در User-Agent همگامسازی کنید.
- ترتیب هدرهای HTTP را بررسی کنید — باید با مرورگر واقعی مطابقت داشته باشد، نه با ترتیب الفبایی.
- یک IP مقیم یا موبایل خالص متناسب با جغرافیای وظیفه خود متصل کنید.
- چرخش IP را جدا از پروفایل TLS تنظیم کنید — یکی را به دیگری به شدت متصل نکنید.
- بهطور منظم پروفایل TLS را هنگام انتشار نسخههای جدید Chrome بهروزرسانی کنید — امضاهای قدیمی سریعتر از آنچه که به نظر میرسد به پایگاههای ضد ربات میرسند.
- برای سناریوهای با بررسیهای JS (چالش Cloudflare) از مرورگر headless با پچهای stealth به جای کلاینت HTTP خالص استفاده کنید.
مقایسه کتابخانهها و ابزارها
| ابزار | اثر انگشت TLS مرورگر | سرعت | کی استفاده کنیم |
|---|---|---|---|
| requests / httpx | خیر، اسکریپت را ارائه میدهد | بالا | سایتهای بدون شناسایی TLS، APIهای داخلی |
| curl_cffi | بله، کپی دقیق | بالا | بازارها، ضد ربات Cloudflare/Akamai |
| tls-client (Go) | بله | بسیار بالا | بار بالا، پارس انبوه |
| Playwright / Puppeteer | بله، موتور واقعی | پایین | رندر JS، چالش Cloudflare، SPAهای پیچیده |
| Scrapy (استاندارد) | خیر | بالا | سایتهای بدون حفاظت ضد ربات سخت |
نتیجهگیری
اثر انگشت TLS لایهای از حفاظت است که بسیاری از پارسرها به طور کامل نادیده میگیرند و منابع را صرف پیدا کردن IP و User-Agent ایدهآل میکنند، اما فراموش میکنند که خود ساختار دست دادن TLS خودکار بودن را زودتر از آنکه سرور به هدرها نگاه کند، فاش میکند. راه حل — استفاده از کتابخانههای با پشتیبانی از جعل TLS (curl_cffi، tls-client)، همگامسازی نسخه پروفایل با User-Agent و بررسی هش نهایی JA4 قبل از راهاندازی در مقیاس است.
IP همچنان یک عامل مهم باقی میماند — بدون آدرس خالص حتی اثر انگشت TLS ایدهآل نیز نمیتواند از مسدود شدن بر اساس شهرت شبکه جلوگیری کند. برای پارس بازارها و نظارت بر قیمتها، معقول است که تنظیم صحیح TLS را با پروکسیهای مقیم ترکیب کنید — این ترکیب هر دو لایه شناسایی را پوشش میدهد و به طور قابل توجهی درصد مسدود شدن را در جلسات طولانی پارس کاهش میدهد.