برنامه نویسی

ساختن یک اسکرابر وب برای اعتبار لینک: قسمت 2

مقدمه

خوش آمدید! در قسمت 1 ، ما یک اسکرابر وب ساده ساختیم که پیوندها را از یک صفحه واحد استخراج و تأیید می کند. اکنون ما آن را به سطح بعدی می بریم. در این آموزش ، ما اسکریپت وب ساخته شده در آموزش اول را می گیریم و موارد زیر را اضافه می کنیم:

  1. خزیدن بازگشتی: بنابراین می توانیم پیوندها را دنبال کنیم و ضایعات را ادامه دهیم.
  2. تنظیم عمق خزیدن: برای کنترل اینکه چگونه اسکرابر ما عمق دارد. (ما نمی توانیم اجازه دهیم که وجودی داشته باشد)
  3. اضافه کردن تأخیر: برای جلوگیری از چکش زدن به سرور.
  4. درخواست های دسته بندی: برای تأیید پیوندها در گروه ها برای عملکرد بهتر.

نوشیدنی مورد علاقه خود را بگیرید و اجازه دهید تا اسکرابر خود را تراز کنیم!

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

const puppeteer = require('puppeteer')

const crawl = async () => {
  // Add a headless option to the launch function. It makes the crawler run in the background.
  const browser = await puppeteer.launch({ headless: true })
  const page = await browser.newPage()

  // Add a waitUntil option to the goto function to wait for the page to load.
  await page.goto('https://example.com/', { waitUntil: 'networkidle2' })
  const links = await page.evaluate(() => {
    const anchors = Array.from(document.querySelectorAll('a'))
    return anchors.map((anchor) => anchor.href)
  })

  for (const link of links) {
    try {
      const res = await fetch(link)
      const message = `${res.ok ? '' : ''} - ${link}`
      console.log(message)
    } catch (error) {
      console.log('Error:', error)
    }
  }
  await browser.close()
}

crawl()
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

راه اندازی

اکنون ، قبل از اینکه واقعاً به این موضوع بپردازیم ، باید برای تست این اسکریپت یک تغییر کوچک در وب سایت مورد استفاده خود ایجاد کنیم. www.example.com فقط یک صفحه واحد دارد و فقط یک پیوند برای تأیید وجود دارد و ما برای آزمایش ویژگی های جدید خود به آن بیشتر نیاز داریم. بنابراین ، من یک وب سایت ساده با چندین صفحه و پیوند برای استفاده شما ایجاد کرده ام. می توانید آن را از این مخزن GitHub کلون کنید.

هنگامی که repo را کلون کردید ، فقط باید وابستگی ها را با استفاده از آن نصب کنید npm install یا yarn install و سرور را با شروع کنید npm run dev یا yarn devبشر وب سایت در دسترس خواهد بود http://localhost:3000بشر با پیشروی ، ما از این وب سایت برای آزمایش اسکرابر خود استفاده خواهیم کرد.

خزیدن بازگشتی و تنظیم عمق خزنده

اجرای خزیدن بازگشتی یک ویژگی قدرتمند است که به Scraper اجازه می دهد پیوندها را دنبال کند و چندین صفحه را خراش دهد. با این حال ، با قدرت عالی مسئولیت بزرگی به همراه دارد. ما باید راهی برای کنترل چگونگی عمق اسکرابر برای جلوگیری از گیر کردن در یک حلقه بی نهایت اجرا کنیم.

در این حالت ما در واقع از بازگشت استفاده نمی کنیم ، بلکه یک الگوریتم جستجوی عمق اول برای خزیدن در وب سایت است. ما این کار را با ایجاد یک انجام خواهیم داد toVisit مجموعه ای را که URL هایی را که باید بازدید کنند پیگیری می کنند و visited تنظیم شده برای پیگیری URL هایی که قبلاً بازدید شده اند.

سپس می توانیم بیش از toVisit در یک while حلقه کنید تا خالی شود. برای هر URL ، ما به صفحه مراجعه می کنیم ، پیوندها را استخراج می کنیم و آنها را به toVisit مجموعه ما همچنین اضافه خواهیم کرد depth پارامتر برای کنترل چقدر عمیق می خواهیم خزنده شویم. شما می توانید از عمق به عنوان تعداد کلیک دور از URL شروع کنید.

برای پیاده سازی این ویژگی ، باید چند تغییر در Scraper خود را معرفی کنیم.

  1. ما URL شروع و حداکثر عمق را به crawl عملکرد.
  2. برای پیگیری URL هایی که باید از آنها بازدید کنیم و مواردی که قبلاً از آنها بازدید کرده ایم ، برخی متغیرهای جدید را اضافه خواهیم کرد.
    1. ما با اضافه کردن یک عمق پیگیری خواهیم کرد depth خاصیت به هر URL در toVisit مجموعه
  3. ما اقدامات بازدید از یک صفحه ، استخراج پیوندها و اعتبار آنها را به حلقه ای که از طریق آن تکرار می شود حرکت خواهیم داد toVisit مجموعه
  4. پس از اعتبارسنجی هر لینک ، ما بررسی خواهیم کرد که آیا باید آن را به آن اضافه کنیم toVisit بر اساس عمق فعلی تنظیم شده است.

