نادرًا ما تُرسِل تطبيقات الويب الحديثة HTML كاملًا؛ وبدلًا من ذلك، يُحمَّل المحتوى ويُعرَض ديناميكيًا عبر JavaScript. وهذا يطرح تحديات خاصة في كشط الويب سنتعامل معها في هذين الفصلين.
الفصل 4: موجز أخبار ديناميكي
يتضمن أول تحدٍّ لدينا كشط موجز أخبار حيث تُحمَّل المقالات ديناميكيًا عبر JavaScript. ويُقدِّم هذا عدة مفاهيم أساسية:
- أتمتة المتصفح باستخدام Playwright
- انتظار تحميل المحتوى الديناميكي
- التعامل مع عناصر DOM التي تُعرَض عبر JavaScript
تبدو بنية الصفحة على نحوٍ مماثل لما يلي:
<div class="news-feed">
<article class="news-item">
<h2>Breaking News Title</h2>
<p>Article content...</p>
<div class="meta">
<span>By Author Name</span>
<time datetime="2024-03-08T12:00:00Z">March 8, 2024</time>
</div>
</article>
<!-- More articles load dynamically -->
</div>
الاختلافات الأساسية مقارنةً بكشط HTML الثابت:
// Instead of cheerio.load(), we use Playwright
const browser = await chromium.launch();
const page = await browser.newPage();
// Wait for content to render
await page.waitForSelector('.news-item');
// Extract data from the live DOM using page.$$eval()
// This runs the callback function in the browser context
// to evaluate all elements matching the selector at once
const items = await page.$$eval('.news-item', elements => {
// Works like Array.map() on matching elements
// Returns serializable JavaScript objects
// Perfect for extracting data from multiple elements
});
الفصل 5: معرض صور بتمرير لا نهائي
استنادًا إلى ما تعلمناه عن المحتوى الديناميكي، ننتقل إلى سيناريو أكثر تعقيدًا: معرض صور بتمرير لا نهائي. ويُقدِّم هذا ما يلي:
- التعامل مع المحتوى الذي يُحمَّل بطريقة lazy-loaded
- اكتشاف أحداث التمرير وتحفيزها
- إدارة حالات التحميل غير المتزامن
- استخراج البيانات من أنماط واجهة مستخدم معقدة
يكمن التحدي هنا في أن المحتوى يُحمَّل تدريجيًا مع تمرير المستخدم:
<div class="photo-gallery">
<div class="photo-card">
<img src="..." alt="Photo title" />
<h2>Photo Title</h2>
<p>By Photographer Name</p>
<div class="flex">
<span>❤️ 42</span>
</div>
</div>
<!-- More photos load on scroll -->
</div>
المفاهيم الأساسية للتعامل مع التمرير اللانهائي:
// Scroll to bottom until no new content loads
let previousHeight;
while (true) {
previousHeight = await page.evaluate('document.body.scrollHeight');
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
await page.waitForTimeout(1500); // Wait for content
const newHeight = await page.evaluate('document.body.scrollHeight');
if (newHeight === previousHeight) {
break; // No more content to load
}
}
اعتبارات مهمة
عند العمل مع المحتوى المعروض عبر JavaScript:
- الأداء: يكون كشط المحتوى الديناميكي أبطأ من HTML الثابت
- إدارة الموارد: تستهلك أتمتة المتصفح موارد نظام أكثر
- الاستقرار: تحتاج إلى التعامل مع حالات التحميل وظروف الشبكة
- تحديد المعدل: فكّر في تطبيق فواصل زمنية بين الإجراءات
أفضل الممارسات
- استخدم استراتيجيات انتظار مناسبة:
// Wait for specific elements
await page.waitForSelector('.selector');
// Wait for network idle
await page.waitForLoadState('networkidle');
// Custom wait conditions
await page.waitForFunction(() => {
// Custom JavaScript condition
});
- نفّذ معالجة قوية للأخطاء:
try {
await page.goto(url);
// ... scraping logic
} catch (error) {
console.error('Scraping failed:', error);
} finally {
await browser.close(); // Always clean up
}
- فكّر في تنفيذ آليات إعادة المحاولة لتحسين الاعتمادية
- راقب استخدام الذاكرة عند التعامل مع مجموعات بيانات كبيرة
- تحقّق من صحة البيانات المستخرجة لضمان الاتساق
اختبار الحل الخاص بك
توفّر بيئة الاختبار واجهات API وهمية تحاكي ظروف العالم الحقيقي:
- أزمنة تحميل متغيرة
- كمون الشبكة
- آليات الترقيم إلى صفحات
- حالات الخطأ
جرّب هذه التنويعات:
- عدّل توقيت التمرير
- تعامل مع أحجام شاشات مختلفة
- اختبر في ظل ظروف شبكة بطيئة
- تحقّق من سلامة البيانات
هل أنت مستعد للتعامل مع المحتوى الديناميكي؟ ستجد كود التحدي وبيئات الاختبار في المستودع.
اطّلع على الحلول المرجعية في exercises/chapter-04/solution/src/ و exercises/chapter-05/solution/src/ للاطلاع على تطبيقات عاملة. وتذكّر: إن كشط الويب الحديث يتمحور حول فهم كلٍّ من بنية HTML وسلوك التطبيق.
كشطًا موفقًا!