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

پارسر فیلدهای خالی می‌دهد، پروکسی بی‌تأثیر است: رفع مشکل در درفت طراحی

پارسر خالی برگشت، شما پروکسی را تغییر می‌دهید — در حالی که مشکل در طراحی مجدد سایت بود. بررسی می‌کنیم که چگونه در پنج دقیقه تفاوت بین بن و درفت ویرایش را تشخیص دهیم، چگونه انتخاب‌گرهای سازگار Scrapling (اثر عنصر در SQLite و جستجو بر اساس شباهت در ۲.۴۶ میلی‌ثانیه) کار می‌کنند و چرا باید استاندارد را از همان جغرافیا که بعداً داده‌ها را جمع‌آوری می‌کنید، برداشت.

📅۱۴ شهریور ۱۴۰۵
پارسر فیلدهای خالی می‌دهد، پروکسی بی‌تأثیر است: رفع مشکل در درفت طراحی

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

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

این برای چه کسی لازم است

راهنمایی برای کسانی که پارسر را بیش از یک اسپرینت در تولید نگه می‌دارند: نظارت بر قیمت‌های رقباء، جمع‌آوری نظرات، تجمیع آگهی‌های شغلی، بارگذاری‌های روزانه برای تحلیل. اگر شما یک بار اسکریپت را اجرا می‌کنید و آن را دور می‌اندازید — تغییر طراحی به شما مربوط نمی‌شود. اگر اسکریپت به مدت ماه‌ها به صورت cron اجرا می‌شود، این هزینه اصلی شما برای نگهداری است.

مقیاس مشکل خیالی نیست. به گفته تحلیلگران GroupBWT، تغییرات ساختاری غیرقابل کنترل در سایت‌ها حدود 40–60% هزینه‌های تکراری نگهداری اسکرپرها را در پروژه‌های بزرگ ایجاد می‌کند. در برخی صنایع 10–15% از خزنده‌ها به طور هفتگی نیاز به تعمیر دارند — به دلیل تغییرات DOM، اثر انگشت‌گذاری و محدود کردن نقاط پایانی. به عبارت دیگر، تعمیر انتخابگرها از نظر هزینه با دور زدن ضد ربات‌ها رقابت می‌کند، در حالی که توجه به آن به مراتب کمتر است.

زمینه در اینجا خوشایند نیست: در گزارش Apify «وضعیت وب اسکرپینگ 2026» 65.8% از پاسخ‌دهندگان استفاده از پروکسی‌ها را افزایش داده‌اند، 58.3% افزایش هزینه‌های پروکسی را سال به سال گزارش کرده‌اند و بیش از 62% — افزایش کلی هزینه‌های زیرساختی، عمدتاً به دلیل افزایش حفاظت در برابر ربات‌ها. در این زمینه، سوزاندن ترافیک پرداخت شده بر روی صفحاتی که به هر حال چیزی از آن‌ها نمی‌گیرید، دو برابر ناامیدکننده است.

مرحله 1. تشخیص مسدود شدن از تغییر طراحی

تشخیص چند دقیقه طول می‌کشد و باید به ترتیب انجام شود — در غیر این صورت به راحتی می‌توانید «تعمیر» اشتباهی انجام دهید.

  1. به کد پاسخ و اندازه بدنه نگاه کنید. 403، 429، 503، ریدایرکت به صفحه بررسی یا بدنه‌ای به اندازه 2–5 کیلوبایت — این ضد ربات است. HTTP 200 و صفحه‌ای کامل به اندازه 200–800 کیلوبایت — سایت شما را پذیرفته، مشکل از پروکسی نیست.
  2. HTML خام را روی دیسک ذخیره کنید و با چشم باز کنید. نه در دیباگر، بلکه در مرورگر. اگر محصول/نظرات/قیمت در جای خود هستند و پارسر آن‌ها را نمی‌بیند — این تغییر طراحی است.
  3. متن مورد نظر را با جستجو در فایل پیدا کنید. اگر در HTML وجود دارد، اما با انتخابگر شما در دسترس نیست — نشانه‌گذاری تغییر کرده است. اگر اصلاً وجود ندارد — محتوا با اسکریپت بارگذاری می‌شود، به یک موتور مرورگر نیاز دارید، نه یک درخواست HTTP.
  4. با بارگذاری موفق قبلی مقایسه کنید. HTML قدیمی و جدید یک URL یکسان را مقایسه کنید: معمولاً بلافاصله کلاس جدید، بلوک منتقل شده یا جایگزینی id با data-* قابل مشاهده است.
  5. بررسی کنید که آیا سایت نسخه دیگری از صفحه را ارائه کرده است یا خیر. در مورد این موضوع — جداگانه در زیر، زیرا در اینجا پروکسی به هر حال مرتبط است.

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

مرحله 2. انتخابگرهای تطبیقی چیستند

ایده ساده است: به جای اینکه به شدت به رشته .product-card > h3.title متصل شوید، کتابخانه یک بار «پرتره» عنصر مورد نظر را به خاطر می‌سپارد و در اجرای بعدی، نزدیک‌ترین عنصر به این پرتره را در صفحه جستجو می‌کند.

این به طور عملی در Scrapling — فریم‌ورک پایتون باز کاریم شواهر به بهترین شکل پیاده‌سازی شده است. این پروژه در اکتبر 2024 منتشر شد و تا سپتامبر 2026 بیش از 78,000 ستاره در GitHub جمع‌آوری کرده است؛ آخرین نسخه در زمان نوشتن — v0.4.15 از 23 اوت 2026، کمیت‌ها به صورت روزانه انجام می‌شوند. به Python 3.10+ نیاز است.

مکانیسم جستجوی تطبیقی به این صورت است. وقتی شما انتخابگر را با auto_save=True فراخوانی می‌کنید، Scrapling اثر انگشت عنصر را ذخیره می‌کند:

  • نام تگ، متن و تمام ویژگی‌ها با مقادیرشان؛
  • نام‌های تگ‌های همسایه؛
  • مسیر تا عنصر — فقط بر اساس نام‌های تگ‌ها؛
  • تگ، ویژگی‌ها و متن والد.

اثر انگشت در پایگاه داده محلی SQLite ذخیره می‌شود و با جفت «دامنه + شناسه» کلیدگذاری می‌شود. دامنه از URL صفحه گرفته می‌شود (یا با پارامتر adaptive_domain تعیین می‌شود)، شناسه به طور پیش‌فرض خود رشته انتخابگر است — یا شناسه خودتان، اگر identifier= را منتقل کنید.

وقتی طراحی تغییر می‌کند و انتخابگر معمولی خالی برمی‌گردد، فراخوانی با adaptive=True اثر انگشت ذخیره شده را بالا می‌آورد و بر روی تمام عناصر صفحه اجرا می‌کند، با ارزیابی نادقیق شباهت — حتی تا ترتیب ویژگی‌ها. عنصری با بیشترین تطابق بازگردانده می‌شود.

این هزینه کمی دارد. بر اساس بنچمارک‌های رسمی پروژه، پارسینگ 1.99 میلی‌ثانیه طول می‌کشد در مقابل 2.01 میلی‌ثانیه برای Parsel/Scrapy، 22.93 میلی‌ثانیه برای PyQuery، 80.57 میلی‌ثانیه برای Selectolax و 1541 میلی‌ثانیه برای BeautifulSoup با lxml. خود جستجوی تطبیقی عنصر مشابه — 2.46 میلی‌ثانیه در مقابل 13.3 میلی‌ثانیه برای AutoScraper. به عبارت دیگر، بیمه در برابر طراحی مجدد تقریباً دو میلی‌ثانیه به درخواست اضافه می‌کند در حالی که تأخیر شبکه در صدها میلی‌ثانیه است.

مرحله 3. نصب و فعال‌سازی

نصب بستگی به این دارد که آیا به مرورگر نیاز دارید یا نه:

  1. pip install scrapling — فقط پارسر، بدون بخش شبکه. کافی است اگر HTML را با کد خود دریافت می‌کنید.
  2. pip install "scrapling[fetchers]"، سپس scrapling install — فچ‌ها را اضافه می‌کند و مرورگرها را با وابستگی‌ها دانلود می‌کند.
  3. اضافی: [ai] — سرور MCP، [rag] — رابط برای RAG، [shell] — کنسول تعاملی، [all] — همه با هم. یک تصویر آماده pyd4vinci/scrapling وجود دارد.

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

  1. اجرای مرجع. یک شیء Selector با adaptive=True ایجاد کنید و حتماً url را منتقل کنید — در غیر این صورت دامنه به کلید "default" می‌رود و اثر انگشت‌های سایت‌های مختلف با هم مخلوط می‌شوند. انتخابگر مورد نیاز را با auto_save=True فراخوانی کنید.
  2. اجرای عملیاتی. همان انتخابگر، اما با adaptive=True. تا زمانی که نشانه‌گذاری سالم باشد، مسیر معمول کار می‌کند. وقتی خراب شد — جستجوی شباهت فعال می‌شود.
  3. اختلافات را ثبت کنید. لحظه‌ای که انتخابگر معمولی خالی داد و انتخابگر تطبیقی چیزی پیدا کرد، سیگنالی است که «سایت منتقل شده» است، باید در نظارت دیده شود، نه به سکوت بلعیده شود.

جزئیات مهم درباره بازنویسی: ذخیره‌سازی انباشته نمی‌شود. auto_save تکراری برای همان جفت «دامنه + شناسه» اثر انگشت قبلی را بازنویسی می‌کند. بنابراین اثر انگشت مرجع باید از صفحه‌ای که به وضوح صحیح است، گرفته شود، نه در چرخه‌ای از تمام استخر URL.

مرحله 4. پروکسی: کجا واقعاً مرتبط هستند

ما با این شروع کردیم که تغییر طراحی به پروکسی مربوط نیست. این درست است، اما به طور نصفه، و نیمه دیگر هزینه دارد.

سایت ممکن است به شما نشانه‌گذاری دیگری بدهد به دلیل خروج پروکسی. محل، زبان و کشور الگوی صفحه را تغییر می‌دهند: ترتیب بلوک‌ها متفاوت، کلاس‌های متفاوت، فرمت‌های قیمت و تاریخ متفاوت. این یک فرضیه نیست — در خود Scrapling یک اصلاح نمایشی وجود دارد: در نسخه 0.4.12 از StealthyFetcher محلی اجباری en-US حذف شد، زیرا محلی تحمیلی با جغرافیای واقعی همخوانی نداشت و رفتار را خراب می‌کرد. از این رو قاعده عملی: اثر انگشت مرجع را از همان جغرافیا بگیرید که سپس داده‌ها را جمع‌آوری می‌کنید. اثر انگشتی که از طریق IP آلمانی گرفته شده، با صفحه‌ای که از طریق IP برزیلی دریافت شده، کمتر تطابق خواهد داشت — و شما هشدار کاذب «سایت طراحی را تغییر داده» دریافت خواهید کرد.

پیامدهای عملی:

  • اگر استخر چندکشوری است — اثر انگشت‌ها را از طریق adaptive_domain جدا کنید و کلیدهایی از نوع «دامنه + کشور» تعیین کنید. در غیر این صورت یک رکورد در SQLite به طور مداوم با نسخه‌های جغرافیای مختلف بازنویسی می‌شود.
  • برای سناریوهای طولانی، یک کشور و یک جلسه را برای کل وظیفه نگه دارید. نحوه انجام این کار به تفصیل در مطلبی درباره جلسات چسبنده و زمان استفاده از آن‌ها توضیح داده شده است.
  • آزمایش‌های A/B و استقرار تدریجی به طور همزمان دو طراحی زنده را در یک دامنه ارائه می‌دهند. در اینجا جستجوی تطبیقی به ویژه مفید است: او عنصر را از هر دو شاخه بیرون می‌آورد، در حالی که انتخابگر سخت به طور تصادفی در نیمی از درخواست‌ها خالی می‌دهد.

تنظیم پروکسی در Scrapling در تمام سطوح امکان‌پذیر است. برای درخواست‌های سریع HTTP، Fetcher و AsyncFetcher دارای پارامتر proxies هستند. برای جلسات، ProxyRotator وجود دارد که لیستی از آدرس‌ها را می‌گیرد — این در FetcherSession قرار می‌گیرد. مرورگرهای DynamicSession و StealthySession پروکسی را در سطح جلسه می‌پذیرند تا IP در میانه سناریو تغییر نکند.

یک چیز دیگر که هم استخر و هم اعصاب را صرفه‌جویی می‌کند، در نسخه 0.4.12 معرفی شد — AutoThrottle: کتابخانه به طور خودکار وقفه‌ها را بین درخواست‌ها با پاسخ‌های سرور تنظیم می‌کند، تأخیر را در صورت مسدود شدن دو برابر می‌کند و به هدر Retry-After احترام می‌گذارد. این دقیقاً همان رفتاری است که جمع‌آوری دقیق را از افزایش مسدود شدن‌ها به دلیل تلاش‌های احمقانه متمایز می‌کند.

دام‌های پنهان

  • SQLite با اثر انگشت‌ها را در git کامیت نکنید. این موضوع به وضوح در مستندات هشدار داده شده است. همچنین از auto_save در صفحات دارای داده‌های شخصی استفاده نکنید — متن و ویژگی‌های عنصر در اثر انگشت قرار می‌گیرند.
  • جستجوی تطبیقی — جایگزین نظارت نیست. او «نزدیک‌ترین» عنصر را برمی‌گرداند، اما نزدیک‌ترین همیشه درست نیست. اگر سایت قیمت تخفیف‌دار و قیمت بدون تخفیف را جابجا کرده باشد، شباهت بالا است، اما داده‌ها نادرست هستند. بررسی‌ها را بر روی دامنه‌های مقادیر و نسبت فیلدهای خالی در خروجی نگه دارید.
  • خرابی ساکت گران‌تر از خرابی بلند است. در حالی که انتخابگر به آرامی None را برمی‌گرداند، پایپ‌لاین به مرور صفحات ادامه می‌دهد و ترافیک پرداخت شده را می‌سوزاند. درباره هزینه واقعی یک گیگابایت که داده‌ای از آن استخراج نشده است، یک بررسی جداگانه وجود دارد — چرا قیمت پروکسی به ازای گیگابایت نادرست است.
  • اثر انگشت قدیمی می‌شود. پس از تأیید طراحی مجدد، اثر انگشت مرجع را دوباره بگیرید، در غیر این صورت اصلاح بعدی سایت به عنوان اثر انگشت قدیمی در نظر گرفته می‌شود و دقت کاهش می‌یابد.
  • اگر محتوا اصلاً در HTML وجود ندارد — تطبیق‌پذیری کمک نمی‌کند، به یک فچری مرورگر نیاز دارید. در 0.4.15، تب‌های مرورگر بین درخواست‌ها دوباره استفاده می‌شوند و متد close_pages() آن‌ها را به طور اجباری می‌بندد؛ همچنین قفل‌ها در حالت headless تعمیر شده و راه‌حل Turnstile دیگر به محلی مرورگر وابسته نیست.

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

انتخاب به جای پارسر، به سایت هدف بستگی دارد:

  • پروکسی‌های دیتاسنتر — برای سایت‌هایی بدون ضد ربات جدی: مستندات، ثبت‌نام‌های دولتی، دایرکتوری‌های باز، فیدهای RSS و CSV (برای آخرین‌ها در 0.4.13 XMLFeedSpider و CSVFeedSpider با استخراج خودکار gzip اضافه شده است). ارزان و سریع، و ثبات نشانه‌گذاری در اینجا معمولاً بالاتر است.
  • پروکسی‌های مقیم — برای بازارها، تجمیع‌کننده‌ها و هر چیزی که نتایج را بر اساس جغرافیا شخصی‌سازی می‌کند. در واقع، در اینجا مهم است که اثر انگشت را بگیرید و داده‌ها را از یک کشور جمع‌آوری کنید، در غیر این صورت شما خرابی را تعمیر نمی‌کنید، بلکه جغرافیای خود را تعمیر می‌کنید.
  • پروکسی‌های موبایل — زمانی که سایت الگوی موبایل را ارائه می‌دهد و باید به همان شکل پارس شود، یا زمانی که اعتماد به IP مهم‌تر از قیمت به ازای گیگابایت است.

خلاصه

فیلدهای خالی در خروجی دو تشخیص مختلف با درمان‌های متفاوت هستند. ابتدا کد پاسخ و HTML خام را بررسی کنید: اگر صفحه به طور کامل آمده است، نیازی به تغییر پروکسی نیست، نشانه‌گذاری خراب شده است. انتخابگرهای تطبیقی Scrapling این نوع خرابی‌ها را در عرض چند میلی‌ثانیه در درخواست‌ها پوشش می‌دهند — اثر انگشت را بر روی طراحی کار کنید، adaptive=True را در عملیات فعال کنید و لحظات فعال‌سازی را به عنوان سیگنالی برای طراحی مجدد ثبت کنید. و جغرافیا را پایدار نگه دارید: نیمی از «طراحی‌های ناگهانی» در عمل نسخه زبان دیگری از صفحه است که به دلیل تغییر کشور خروجی آمده است.

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