در اینجا به نظر می رسد که Scraper به روز شده ، من همچنین برای تجسم خزنده در هنگام اجرا ، ورود به سیستم مفیدی را اضافه کرده ام:

const puppeteer = require('puppeteer')

// Check if a link is already scheduled for a visit
const isScheduled = (links, link) => links.some((l) => l.url === link)

// 1. Add parameters for the starting URL and the maximum depth
const crawl = async (baseUrl = 'http://localhost:3000', maxDepth = 1) => {
  const browser = await puppeteer.launch({ headless: true })

  // 2. Initialize the "toVisit" and "visited" variables
  const toVisit = [{ url: baseUrl, depth: 0 }]
  const visited = new Set()

  // 3. Loop over the "toVisit" set until it's empty
  while (toVisit.length > 0) {
    // Grab the next URL to visit and add it to the visited set
    const { url, depth } = toVisit.pop()
    visited.add(url)

    const page = await browser.newPage()
    console.log('--------------------------------------')
    console.log(`Crawling: ${url}`)

    await page.goto(url, { waitUntil: 'networkidle2' })

    const links = await page.evaluate(() => {
      const anchors = Array.from(document.querySelectorAll('a'))
      return anchors.map((anchor) => anchor.href)
    })

    console.log(`Found ${links.length} links.`)

    for (const link of links) {
      try {
        const res = await fetch(link)
        const message = `${res.ok ? '' : ''} - ${link}`
        console.log(message)
      } catch (error) {
        console.log('Error:', error)
      }

      // 4. Check if the link should be added to "toVisit" based on the following:
      if (
        link.startsWith(baseUrl) && // If it's internal. Meaning it's from the same domain
        !isScheduled(links, link) && // If we haven't already added it to the stack
        !visited.has(link) && // If we haven't visited it already
        depth < maxDepth // If we haven't reached the maximum depth
      ) {
        console.log(`Adding "${link}" to stack.`)
        toVisit.push({ url: link, depth: depth + 1 })
      }
    }

    await page.close()
  }

  await browser.close()
}

crawl('http://localhost:3000', 1)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون وقتی خزنده را اجرا می کنید ، با فرض استفاده از سایت مثال ، باید چیزی شبیه به این را ببینید:

Crawling: http://localhost:3000
Found 4 links.
✅ - http://localhost:3000/
Adding "http://localhost:3000/" to stack.
✅ - http://localhost:3000/about
Adding "http://localhost:3000/about" to stack.
✅ - http://localhost:3000/contact
Adding "http://localhost:3000/contact" to stack.
✅ - http://localhost:3000/articles
Adding "http://localhost:3000/articles" to stack.
--------------------------------------
Crawling: http://localhost:3000/about
Found 5 links.
✅ - http://localhost:3000/
✅ - http://localhost:3000/about
✅ - http://localhost:3000/contact
✅ - http://localhost:3000/articles
❌ - http://localhost:3000/nowhere
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما می توانیم ببینیم که خزنده اکنون پیوندها را دنبال می کند و بر اساس عمق تنظیم شده ، آنها را به پشته اضافه می کند. سعی کنید تغییر دهید depth پارامتر برای دیدن نحوه رفتار خزنده. در درون هر article صفحه پیوند دیگری به نظرات وجود دارد. اگر تنظیم کنید maxDepth به 3، شما باید شاهد بازدید از خزنده باشید comments صفحه

با هشدار !!!

هنگام اجرای این اسکریپت مراقب باشید. اگر سعی می کنید آن را در وب سایت های بزرگ یا با بالایی اجرا کنید maxDepth ارزش شما می توانید در نهایت خزیدن هزاران صفحه و به طور بالقوه توسط وب سایت مسدود شوید. همیشه از تأثیر Scraper خود در وب سایتی که در حال خزیدن است ، توجه داشته باشید.

برای کسب اطلاعات بیشتر در مورد خراش وب اخلاقی ، این صفحه ویکی را در Robots.txt ببینید.

درخواست های دسته بندی و اضافه کردن تأخیر

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

برای دسته بندی ، ما پیوندهایی را به گروه هایی از n و آنها را با هم تأیید کنید. به این ترتیب ما درخواست های کمتری خواهیم کرد ، کارآمدتر خواهیم بود و بار روی سرور را کاهش می دهیم. ما همچنین تأخیر اضافه خواهیم کرد 1000ms بین هر دسته برای دادن اتاق تنفس به سرور.

یک عملکرد دسته بندی ساده می تواند به این شکل باشد:

const validateLinksInBatches = async (links, batchSize = 5) => {
  // Iterate over the list of links in batches of `batchSize`
  for (let i = 0; i < links.length; i += batchSize) {
    const batch = links.slice(i, i + batchSize)
    await Promise.all(batch.map((link) => validateLink(link)))
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در حال حاضر ، بیایید اندازه دسته بندی خود را روی 2 تنظیم کنیم و تأخیر را اضافه کنیم 1000ms بین هر دسته و هر صفحه بازدید کنید. ما همچنین باید اعتبار لینک را در یک عملکرد جداگانه تجزیه کنیم.

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

const validateLink = async (link) => {
  try {
    const res = await fetch(link)
    console.log(`${res.ok ? '' : ''} - ${link}`)
  } catch (error) {
    console.log('Error:', error)
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

قرار دادن همه اینها

در زیر یک مثال کامل وجود دارد که ترکیبی از خزیدن بازگشتی ، کنترل عمق ، تأخیر بین درخواست ها و دسته بندی است. توجه کنید که همه چیز ماژولار است ، و تنظیم اجزای فردی در صورت لزوم آسان تر است:

const puppeteer = require('puppeteer')

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

const validateLink = async (link) => {
  try {
    const res = await fetch(link)
    console.log(`${res.ok ? '' : ''} - ${link}`)
  } catch (error) {
    console.log('Error:', error)
  }
}

const validateLinksInBatches = async (links, batchSize = 5) => {
  for (let i = 0; i < links.length; i += batchSize) {
    const batch = links.slice(i, i + batchSize)
    console.log(`Validating ${batch.length} links...`)
    await delay(1000)
    await Promise.all(batch.map((link) => validateLink(link)))
  }
}

// Check if a link is already scheduled for a visit
const isScheduled = (links, link) => links.some((l) => l.url === link)

const crawl = async (baseUrl = 'http://localhost:3000', maxDepth = 1) => {
  const browser = await puppeteer.launch({ headless: true })

  const toVisit = [{ url: baseUrl, depth: 0 }]
  const visited = new Set()

  while (toVisit.length > 0) {
    // Grab the next URL to visit and add it to the visited set
    const { url, depth } = toVisit.pop()
    visited.add(url)

    const page = await browser.newPage()
    console.log('--------------------------------------')
    console.log(`Crawling: ${url}`)

    await page.goto(url, { waitUntil: 'networkidle2' })

    const links = await page.evaluate(() => {
      const anchors = Array.from(document.querySelectorAll('a'))
      return anchors.map((anchor) => anchor.href)
    })

    console.log(`Found ${links.length} links.`)

    await validateLinksInBatches(links, 2)
    await delay(1000)

    for (const link of links) {
      if (
        link.startsWith(baseUrl) &&
        !isScheduled(link) &&
        !visited.has(link) &&
        depth < maxDepth
      ) {
        console.log(`Adding "${link}" to stack.`)
        toVisit.push({ url: link, depth: depth + 1 })
      }
    }

    await page.close()
  }

  await browser.close()
}

crawl('http://localhost:3000', 1)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون ، هنگامی که خزنده را اجرا می کنید ، متوجه می شوید که اکنون علاوه بر انتظار یک ثانیه اضافی بین دسته ها و بعد از هر بازدید از صفحه ، پیوندهایی را در دسته های 2 تأیید می کند. تبریک می گویم! شما به تازگی یک اسکرابر وب قوی تر ساخته اید که می تواند چندین صفحه ، عمق کنترل را خزیده و پیوندها را به طور کارآمد اعتبار دهد. چه چیزی بعدی؟ بیایید این را بپیچیم.

پایان

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

احساس راحتی کنید که با کد آزمایش کنید – ممکن است ورود به خطای خطا را اضافه کنید ، تأخیرها را بر اساس پاسخ سرور سفارشی کنید. اسکرابر شما اکنون پایه و اساس محکمی برای ساختن است!

در اینجا برخی از الهامات برای پیشرفت های بیشتر آورده شده است:

  • نتایج را به صورت سازمان یافته در یک پرونده ذخیره کنید.
  • ایجاد شده validated تنظیم کنید تا از اعتبار مجدد همان پیوندها جلوگیری شود.
  • یک مکانیزم آزمایش مجدد را برای درخواست های ناموفق اجرا کنید.
  • پردازش موازی را با محدودیت های همزمانی اضافه کنید.

برنامه نویسی مبارک ، و ممکن است پیوندهای شما همیشه اعتبار یابد! اگر سوالی دارید یا به ترفندهای بیشتری احتیاج دارید ، فقط فریاد بزنید. من اینجا هستم تا به شما در بهبود و پالایش مهارت های خود کمک کنم.

شاخه

از محتوای من لذت می برید؟ می توانید اطلاعات بیشتری را در وبلاگ من در The Glitched Goblet بخوانید یا مرا در Bluesky در Kaemonisland.bsky.social دنبال کنید. من هر هفته پست های قدیمی تر و اضافه کردن موارد جدید را به روز می کنم ، بنابراین حتماً اغلب بررسی کنید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا