اگر وب سایتی نتواند یک API خوب ارائه دهد، بهترین شرط بعدی شما حذف محتوای آن است. Cheerio و Express.js به شما در انجام این کار کمک می کنند.
Web scraping تکنیکی است که به دست آوردن داده ها از یک وب سایت خاص را ممکن می سازد. وب سایت ها از HTML برای توصیف محتوای خود استفاده می کنند. اگر HTML تمیز و معنایی باشد، استفاده از آن برای مکان یابی داده های مفید آسان است.
شما معمولاً از یک اسکراپر وب برای به دست آوردن و نظارت بر داده ها و ردیابی تغییرات آتی آن استفاده می کنید.
مفاهیم jQuery ارزش دانستن قبل از استفاده از Cheerio را دارد
jQuery یکی از محبوب ترین بسته های جاوا اسکریپت موجود است. کار با Document Object Model (DOM)، مدیریت رویدادها، انیمیشنها و موارد دیگر را آسانتر میکند. Cheerio بستهای برای اسکراپینگ وب است که بر روی jQuery ساخته میشود – نحو و API یکسان را به اشتراک میگذارد، در حالی که تجزیه اسناد HTML یا XML را آسانتر میکند.
قبل از اینکه یاد بگیرید چگونه از Cheerio استفاده کنید، مهم است که بدانید چگونه عناصر HTML را با jQuery انتخاب کنید. خوشبختانه، jQuery از اکثر انتخابگرهای CSS3 پشتیبانی می کند که گرفتن عناصر از DOM را آسان تر می کند. به کد زیر دقت کنید:
$("#container");
در بلوک کد بالا، jQuery عناصر را با شناسه “container” انتخاب می کند. یک پیاده سازی مشابه با استفاده از جاوا اسکریپت معمولی قدیمی چیزی شبیه به این خواهد بود:
document.querySelectorAll("#container");
با مقایسه دو بلوک کد آخر، می توانید ببینید بلوک کد قبلی بسیار راحت تر از دومی خوانده می شود. این زیبایی جی کوئری است.
جی کوئری همچنین متدهای مفیدی مانند text()، html() و موارد دیگر دارد که دستکاری عناصر HTML را ممکن میسازد. چندین روش وجود دارد که می توانید برای عبور از DOM از آنها استفاده کنید، مانند parent()، siblings()، prev() و next().
متد every() در jQuery در بسیاری از پروژه های Cheerio بسیار محبوب است. به شما امکان می دهد روی اشیا و آرایه ها تکرار کنید. سینتکس متد هر() به شکل زیر است:
$(<element>).each(<array or object>, callback)
در بلوک کد بالا، callback برای هر تکرار آرایه یا آرگومان شی اجرا می شود.
بارگیری HTML با Cheerio
برای شروع تجزیه داده های HTML یا XML با Cheerio، می توانید از متد ()cheerio.load استفاده کنید. به این مثال توجه کنید:
const $ = cheerio.load('<html><body><h1>Hello, world!</h1></body></html>');
console.log($('h1').text())
این بلوک کد از متد jQuery text() استفاده می کند و محتوای متن عنصر h1 را بازیابی می کند. سینتکس کامل متد load() به شکل زیر است:
load(content, options, mode)
پارامتر محتوا به داده های واقعی HTML یا XML اشاره دارد که از روش load() عبور می کنید. گزینه ها یک شی اختیاری است که می تواند رفتار متد را تغییر دهد. بهطور پیشفرض، متد load() عناصر html، head و body را در صورت عدم وجود آنها معرفی میکند. اگر می خواهید این رفتار را متوقف کنید، مطمئن شوید که حالت را روی false تنظیم کرده اید.
خراش دادن اخبار هکرها با Cheerio
کد مورد استفاده در این پروژه در یک مخزن GitHub موجود است و برای استفاده شما تحت مجوز MIT رایگان است.
وقت آن است که همه چیزهایی را که تاکنون آموخته اید ترکیب کنید و یک وب اسکراپر ساده بسازید. Hacker News یک وب سایت محبوب برای کارآفرینان و نوآوران است. همچنین یک وب سایت عالی برای استفاده از مهارت های خراش دادن وب شما است زیرا سریع بارگیری می شود، رابط کاربری بسیار ساده ای دارد و هیچ تبلیغی را ارائه نمی دهد.
مطمئن شوید که Node.js و Node Package Manager روی دستگاه شما اجرا شده است. یک پوشه خالی و سپس یک فایل package.json ایجاد کنید و JSON زیر را داخل فایل اضافه کنید:
{
"name": "web-scraper",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon index.js"
},
"author": "",
"license": "MIT",
"dependencies": {
"cheerio": "^1.0.0-rc.12",
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
پس از انجام این کار، ترمینال را باز کرده و اجرا کنید:
npm i
این باید وابستگی های لازم برای ساخت اسکراپر را نصب کند. این بسته ها شامل Cheerio برای تجزیه HTML، ExpressJS برای ایجاد سرور، و – به عنوان یک وابستگی توسعه – Nodemon، ابزاری است که به تغییرات پروژه گوش می دهد و به طور خودکار سرور را راه اندازی مجدد می کند.
تنظیم چیزها و ایجاد توابع لازم
یک فایل index.js ایجاد کنید و در آن فایل یک متغیر ثابت به نام PORT ایجاد کنید. PORT را روی 5500 (یا هر عددی که انتخاب می کنید) تنظیم کنید، سپس بسته های Cheerio و Express را به ترتیب وارد کنید.
const PORT = 5500;
const cheerio = require("cheerio");
const express = require("express");
const app = express();
سپس سه متغیر url، html و finishedPage را تعریف کنید. url را روی URL Hacker News تنظیم کنید.
const url = 'https://news.ycombinator.com';
let html;
let finishedPage;
اکنون تابعی به نام getHeader() ایجاد کنید که مقداری HTML را که مرورگر باید رندر کند برمی گرداند.
function getHeader(){
return `
<div style="display:flex; flex-direction:column; align-items:center;">
<h1 style="text-transform:capitalize">Scraper News</h1>
<div style="display:flex; gap:10px; align-items:center;">
<a href="/" id="news" onClick='showLoading()'>Home</a>
<a href="/best" id="best" onClick='showLoading()'>Best</a>
<a href="/newest" id="newest" onClick='showLoading()'>Newest</a>
<a href="/ask" id="ask" onClick='showLoading()'>Ask</a>
<a href="/jobs" id="jobs" onClick='showLoading()'>Jobs</a>
</div>
<p class="loading" style="display:none;">Loading...</p>
</div>
`}
یک تابع دیگر ()getScript ایجاد کنید که مقداری جاوا اسکریپت را برای اجرای مرورگر برمی گرداند. مطمئن شوید که هنگام فراخوانی متغیر نوع آن را به عنوان آرگومان ارسال می کنید.
function getScript(type){
return `
<script>
document.title = "${type.substring(1)}"
window.addEventListener("DOMContentLoaded", (e) => {
let navLinks = [...document.querySelectorAll("a")];
let current = document.querySelector("#${type.substring(1)}");
document.body.style = "margin:0 auto; max-width:600px;";
navLinks.forEach(x => x.style = "color:black; text-decoration:none;");
current.style.textDecoration = "underline";
current.style.color = "black";
current.style.padding = "3px";
current.style.pointerEvents = "none";
})
function showLoading(e){
document.querySelector(".loading").style.display = "block";
document.querySelector(".loading").style.textAlign = "center";
}
</script>`
}
در نهایت، یک تابع ناهمزمان به نام ()fetchAndRenderPage ایجاد کنید. این تابع دقیقاً همان کاری را انجام می دهد که شما فکر می کنید – صفحه ای را در Hacker News خراش می دهد، آن را با Cheerio تجزیه و قالب بندی می کند، سپس مقداری HTML را برای رندر به مشتری می فرستد.
async function fetchAndRenderPage(type, res) {
const response = await fetch(`${url}${type}`)
html = await response.text();
}
در هکر نیوز، انواع مختلفی از پست ها موجود است. “اخبار” وجود دارد، که مواردی است که در صفحه اول وجود دارد، پست هایی که به دنبال پاسخ از سایر اعضای هکر نیوز هستند دارای برچسب “پرسیدن” هستند. پست های پرطرفدار دارای برچسب “بهترین”، آخرین پست ها دارای برچسب “جدیدترین” و پست های مربوط به فرصت های شغلی دارای برچسب “شغل” هستند.
fetchAndRenderPage () لیستی از پستها را از صفحه Hacker News بر اساس نوع ارسالی که به عنوان آرگومان ارسال میکنید واکشی میکند. اگر عملیات واکشی موفقیت آمیز باشد، تابع متغیر html را به متن پاسخ متصل می کند.
سپس خطوط زیر را به تابع اضافه کنید:
res.set('Content-Type', 'text/html');
res.write(getHeader());
const $ = cheerio.load(html);
const articles = [];
let i = 1;
در بلوک کد بالا، متد set() فیلد هدر HTTP را تنظیم می کند. متد write() مسئول ارسال یک تکه از بدنه پاسخ است. تابع load() در html به عنوان آرگومان می گیرد.
سپس، خطوط زیر را اضافه کنید تا فرزندان مربوطه از همه عناصر با کلاس “Titleline” انتخاب شوند.
$('.titleline').children('a').each(function(){
let title = $(this).text();
articles.push(`<h4>${i}. ${title}</h4>`);
i++;
})
در این بلوک کد، هر تکرار محتوای متن عنصر HTML هدف را بازیابی می کند و آن را در متغیر عنوان ذخیره می کند.
سپس، پاسخ تابع getScript() را به آرایه مقالات فشار دهید. سپس یک متغیر با نام finishedPage ایجاد کنید که HTML تمام شده را برای ارسال به مرورگر نگه می دارد. در نهایت، از متد write() برای ارسال finishedPage به عنوان یک تکه استفاده کنید و فرآیند پاسخ را با متد end() پایان دهید.
articles.push(getScript(type))
finishedPage = articles.reduce((c, n) => c + n);
res.write(finishedPage);
res.end();
تعریف مسیرهایی برای رسیدگی به درخواست های GET
درست در زیر تابع fetchAndRenderPage، از متد express get() برای تعریف مسیرهای مربوطه برای انواع مختلف پست استفاده کنید. سپس از روش listen برای گوش دادن به اتصالات به پورت مشخص شده در شبکه محلی خود استفاده کنید.
app.get('/', (req, res) => {
fetchAndRenderPage('/news', res);
})
app.get('/best', (req, res) => {
fetchAndRenderPage('/best', res);
})
app.get('/newest', (req, res) => {
fetchAndRenderPage('/newest', res);
})
app.get('/ask', (req, res) => {
fetchAndRenderPage('/ask', res);
})
app.get('/jobs', (req, res) => {
fetchAndRenderPage('/jobs', res);
})
app.listen(PORT)
در بلوک کد بالا، هر متد get یک تابع callback دارد که تابع fetchAndRenderPage را که در انواع مربوطه و اشیاء res ارسال میشود، فراخوانی میکند.
هنگامی که ترمینال خود را باز می کنید و npm را اجرا می کنید، اجرا شروع می شود. سرور باید راه اندازی شود، سپس می توانید از localhost:5500 در مرورگر خود بازدید کنید تا نتایج را ببینید.
تبریک میگوییم، شما به تازگی موفق به حذف Hacker News و دریافت عناوین پستها بدون نیاز به API خارجی شدهاید.
با Web Scraping کارها را بیشتر کنید
با دادههایی که از Hacker News جمعآوری میکنید، میتوانید تجسمهای مختلفی مانند نمودارها، نمودارها و ابرهای کلمه ایجاد کنید تا بینشها و روندها را در قالبی قابل هضمتر ارائه دهید.
همچنین میتوانید پروفایلهای کاربر را برای تجزیه و تحلیل شهرت کاربران در پلتفرم بر اساس عواملی مانند رأیهای مثبت دریافتشده، نظرات دادهشده و موارد دیگر، خراش دهید.