Cạo trang web động javascript

Trong bài đăng cuối cùng của tôi, tôi đã giải thích cách chúng tôi có thể cạo một trang web tĩnh bằng cách sử dụng lời hứa yêu cầu và cổ vũ. Hãy nhớ rằng một trang web tĩnh có nội dung cố định được mã hóa bằng HTML và được lưu trữ trên máy chủ web và nó không thay đổi. Chúng tôi có thể tìm nạp nội dung trên trang tĩnh một cách hiệu quả bằng cách sử dụng 2 thư viện đó. Tuy nhiên, ngày nay, nhiều trang web, chẳng hạn như Best Buy, là trang web động, có nghĩa là nội dung của chúng thay đổi liên tục do JavaScript quyết định. Nếu bạn cố gắng truy cập các trang web đó bằng cách sử dụng lời hứa yêu cầu và cổ vũ, bạn sẽ không thể tìm nạp nội dung mà bạn có thể xem trên máy tính xách tay của mình

Do đó, trong bài viết này, tôi sẽ giới thiệu một thư viện Node — Puppeteer cho phép bạn điều khiển trình duyệt không đầu (Chrome hoặc Chromium), mô phỏng quá trình một người truy cập trang web, để thực hiện thao tác cạo mà chúng tôi muốn

$ npm install puppeteer

Trước tiên hãy chắc chắn rằng bạn đã cài đặt con rối

Chúng tôi bắt đầu bằng cách nhập mô-đun Puppeteer và sau đó khai báo url mà chúng tôi muốn cạo

const puppeteer = require('puppeteer');
const url = 'https://www.bestbuy.ca/en-ca';

Chúng tôi đặt chúng không đổi vì chúng tôi sẽ không thay đổi chúng trong suốt chương trình

Tiếp theo, chúng tôi viết chức năng chính thực hiện việc cạo. Chúng tôi định nghĩa nó là một phương thức không đồng bộ, trong đó chúng tôi có thể sử dụng từ khóa chờ đợi. Một cái nhìn tổng thể của phương pháp có thể trông giống như thế này

(async function scrape() {

})().catch(function(err){
console.log(err);
});

Chúng tôi phản hồi bất kỳ dạng lỗi nào trong phương thức cạo trong hàm bắt bằng cách xuất lỗi ra bàn điều khiển

Trong chức năng, chúng tôi khởi tạo một đối tượng trình duyệt bằng cách gọi chức năng khởi chạy của người múa rối. Lưu ý rằng trình duyệt không đầu có nghĩa là không có giao diện người dùng đồ họa và nghệ sĩ múa rối theo mặc định có chế độ không đầu là đúng. Chúng tôi có thể đặt nó thành sai để chúng tôi có thể biết liệu trình duyệt có đang thực hiện các hành động mong đợi hay không

const puppeteer = require('puppeteer');
const url = 'https://www.bestbuy.ca/en-ca';
(async function scrape() {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
console.log("We are scraping from " + url + ":");
await page.goto(url);
await browser.close();
})().catch(function(err){
console.log(err);
});

Chúng ta sẽ tạo một trang mới bằng cách gọi hàm newPage() được cung cấp bởi đối tượng trình duyệt của nghệ sĩ múa rối, sau đó trình duyệt sẽ truy cập vào url mà chúng ta đã xác định. Cuối cùng, chúng tôi phải đóng trình duyệt trước khi chương trình của chúng tôi kết thúc

Tôi sẽ không giải thích nhiều về async/await trong bài viết này. Tóm lại, bạn có thể coi await như một từ khóa đảm bảo dòng chương trình tạm dừng cho đến khi dòng mã đó được thực thi

Ngay bây giờ, nếu bạn chạy chức năng này, bạn sẽ thấy một trang mới bật lên trên màn hình của mình, vì chúng tôi đã đặt headless thành false và nó trực tiếp truy cập trang Best Buy

Bây giờ, hãy tiếp tục khám phá một số nội dung thú vị mà bạn có thể thực hiện với trình duyệt không đầu

Giả sử bạn muốn lấy tất cả các liên kết tồn tại trong trang chủ. Đoạn mã này có thể đạt được điều đó

let hrefs = await page.evaluate(() => {
let Element = Array.from(document.body.querySelectorAll('a'), (el) => el.href);
return Element;
});

console.log(hrefs);

