Tin tức và phân tích của tất cả các thiết bị di động

Giới thiệu về quét web với Cheerio

Quét web là một kỹ thuật cho phép bạn lấy dữ liệu từ một trang web cụ thể. Các trang web sử dụng HTML để mô tả nội dung của chúng. Nếu mã HTML rõ ràng và có ngữ nghĩa thì bạn có thể dễ dàng sử dụng nó để định vị dữ liệu hữu ích.

Thông thường, bạn sẽ sử dụng trình quét web để thu thập và giám sát dữ liệu cũng như theo dõi các thay đổi trong tương lai đối với dữ liệu đó.

Các khái niệm jQuery bạn nên biết trước khi sử dụng Cheerio

jQuery là một trong những gói JavaScript phổ biến nhất hiện nay. Giúp bạn dễ dàng làm việc với Mô hình đối tượng tài liệu (DOM), xử lý sự kiện, hoạt ảnh, v.v. Cheerio là một gói quét web dựa trên jQuery – nó có chung cú pháp và API, đồng thời giúp phân tích cú pháp các tài liệu HTML hoặc XML dễ dàng hơn.

Trước khi tìm hiểu cách sử dụng Cheerio, điều quan trọng là phải biết cách chọn các phần tử HTML bằng jQuery. May mắn thay, jQuery hỗ trợ hầu hết các bộ chọn CSS3, giúp dễ dàng truy xuất các phần tử từ DOM. Nhìn vào đoạn mã sau:

 $("#container");

Trong khối mã ở trên, jQuery chọn các phần tử có ID ‘container’. Việc triển khai tương tự bằng cách sử dụng JavaScript cũ sẽ trông giống như thế này:

 document.querySelectorAll("#container");

So sánh hai khối mã cuối cùng, bạn có thể thấy khối mã trước dễ đọc hơn khối mã thứ hai nhiều. Đó là vẻ đẹp của jQuery.

jQuery cũng có các phương thức hữu ích như text(), html() và các phương thức khác cho phép bạn thao tác với các phần tử HTML. Có một số phương pháp bạn có thể sử dụng để duyệt qua DOM, chẳng hạn như parent(), anh chị em(), prev() và next().

Phương thức each() trong jQuery rất phổ biến trong nhiều dự án Cheerio. Nó cho phép bạn lặp lại các đối tượng và mảng. Cú pháp của phương thức each() như sau:

 $(<element>).each(<array or object>, callback)

Trong khối mã ở trên, lệnh gọi lại được kích hoạt cho mỗi lần lặp của đối số mảng hoặc đối tượng.

Đang tải HTML bằng Cheerio

Để bắt đầu phân tích dữ liệu HTML hoặc XML bằng Cheerio, bạn có thể sử dụng phương thức cổ vũ.load(). Nhìn vào ví dụ này:

 const $ = cheerio.load('<html><body><h1>Hello, world!</h1></body></html>');
console.log($('h1').text())

Khối mã này sử dụng phương thức jQuery text() để truy xuất nội dung văn bản của phần tử h1. Cú pháp đầy đủ của phương thức Load() như sau:

 load(content, options, mode)

Tham số nội dung đề cập đến dữ liệu HTML hoặc XML thực tế được truyền cho phương thức Load(). tùy chọn là một đối tượng tùy chọn có thể sửa đổi hành vi của phương thức. Theo mặc định, phương thức Load() sẽ chèn các phần tử html, head và body nếu chúng bị thiếu. Nếu bạn muốn dừng hành vi này, hãy đảm bảo bạn đặt chế độ thành sai.

Quét tin nhắn của hacker bằng Cheerio

Mã được sử dụng trong dự án này có sẵn ở định dạng Kho lưu trữ GitHub và được sử dụng miễn phí theo Giấy phép MIT.

Đã đến lúc kết hợp mọi thứ bạn đã học được cho đến nay và tạo một công cụ quét web đơn giản. Hacker News là một trang web phổ biến dành cho các doanh nhân và nhà đổi mới. Đây cũng là một trang web lý tưởng nơi bạn có thể sử dụng kỹ năng duyệt web của mình vì nó tải nhanh, có giao diện rất đơn giản và không hiển thị bất kỳ quảng cáo nào.

Đảm bảo Node.js và Trình quản lý gói Node đang chạy trên máy tính của bạn. Tạo một thư mục trống, sau đó pack.json và thêm JSON sau vào tệp:

 {
  "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"
  }
}

Khi bạn đã hoàn thành việc đó, hãy mở một terminal và chạy:

 npm i

Điều này sẽ cài đặt các phần phụ thuộc cần thiết để xây dựng bộ cạp. Các gói này bao gồm Cheerio để phân tích cú pháp HTML, ExpressJS để xây dựng máy chủ và, như một phần phụ thuộc phát triển, Nodemon, một công cụ lắng nghe các thay đổi thiết kế và tự động khởi động lại máy chủ.

Thiết lập mọi thứ và tạo các chức năng cần thiết

Tạo một tệp index.js và trong tệp đó tạo một biến không đổi có tên là “PORT”. Đặt PORT thành 5500 (hoặc bất kỳ số nào bạn chọn), sau đó nhập gói Cheerio và Express tương ứng.

 const PORT = 5500;
const cheerio = require("cheerio");
const express = require("express");
const app = express();

Sau đó xác định ba biến: url, html và ReadyPage. Đặt URL thành URL Tin tức Hacker.

 const url="https://news.ycombinator.com";
let html;
let finishedPage;

