Web scraping đang tự động hóa quá trình thu thập dữ liệu từ web. Thông thường, điều này có nghĩa triển khai một “crawler” tự động tìm kiếm web và thu thập dữ liệu từ các trang được chọn. Việc thu thập dữ liệu thông qua scraping có thể nhanh hơn nhiều, loại bỏ nhu cầu thu thập dữ liệu thủ công, và có thể bắt buộc nếu trang web không cung cấp API. Phương pháp scraping thay đổi dựa trên cơ chế hiển thị dữ liệu của trang web.
Một cách hiển thị nội dung khác là thông qua một trang web duy nhất, còn được gọi là ứng dụng trang duy nhất. Ứng dụng trang duy nhất (SPA) đã trở thành một xu hướng, và với việc triển khai các kỹ thuật cuộn vô tận, các lập trình viên có thể phát triển SPA cho phép người dùng cuộn vô tận. Nếu bạn là người dùng mạng xã hội nhiệt tình, bạn có khả năng đã trải nghiệm tính năng này trước đây trên các nền tảng như Instagram, Twitter, Facebook, Pinterest, v.v.
Một trang web duy nhất có lợi cho trải nghiệm người dùng (UX), nhưng nó có thể làm cho việc cố gắng trích xuất dữ liệu của bạn trở nên phức tạp hơn. Nhưng không cần lo lắng, vì nhờ Puppeteer, bạn sẽ có khả năng thu thập dữ liệu vô tận vào cuối bài viết này.
Tiền đề & Mục tiêu
Để tận dụng đầy đủ bài viết này, bạn nên có những điều sau đây:
✅ Một chút kinh nghiệm với việc viết Javascript ES6.
✅ Hiểu rõ về promises và có một chút kinh nghiệm với async/await.
✅ Đã cài đặt Node.js trên máy phát triển của bạn.
Scraping Vô tận là gì?
Trước khi bạn cố gắng thu thập dữ liệu từ một dòng thời gian không bao giờ kết thúc, điều quan trọng là tự hỏi, scraping vô tận là gì?
Infinite Scrolling, một kỹ thuật thiết kế web cho phép tải nội dung liên tục khi người dùng cuộn xuống trang. Có một plugin JavaScript Infinite Scroll tự động thêm trang tiếp theo, ngăn chặn việc tải trang đầy đủ. Phiên bản đầu tiên được tạo ra vào năm 2008 bởi Paul Irish và đã là một bước đột phá trong phát triển web. Plugin này sử dụng ajax để tiền thân nội dung từ trang tiếp theo và sau đó thêm nó trực tiếp vào trang hiện tại. Có nhiều cách khác để tạo ra nội dung cuộn vô tận, như dữ liệu được cung cấp thông qua các điểm cuối API để từ từ cung cấp thêm dữ liệu, xử lý dữ liệu từ nhiều điểm cuối trước khi chèn vào trang web, hoặc cung cấp dữ liệu trong thời gian thực thông qua WebSockets.
Ưu điểm ✨
. Ứng dụng Khám phá
* Đây gần như là một tính năng bắt buộc cho các ứng dụng/giao diện khám phá. Nếu người dùng không biết họ nên tìm kiếm cái gì cụ thể, họ có thể cần xem một lượng lớn các mục để tìm thứ họ thích.
. Thiết bị Di động
* Vì thiết bị di động có kích thước màn hình nhỏ hơn nhiều, cuộn vô tận có thể tạo ra trải nghiệm người dùng thú vị hơn nhiều.
. Tương tác của Người dùng
* Vì kết quả mới luôn luôn được tải lên trang, người dùng sẽ bị cuốn vào ứng dụng.
Nhược điểm ⛔
. Kém hiệu suất trang
* Việc tải trang quan trọng đối với trải nghiệm người dùng. Khi người dùng cuộn xuống trang, nhiều nội dung phải tải lên cùng một trang. Kết quả là hiệu suất trang sẽ trở nên chậm hơn dần.
. Kém cho việc Tìm kiếm Mục và Vị trí
* Người dùng có thể đến một điểm cụ thể trong dòng thời gian mà họ không thể đánh dấu vị trí của họ. Nếu họ rời khỏi trang web, họ sẽ mất toàn bộ tiến trình của họ, làm giảm trải nghiệm người dùng.
. Mất đi phần cuối trang
* Một phần hữu ích của các ứng dụng có thể chứa thông tin quan trọng dễ dàng truy cập đã biến mất.
Bây giờ bạn đã biết thêm một chút về kiểu trình bày nội dung và các ứng dụng phát triển của nó, bạn có thể hiểu rõ hơn về cách thu thập dữ liệu từ các giao diện cuộn vô tận. Đó là nơi Puppeteer đến sự giúp đỡ.
Puppeteer là gì?
Việc hiểu và đảo ngược cách cung cấp dữ liệu của ứng dụng có thể mất thời gian, và các trang web có thể tiếp cận các phương pháp khác nhau để tạo nội dung cuộn vô tận. Tuy nhiên, bạn sẽ không cần phải lo lắng về điều đó ngày hôm nay, tất cả nhờ Puppeteer. Và không, không phải là loại làm việc với búp bê. Puppeteer là một API Node Chrome không giao diện, cho phép bạn mô phỏng việc cuộn trang và thu thập dữ liệu cần thiết từ các phần tử đã được hiển thị.
Puppeteer cho phép bạn hoạt động gần như chính xác như khi bạn sử dụng trình duyệt thông thường của bạn, ngoại trừ việc lập trình và không cần giao diện người dùng. Dưới đây là một số ví dụ về những gì bạn có thể làm:
- Tạo ảnh chụp màn hình và PDF của các trang.
- Cuộn cuộc sống và tạo nội dung đã được tạo trước.
- Automate form submission, kiểm tra giao diện người dùng, nhập bàn phím, v.v.
- Tạo môi trường kiểm tra tự động hiện đại – Chạy kiểm tra của bạn trực tiếp trong phiên bản mới nhất của Chrome sử dụng JavaScript và các tính năng trình duyệt mới nhất.
- Chụp một timeline trace của trang web của bạn để giúp chẩn đoán vấn đề về hiệu suất.
- Kiểm tra tiện ích mở rộng Chrome.
Bạn có thể tìm thấy danh sách này, cùng với thông tin bổ sung về cách sử dụng Puppeteer, trong documentation.
Cách Thu Thập Dữ Liệu từ Các Trang Web Cuộn Vô Tận Bằng Puppeteer
Giả sử bạn đã có [npm](https://www.npmjs.com/)
đã được cài đặt, hãy tạo một thư mục để lưu trữ dự án Puppeteer của bạn.
mkdir infinite-scroll
cd infinite-scroll
npm install --save puppeteer
Bằng cách sử dụng npm
, bạn đang cài đặt cả Puppeteer và một phiên bản trình duyệt Chromium được sử dụng bởi Puppeteer. Trên máy tính Linux, Puppeteer có thể cần một số phụ thuộc bổ sung. Điều này giúp bạn tiết kiệm thời gian bằng cách bắt đầu viết kịch bản scraping. Hãy mở trình soạn thảo văn bản ưa thích của bạn và tạo một tệp scrape-infinite-scroll.js
. Trong tệp đó, sao chép mã sau đây:
// Puppeteer will not run without these lines
const fs = require('fs');
const puppeteer = require('puppeteer');
Những dòng mã đầu tiên này là cấu hình mã mẫu. Bạn sẽ tạo một hàm cho các mục bạn muốn thu thập. Mở cửa sổ console của bạn và xem xét mã HTML trang để xác định hằng số extractedElements của bạn.
function extractItems() {
/* For extractedElements, you are selecting the tag and class,
that holds your desired information,
then choosing the disired child element you would like to scrape from.
in this case, you are selecting the
"<div class=blog-post />" from "<div class=container />" See below: */
const extractedElements = document.querySelectorAll('#container > div.blog-post');
const items = [];
for (let element of extractedElements) {
items.push(element.innerText);
}
return items;
}
Hàm tiếp theo được gọi là scrapeItems
. Hàm này điều khiển việc cuộn và trích xuất thực tế, bằng cách sử dụng page.evaluate
để lặp đi lặp lại cuộn xuống từng trang, trích xuất bất kỳ mục nào từ phương thức extractItems được chèn vào, cho đến khi ít nhất là itemCount
mục đã được thu thập. Vì các phương thức của Puppeteer dựa trên Promise, bằng cách sử dụng await
và đặt tất cả vào một bọc async
, bạn có thể viết mã như nó đang thực thi đồng bộ.
async function scrapeItems(
page,
extractItems,
itemCount,
scrollDelay = 800,
) {
let items = [];
try {
let previousHeight;
while (items.length < itemCount) {
items = await page.evaluate(extractItems);
previousHeight = await page.evaluate('document.body.scrollHeight');
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
await page.waitForFunction(`document.body.scrollHeight > ${previousHeight}`);
await page.waitForTimeout(scrollDelay);
}
} catch(e) { }
return items;
}
Phần mã cuối cùng này xử lý khởi động và điều hướng đến trình duyệt Chromium, cũng như số lượng mục bạn đang thu thập và nơi dữ liệu đó đang được lưu trữ.
(async () => {
// Set up Chromium browser and page.
const browser = await puppeteer.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
page.setViewport({ width: 1280, height: 926 });
// Navigate to the example page.
await page.goto('https://mmeurer00.github.io/infinite-scroll-example/');
// Auto-scroll and extract desired items from the page. Currently set to extract ten items.
const items = await scrapeItems(page, extractItems, 10);
// Save extracted items to a new file.
fs.writeFileSync('./items.txt', items.join('\n') + '\n');
// Close the browser.
await browser.close();
})();
Quan trọng là phải bao gồm tất cả những gì bạn muốn trích xuất cho hàm extractItems
trong định nghĩa của hàm đó. Dòng sau đây:
items = await page.evaluate(extractItems);
sẽ tuần tự hóa hàm extractItems
trước khi xem xét nó trong ngữ cảnh trình duyệt, về cơ bản, môi trường từ vựng trở nên không khả dụng trong quá trình thực thi.
Khi hoàn thành, tệp của bạn nên trông tương tự như:
// Puppeteer will not run without these lines
const fs = require('fs');
const puppeteer = require('puppeteer');
function extractItems() {
/* For extractedElements, you are selecting the tag and class,
that holds your desired information,
then choosing the desired child element you would like to scrape from.
in this case, you are selecting the
"<div class=blog-post />" from "<div class=container />" See below: */
const extractedElements = document.querySelectorAll('#container > div.blog-post');
const items = [];
for (let element of extractedElements) {
items.push(element.innerText);
}
return items;
}
async function scrapeItems(
page,
extractItems,
itemCount,
scrollDelay = 800,
) {
let items = [];
try {
let previousHeight;
while (items.length < itemCount) {
items = await page.evaluate(extractItems);
previousHeight = await page.evaluate('document.body.scrollHeight');
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
await page.waitForFunction(`document.body.scrollHeight > ${previousHeight}`);
await page.waitForTimeout(scrollDelay);
}
} catch(e) { }
return items;
}
(async () => {
// Set up Chromium browser and page.
const browser = await puppeteer.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
page.setViewport({ width: 1280, height: 926 });
// Navigate to the example page.
await page.goto('https://mmeurer00.github.io/infinite-scroll-example/');
// Auto-scroll and extract desired items from the page. Currently set to extract ten items.
const items = await scrapeItems(page, extractItems, 10);
// Save extracted items to a new file.
fs.writeFileSync('./items.txt', items.join('\n') + '\n');
// Close the browser.
await browser.close();
})();
Tuyệt vời, bạn đã sẵn sàng! 😎 Chạy mã với:
node scrape-infinite-scroll.js
Dòng mã này sẽ mở trang demo trong trình duyệt không giao diện và cuộn xuống cho đến khi mười mục #container > div.blog-post đã được tải, lưu lại văn bản từ các mục đã trích xuất vào ./items.txt. Bằng cách chạy
open ./items.txt
bạn sẽ có quyền truy cập vào tất cả dữ liệu bạn đã thu thập, như bạn có thể thấy dưới đây:
Blog Post #1
sed ab est est
at pariatur consequuntur earum quidem quo est laudantium soluta voluptatem qui ullam et est et cum voluptas voluptatum repellat est
كيان سلطانی نژاد
Blog Post #2
enim unde ratione doloribus quas enim ut sit sapiente
odit qui et et necessitatibus sint veniam mollitia amet doloremque molestiae commodi similique magnam et quam blanditiis est itaque quo et tenetur ratione occaecati molestiae tempora
Carla Vidal
Blog Post #3
fugit voluptas sed molestias voluptatem provident
eos voluptas et aut odit natus earum aspernatur fuga molestiae ullam deserunt ratione qui eos qui nihil ratione nemo velit ut aut id quo
Anna Lane
Blog Post #4
laboriosam dolor voluptates
doloremque ex facilis sit sint culpa soluta assumenda eligendi non ut eius sequi ducimus vel quasi veritatis est dolores
Tyrone Terry
Blog Post #5
commodi ullam sint et excepturi error explicabo praesentium voluptas
etc...
Bạn cũng có thể chạy tail ./items.txt
để xem 10 mục cuối cùng được thu thập trong cửa sổ terminal của bạn.
Điều gì xảy ra nếu mã không thể trích xuất số lượng mục đã chỉ định? Xin lỗi, Puppeteer có các hàm đánh giá JavaScript trên trang như page.waitForFunction
thường có thời gian chờ 30 giây (có thể tùy chỉnh). Hàm này đợi chiều cao trang tăng sau mỗi lần cuộn. Vì vậy, khi trang tải thêm mục, nó sẽ đi qua một vòng lặp while, chỉ dừng lại và đưa ra lỗi khi chiều cao không thay đổi trong vòng 30 giây, hoặc thời gian chờ tùy chỉnh.
Các Phương Pháp Thu Thập Khác Cho Cuộn Vô Tận
Mặc dù Puppeteer có thể giảm công việc của bạn, nhưng nó có thể không phải lúc nào cũng là cách tiếp cận tốt nhất cho việc thu thập dữ liệu, tùy thuộc vào tình huống của bạn. Một cách thu thập thay thế và ít phức tạp hơn là sử dụng Cheerio. Cheerio là một thư viện NPM, còn được gọi là “JQuery cho Node”, cho phép bạn thu thập dữ liệu với một framework nhẹ. Cheerio hoạt động với dữ liệu HTML thô được đưa vào nó, hoạt động tốt nhất khi dữ liệu bạn cần phân tích được trích xuất trực tiếp từ một URL. Nếu bạn quan tâm đến việc sử dụng phương pháp thu thập này, hãy xem bài viết Web Scraping with node-fetch, để hiểu thêm về Cheerio và các trường hợp sử dụng của nó.
Kết luận
Nhờ Puppeteer, bạn có thể trích xuất dữ liệu từ các ứng dụng cuộn vô tận một cách nhanh chóng và hiệu quả. Mặc dù có thể không phải lúc nào bạn cũng sử dụng nó, tuy nhiên, mã từ bài viết này sẽ phục vụ như một điểm khởi đầu để mô phỏng cuộn giống con người trên một ứng dụng.
Nếu bạn đã thích bài viết này về Cào Dữ Liệu Ứng Dụng Cuộn Vô Tận bằng Puppeteer, hãy thử ScrapingBee và nhận 1000 yêu cầu đầu tiên miễn phí. Xem hướng dẫn bắt đầu tại here!
Cào web là một thách thức, bởi vì các cơ chế chống cào ngày càng phát triển, vì vậy việc thực hiện nó đúng cách có thể rất phiền toái. ScrapingBee cho phép bạn bỏ qua tiếng ồn và tập trung chỉ vào điều quan trọng nhất: dữ liệu.
Tài Nguyên
- https://pptr.dev/
- https://www.npmjs.com/package/puppeteer
- https://www.npmjs.com/package/cheerio
- https://www.scrapingbee.com/blog/node-fetch/#scraping-the-web-with-nodefetch-and-cheerio
- Bài viết blog này ban đầu được xuất bản tại: https://www.scrapingbee.com/blog/