Về cơ bản, chúng tôi gọi hàm đánh giá tích hợp, trong đó chúng tôi xác định hàm ẩn danh. Trong hàm ẩn danh, chúng ta gọi phương thức querySelectorAll của HTMl DOM bằng cách chuyển 'a' làm bộ chọn CSS. Bên cạnh đó, chúng ta cũng cần xác định hàm ánh xạ mũi tên/ẩn danh cho biết thuộc tính nào của bộ chọn được lưu trữ trong mảng. Trong trường hợp của chúng tôi, đối với mỗi HTMLAnchorElement, chúng tôi lưu trữ thuộc tính href của nó trong mảng. Không có chức năng ánh xạ này, mảng được trả về sẽ không được xác định. Cuối cùng, chúng tôi đặt tất cả các giá trị href trong mảng. Dưới đây là những gì bạn mong đợi để xem

Tiến lên một bước, giả sử chúng ta muốn lấy liên kết cho iphone

Sau đó, chúng tôi cần kiểm tra từng liên kết mà chúng tôi nhận được trước đó và xem liệu nó có chứa chuỗi “iphone” không. Điều này xuất phát từ quan sát rằng tất cả các từ trong url đều ở dạng chữ thường. Để giảm kiểm tra không cần thiết (liên kết trùng lặp), thật đơn giản để chuyển đổi mảng liên kết thành một tập hợp, như hình bên dưới

let hrefs = await page.evaluate(() => {
let Element = Array.from(document.body.querySelectorAll('a'), (el) => el.href);
return Array.from(new Set(Element));
});
let res = [];
for (let i = 0; i < hrefs.length; i++) {
if (hrefs[i].indexOf("iphone") > -1) {
res.push(hrefs[i]);
}
}
console.log(res);

Same as before, we select all urls in the tag. But in the return statement, we cast the array Element to a Set, which contains only unique values(links). Then turn it into an array so we can traverse it. Next we could define a result array to store the intended links that we want. Then for each link, we check if it contains the string “iphone”. If so, the indexOf function will return a valid index (larger than -1) and we add that link to our res array, otherwise the function will return -1 and we don’t take any action. Below is the expected output. You can see all the links that have “iphone” as part of it.

Rõ ràng là một công cụ quét web đơn giản có thể đơn giản hóa tất cả quá trình lặp lại (nhấp và cuộn). Chỉ bằng cách chạy trình quét, bạn sẽ nhận được các liên kết bạn muốn sau vài giây

Ví dụ, nếu bạn muốn tìm trang web tương ứng với iPhone, bạn phải vào “Best Buy Mobile”, tìm “Cell Phones & Plans”, sau đó bạn sẽ đến phần iPhone

Hãy viết một công cụ tìm kiếm phức tạp hơn để lấy giá và tên của các máy tính xách tay đang được khuyến mãi. Giống như trước đây, chúng tôi lấy tất cả các liên kết trong trang chủ nhưng lọc ra những liên kết có chứa chuỗi đặc biệt “máy tính xách tay”

let hrefs = await page.evaluate(() => {
let Element = Array.from(document.body.querySelectorAll('a'), (el) => el.href);
return Array.from(new Set(Element));
});
let res = [];
for (let i = 0; i < hrefs.length; i++) {
if (hrefs[i].indexOf("laptop") > -1) {
res.push(hrefs[i]);
}
}

Sau đó, chúng tôi duyệt qua mảng kết quả với laptop-url. Đối với mỗi người trong số họ, chúng tôi truy cập trang tương ứng

for (let i = 0; i < res.length; i++) {
await page.goto(res[i]);
// getting the name and price
}

Lưu ý rằng từ khóa chờ đợi ở đây rất quan trọng. Dòng này đảm bảo rằng trang mới được tải đầy đủ trước khi chúng tôi gọi querySelector. Nếu không có từ khóa này, chúng tôi sẽ gặp lỗi khi nói rằng. Điều này là do trang chưa được tải

To get the name, we may open up the inspection tool. Then notice that the name of the item is in a

tag.

It’s also clear that there’s no

tag in the content above it. Therefore, the following lines will do the task for us.

________số 8

Chúng tôi gọi hàm querySelector tích hợp để chọn phần tử h1 đầu tiên. Chỉ trong trường hợp trang không dành cho các mục đơn lẻ và không có thẻ h1 (null được trả về bởi hàm querySelector), chúng tôi sẽ trả về null làm tên. Nếu không có kiểm tra này, cuối cùng chúng tôi sẽ tìm nạp thuộc tính của một đối tượng null, điều này sẽ gây ra lỗi. Trong vòng lặp, chúng tôi sử dụng tiếp tục bỏ qua liên kết hiện tại vì không cần truy vấn giá

Tiếp theo, để biết giá, trước tiên chúng tôi kiểm tra lại trang kiểm tra. Cũng dễ dàng nhận thấy rằng giá cả và khuyến mãi được lưu trữ trong các khoảng thời gian liên tiếp

