خراشیدن پایه‌ای HTML: نخستین گام‌ها

۵ اسفند ۱۴۰۳

وب‌اسکرپینگ شاید در ابتدا ترسناک به نظر برسد، اما مثل هر مهارت دیگری، بهترین راه یادگیری آن تمرین عملی است. در این سه فصل نخست، مبانی استخراج داده از صفحات HTML ایستا را بررسی خواهیم کرد.

فصل ۱: چادِ خراش‌دهندهٔ HTML

سفر ما با ساده‌ترین سناریوی ممکن آغاز می‌شود — استخراج متن از یک صفحهٔ HTML ساده. این فصل شما را با مفاهیم اصلی زیر آشنا می‌کند:

  • ارسال درخواست‌های HTTP برای دریافت صفحات وب
  • بارگذاری محتوای HTML در یک parser
  • انتخاب پایه‌ای DOM با استفاده از CSS selectorها

هرچند این مثال ممکن است پیش‌پاافتاده به نظر برسد (بالاخره فقط یک “hello world” است!)، اما پایه‌ای را می‌سازد که همهٔ مطالب بعدی بر آن استوار خواهند بود.

<!-- Example HTML structure -->
<div class="content">
  <p>Some text we want to extract</p>
</div>

در اینجا نمونه‌ای از چیزی را می‌بینید که با آن کار می‌کنیم:

// Basic concepts (not the solution!)
import * as cheerio from 'cheerio';

// Loading HTML content
const $ = cheerio.load(htmlContent);

// Using CSS selectors
const text = $('p').text();  // Selects all <p> tags
const specific = $('.content p').text();  // More specific selection

فصل ۲: داده‌های ساخت‌یافته

وقتی وارد یک صفحهٔ ساختگی محصول در تجارت الکترونیک می‌شویم، اوضاع جذاب‌تر می‌شود. اینجا با چندین عنصر با ساختارهای مشابه روبه‌رو هستیم:

<!-- Example product structure (simplified) -->
<div class="product">
  <h2>Product Name</h2>
  <span class="price">$99.99</span>
  <div class="specs">
    <ul>
      <li>Size: M</li>
      <li>Color: Blue</li>
    </ul>
  </div>
</div>

هنگام کار با داده‌های ساخت‌یافته‌ای از این دست، بهتر است به این موارد فکر کنید:

// Conceptual approach (not the solution!)
$('.product').each((index, element) => {
  // For each product, we might want to:
  // 1. Extract the basic info
  const name = $(element).find('h2').text();

  // 2. Parse nested data
  const specs = $(element).find('.specs li');

  // 3. Structure the output
  const data = {
    name,
    specs: specs.map(/* ... */),
  };
});

نکتهٔ حرفه‌ای: پیش از نوشتن هر کدی، زمانی را به تحلیل ساختار HTML اختصاص دهید. به دنبال الگوها در نحوهٔ سازمان‌دهی داده‌ها بگردید — آیا نام کلاس‌ها یکدست هستند؟ عناصر والد و فرزند چه رابطه‌ای با هم دارند؟

فصل ۳: اسکرپینگ با کمک AI

حالا ماجرا جالب می‌شود! در حالی که چالش‌های قبلی به ما تکنیک‌های سنتی اسکرپینگ را یاد دادند، فصل ۳ یک رویکرد مدرن را معرفی می‌کند: وب‌اسکرپینگ با کمک AI. اینجا با یک سناریوی کابوس‌وار روبه‌رو هستیم — ساختارهای HTML ناسازگار، نام‌های کلاس مبهم‌سازی‌شده، و چندین الگوی مربوط به frameworkهای مختلف که همگی با هم مخلوط شده‌اند.

بیایید ببینیم چه چیزی این چالش را خاص می‌کند:

<!-- Traditional product structure -->
<div data-testid="product-container-1" class="_3xj_item">
  <h2 data-qa="name">Red Sneakers</h2>
  <span data-price-current="5999">$59.99</span>
</div>

<!-- React-style component -->
<div class="ProductCard-root-1a2b3c">
  <div class="ProductCard-title-4d5e6f">Pink Walking Shoes</div>
  <div class="ProductCard-pricing-7g8h9i">$84.99</div>