Bây giờ hãy tạo một hàm có tên getHeader() trả về HTML mà trình duyệt sẽ hiển thị.

 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="https://www.makeuseof.com/" id="news" onClick='showLoading()'>Home</a>
        <a href="https://wilku.top/best" id="best" onClick='showLoading()'>Best</a>
        <a href="https://wilku.top/newest" id="newest" onClick='showLoading()'>Newest</a>
        <a href="https://wilku.top/ask" id="ask" onClick='showLoading()'>Ask</a>
        <a href="https://wilku.top/jobs" id="jobs" onClick='showLoading()'>Jobs</a>
        </div>
        <p class="loading" style="display:none;">Loading...</p>
        </div>
`}

Tạo một hàm getScript() khác trả về một số mã JavaScript để khởi chạy trình duyệt. Đảm bảo bạn chuyển loại biến làm đối số khi gọi nó.

 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>`
}

Cuối cùng, tạo một hàm không đồng bộ có tên là getAndRenderPage(). Tính năng này thực hiện chính xác những gì bạn đang nghĩ – nó loại bỏ một trang trong Hacker News, phân tích cú pháp và định dạng nó bằng Cheerio, sau đó gửi HTML trở lại máy khách để hiển thị.

 async function fetchAndRenderPage(type, res) {
    const response = await fetch(`${url}${type}`)
    html = await response.text();
}

Có nhiều loại bài viết khác nhau trên Hacker News. Có những “tin tức” được đưa lên trang nhất, những bài đăng tìm kiếm câu trả lời từ các thành viên khác của Hacker News đều được dán nhãn “hỏi”. Các bài đăng phổ biến được gắn nhãn “hàng đầu”, các bài đăng mới nhất được gắn nhãn “mới nhất” và các bài đăng về vị trí tuyển dụng được gắn nhãn “việc làm”.

FetchAndRenderPage() tìm nạp danh sách các bài đăng từ trang Hacker News dựa trên loại được đưa ra làm đối số. Nếu thao tác tìm nạp thành công, hàm sẽ liên kết biến html với văn bản phản hồi.

Sau đó thêm các dòng sau vào hàm:

 res.set('Content-Type', 'text/html');
res.write(getHeader());

const $ = cheerio.load(html);
const articles = [];
let i = 1;

Trong khối mã ở trên, phương thức set() đặt trường tiêu đề HTTP. Phương thức write() chịu trách nhiệm gửi một đoạn nội dung phản hồi. Hàm Load() lấy html làm đối số.

Sau đó thêm các dòng sau để chọn các mục con thích hợp của tất cả các mục có lớp ‘title’.

 $('.titleline').children('a').each(function(){
    let title = $(this).text();
    articles.push(`<h4>${i}. ${title}</h4>`);
    i++;
})

Trong khối mã này, mỗi lần lặp lại sẽ lấy nội dung văn bản của phần tử HTML đích và lưu nó vào biến tiêu đề.

Sau đó đặt phản hồi từ getScript() vào mảng bài viết. Sau đó, tạo một biến ReadyPage sẽ chứa HTML đã hoàn thành để gửi tới trình duyệt. Cuối cùng, sử dụng phương thức write() để gửi finishPage dưới dạng một đoạn và kết thúc quá trình phản hồi bằng phương thức end().

 articles.push(getScript(type))
finishedPage = articles.reduce((c, n) => c + n);
res.write(finishedPage);
res.end();

Xác định các tuyến đường để xử lý các yêu cầu GET

Ngay bên dưới hàm getAndRenderPage, hãy sử dụng phương thức express get() để xác định các tuyến thích hợp cho các loại bài đăng khác nhau. Sau đó sử dụng phương pháp nghe để lắng nghe các kết nối đến một cổng cụ thể trên mạng cục bộ của bạn.

 app.get("https://www.makeuseof.com/", (req, res) => {
    fetchAndRenderPage('/news', res);
})

app.get("https://wilku.top/best", (req, res) => {
    fetchAndRenderPage("https://wilku.top/best", res);
})

app.get("https://wilku.top/newest", (req, res) => {
    fetchAndRenderPage("https://wilku.top/newest", res);
})

app.get("https://wilku.top/ask", (req, res) => {
    fetchAndRenderPage("https://wilku.top/ask", res);
})

app.get("https://wilku.top/jobs", (req, res) => {
    fetchAndRenderPage("https://wilku.top/jobs", res);
})

app.listen(PORT)

Trong khối mã ở trên, mỗi phương thức get có một hàm gọi lại gọi hàm getAndRenderPage, truyền vào các loại và đối tượng res thích hợp.

Khi bạn mở terminal và chạy npm run start. Máy chủ sẽ khởi động, sau đó bạn có thể truy cập localhost:5500 trong trình duyệt của mình để xem kết quả.

Xin chúc mừng, bạn vừa loại bỏ Hacker News và tìm nạp tiêu đề bài đăng mà không cần API bên ngoài.

Tiến xa hơn với việc quét web

Với dữ liệu được thu thập từ Hacker News, bạn có thể tạo nhiều hình ảnh trực quan khác nhau như biểu đồ, đồ thị và đám mây từ để trình bày thông tin chi tiết và xu hướng ở định dạng dễ tiếp cận hơn.

Bạn cũng có thể thu thập hồ sơ người dùng để phân tích danh tiếng của người dùng trên nền tảng dựa trên các yếu tố như số phiếu tích cực nhận được, nhận xét được thêm vào, v.v.