Do đó, chúng tôi có thể sử dụng chức năng querySelectorAll để lấy tất cả các nhịp và lưu trữ chúng trong một mảng. Sau đó, chúng tôi có thể xác định hai biến để lưu trữ giá và khuyến mãi tương ứng. Khi duyệt qua các nhịp, chúng tôi đảm bảo rằng nó không rỗng và kiểm tra xem có tồn tại ký tự “$” không. Điều này thu hẹp đáng kể tập dữ liệu của chúng tôi, chúng tôi phải kiểm tra vì chúng tôi không xem xét các khoảng chỉ có từ/câu. Nếu có ký tự “$” chúng ta kiểm tra ngay ký tự đó có chứa từ khóa “SAVE” không. Nếu đúng như vậy, chúng tôi biết mình đang ở các nhịp liên tiếp (hàm querySelectorAll quét nội dung HTML cho từng dòng thẻ). Sau đó, chúng tôi có thể dễ dàng nhận được khuyến mãi và giá cả và phá vỡ vòng lặp

let spans = await page.evaluate(() => {
return Array.from(document.body.querySelectorAll('span'), (el) => el.textContent);
});
let price, promotion;
for (let j = 0; j < spans.length; j++) {
if (spans[j] != null && (spans[j].indexOf("$") > -1)) {
if ((spans[j].indexOf("SAVE") > -1)) {
promotion = spans[j];
price = spans[j+2];
break;
}
}
}

Một điều cần lưu ý là giá dự định của chúng tôi là “$229. 99”, nhưng nó nằm trong một nhịp lồng nhau. Vì vậy, nếu chúng ta gọi spans[j+1], nó sẽ thực sự đưa ra một chuỗi “$229. 99$22999”. Đây là sự kết hợp của tất cả các văn bản trong khoảng thời gian đó

Về cơ bản, tại thời điểm đó. spans[j] = “TIẾT KIỆM $70” , spans[j+1] = “$229. 99$22999”, và spans[j+2] = “$229. 99”. Thực tế này đã được đề cập ở trên (chức năng querySelectorAll quét nội dung HTML cho từng dòng thẻ)

Tôi đã thay đổi url thành danh mục máy tính để nó có thể quét nhanh hơn

const puppeteer = require('puppeteer');
const url = 'https://www.bestbuy.ca/en-ca';
0

Cuối cùng, chúng tôi kiểm tra lần cuối, chúng tôi không xuất giá và khuyến mãi nếu bất kỳ giá nào trong số chúng không được xác định/không hoặc không có ký hiệu “$” trong đó. Nếu mọi thứ đều ổn, chúng tôi xuất giá cũng như khuyến mãi ở định dạng Tuple vào bảng điều khiển. Một số đầu ra mẫu là

Nếu bạn thắc mắc tại sao chúng tôi viết một chương trình dài để lấy tên và giá thay vì chỉ truy vấn tên lớp, đó là vì tên lớp được tạo ngẫu nhiên để tránh các thành phần trùng tên lớp. Do đó, tên lớp có thể thay đổi hoặc thay đổi thường xuyên. Tuy nhiên, cấu trúc về cách chúng sắp xếp các phần tử lại với nhau sẽ không thay đổi, vì vậy thuật toán của chúng tôi có thể sẽ hoạt động lâu dài

Bạn có thể cạo một trang web động?

Phụ thuộc vào trình duyệt . Do đó, nội dung được loại bỏ về mặt kỹ thuật không tồn tại trước khi trang được tải . Điều này yêu cầu quy trình quét web bao gồm một bước để hiển thị nội dung trang trên trình duyệt.

Bạn có thể sử dụng JavaScript để quét web không?

Dò web bằng JavaScript là một kỹ thuật rất hữu ích để trích xuất dữ liệu từ Internet để trình bày hoặc phân tích .

Làm cách nào để cạo dữ liệu từ trang web bằng JavaScript?

Đầu tiên, cài đặt Cheerio và Axios bằng cách chạy lệnh sau. npm cài đặt cổ vũ axios. Sau đó tạo một tệp mới có tên là trình thu thập thông tin. js và sao chép/dán đoạn mã sau. const axios = yêu cầu ('axios');

Súp đẹp có thể cạo các trang web động không?

Beautiful Soup gặp khó khăn khi quét web, không thể xử lý trực tiếp nội dung động . Điều đó nói rằng những gì tôi thấy rất nhiều người dọn dẹp web đang làm là sử dụng Selenium để kích hoạt nội dung động nhằm thu thập dữ liệu đã xác định mà họ cần.