</div>

<!-- Vue-style template -->
<div data-v-abcdef class="product">
  <h2 data-v-abcdef>Navy Boat Shoes</h2>
  <span data-v-abcdef>$79.99</span>
</div>

رویکردهای سنتی اسکرپینگ اینجا به مشکل می‌خورند، چون:

  • نام کلاس‌ها تصادفی یا framework-محور هستند
  • ساختار داده‌ها بین محصولات متفاوت است
  • frameworkهای مختلف از الگوهای متفاوتی استفاده می‌کنند
  • معنای semantic در markup از بین می‌رود

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

  • prompt engineering برای وب‌اسکرپینگ
  • استفاده از AI برای درک معنای semantic
  • مدیریت ساختارهای دادهٔ ناسازگار
  • کنار آمدن با markup مخصوص frameworkها
  • حفظ سازگاری داده‌ها در میان الگوهای مختلف

هرچند AI جادو نیست، اما در کارهایی که به تشخیص الگو و انطباق‌پذیری نیاز دارند بسیار توانمند است. همین موضوع آن را برای اسکرپینگ برنامه‌های وب مدرن، جایی که نمی‌توان به الگوهای markup یکدست اطمینان داشت، به‌ویژه ارزشمند می‌کند.

نکته‌ای دربارهٔ استفاده از AI

به خاطر داشته باشید که کمک AI به معنی راه‌حل‌های کاملاً خودکار نیست. بهترین نتایج از ترکیب این موارد به دست می‌آیند:

  • تعریف روشن مسئله
  • promptهای خوش‌ساخت
  • اعتبارسنجی داده
  • نظارت انسانی

چالش شما این خواهد بود که promptهایی طراحی کنید که به AI کمک کنند هم ساختار و هم منظور چیزی را که می‌خواهید استخراج کنید، درک کند.

آماده‌اید دانش وب‌اسکرپینگ سنتی را با قابلیت‌های مدرن AI ترکیب کنید؟ بیایید بررسی کنیم AI چگونه می‌تواند به حل حتی آشفته‌ترین HTMLها کمک کند!

راهنمایی‌ها

  1. selectorهای CSS مختلف را امتحان کنید:
// Different ways to select elements
$('.class')           // By class
$('#id')             // By ID
$('div > p')         // Direct children
$('div p')           // All descendants
$('[data-type="x"]') // By attribute
  1. قالب خروجی را تغییر دهید و امتحان کنید
  2. به error handling و edge caseها فکر کنید
  3. در نظر بگیرید که راه‌حل شما با مجموعه‌داده‌های بزرگ‌تر چگونه مقیاس‌پذیر می‌شود

تمام کدی که برای شروع نیاز دارید در repository پروژه قرار دارد. آن را clone کنید، محیط خود را راه‌اندازی کنید، و اسکرپینگ را شروع کنید!

git clone https://github.com/jonaylor89/housefly.git
cd housefly
npm install

سرورهای فصل‌ها را به‌صورت محلی اجرا کنید — هر فصل روی پورت خودش اجرا می‌شود (3001–3011):

turbo dev

سپس scraper خود را در exercises/chapter-NN/starter/src/index.ts ویرایش کنید و آن را اعتبارسنجی کنید:

npm run validate -- <chapter>

دنبال راهنمایی هستید؟ npm run hint -- <chapter> را برای راهنمایی مرحله‌به‌مرحله اجرا کنید. HTML منبع هر چالش در پوشهٔ apps/chapter{n}/ در دسترس است و راه‌حل‌های مرجع در exercises/chapter-NN/solution/src/ قرار دارند. ساختار را بررسی کنید، رویکرد خود را برنامه‌ریزی کنید، و به یاد داشته باشید — هر متخصصی روزی یک مبتدی بوده است.

به خاطر داشته باشید که درخواست‌های خود را مسئولانه مدیریت کنید:

// Basic error handling example
async function fetchPage(url: string) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.text();
  } catch (error) {
    console.error('Failed to fetch page:', error);
    throw error;
  }
}

اسکرپینگ خوش بگذرد!