Điều khiển Chrome (puppeteer)

A-Parser cho phép sử dụng trình duyệt Chrome (Chromium) làm công cụ để tải và kết xuất trang, sử dụng thư viện phổ biến puppeteer.
Các ưu điểm chính khi làm việc với puppeteer cùng với A-Parser:
- hỗ trợ Proxy riêng biệt cho mỗi tab trình duyệt
- quản lý đa luồng các tab trình duyệt
- chặn bắt các yêu cầu (request)
- tất cả các khả năng của A-Parser về quản lý hàng đợi, tạo truy vấn và xử lý kết quả
Việc sử dụng trình duyệt Chrome mở ra các khả năng sau:
- kết xuất
DOMvàJavaScript - khả năng tương tác với các phần tử của trang web:
- điền biểu mẫu
- chuyển hướng theo liên kết
- drag & drop
- tải lên tệp tin
- mô phỏng chuột
- và nhiều tính năng khác, có thể tự động hóa bất kỳ hành động tiêu chuẩn nào
- vượt qua các loại bảo vệ chống cào dữ liệu dễ dàng hơn, vì trình duyệt Chromium giống hệt trình duyệt người dùng sử dụng
- khả năng làm việc ở chế độ
Headless, tức là không có giao diện đồ họa của trình duyệt, giúp tiết kiệm tài nguyên, cũng như chạy trình duyệt trên các máy chủ không có môi trường đồ họa
Cần lưu ý rằng việc vận hành trình duyệt đòi hỏi nhiều tài nguyên (CPU, Memory) hơn so với các luồng thông thường của A-Parser
Tùy thuộc vào độ phức tạp của trang web, khuyến nghị sử dụng số lượng luồng (tab trình duyệt) không quá 1-2 cho mỗi nhân xử lý có sẵn, ví dụ đối với bộ vi xử lý 8 nhân - từ 8 đến 16 tab
Ví dụ sử dụng
Hãy xem xét ví dụ về công cụ cào dữ liệu Chrome::ScreenshotMaker2:
- công cụ cào dữ liệu thực hiện chụp ảnh màn hình các trang web theo kích thước chỉ định, và cũng có thể thu nhỏ (thay đổi kích thước) hình ảnh
- có thể tùy chọn sử dụng Proxy
- tạo một tab trình duyệt riêng biệt cho mỗi luồng của A-Parser
import { BaseParser, PuppeteerTypes } from 'a-parser-types';
let browser: PuppeteerTypes.Browser;
let jimp;
class JS_Chrome_ScreenshotsMaker2 extends BaseParser {
static defaultConf: typeof BaseParser.defaultConf = {
version: '0.2.1',
results: {
flat: [
['screenshot', 'PNG screenshot'],
]
},
results_format: '$screenshot',
load_timeout: 30,
width: 1024,
height: 768,
log_screenshots: 0,
headless: 1,
};
static editableConf: typeof BaseParser.editableConf = [
['log_screenshots', ['checkbox', 'Log Screenshots']],
['width', ['textfield', 'Viewport Width']],
['height', ['textfield', 'Viewport Height']],
['resize_width', ['textfield', 'Resize Width']],
['resize_height', ['textfield', 'Resize Height']],
['headless', ['checkbox', 'Chrome Headless']],
];
async init() {
// khởi tạo trình duyệt
browser = await this.puppeteer.launch({
headless: this.conf.headless,
logConnections: false,
defaultViewport: {
width: parseInt(this.conf.width),
height: parseInt(this.conf.height),
}
});
if (this.conf.resize_width) {
// kết nối mô-đun jimp nếu cần thay đổi kích thước ảnh chụp màn hình
jimp = require('jimp');
};
};
async destroy() {
// đóng trình duyệt khi kết thúc tác vụ
if (browser)
await browser.close();
}
page: PuppeteerTypes.Page;
async threadInit() {
// tạo trang trình duyệt khi khởi tạo luồng
this.page = await browser.newPage();
// các phương thức puppeteer tiêu chuẩn
await this.page.setCacheEnabled(true);
await this.page.setDefaultNavigationTimeout(this.conf.timeout * 1000);
// chỉ định A-Parser sử dụng proxy cho trang này
await this.puppeteer.setPageUseProxy(this.page);
this.logger.put(`New page created for thread #${this.threadId}`);
}
async parse(set, results) {
const self = this;
const { conf, page } = self;
for (let attempt = 1; attempt <= conf.proxyretries; attempt++) {
try {
self.logger.put(`Attempt #${attempt}`);
// chuyển đến trang được chỉ định trong truy vấn
await page.goto(set.query);
// ẩn thanh cuộn để chụp ảnh màn hình
await page.evaluate(() => { document.querySelector('html').style.overflow = 'hidden'; });
// lấy ảnh chụp màn hình
results.screenshot = await page.screenshot();
if (parseInt(conf.resize_width)) {
// thay đổi kích thước hình ảnh nếu cần
let image = await jimp.read(results.screenshot);
image.resize(parseInt(conf.resize_width), parseInt(conf.resize_height));
results.screenshot = await image.getBufferAsync('image/png');
}
self.logger.put(`Screenshot(${attempt}): OK, size: ${parseInt("" + (results.screenshot.length / 1024))}KB`);
if (conf.log_screenshots)
self.logger.putHTML("<img src='data:image/png;base64," + results.screenshot.toString('base64') + "'>");
results.success = 1;
// đóng các kết nối hiện tại vì trình duyệt sử dụng keep-alive
await self.puppeteer.closeActiveConnections();
break;
}
catch (error) {
self.logger.put(`Fetch page error: ${error}`);
// đóng các kết nối hiện tại vì trình duyệt sử dụng keep-alive
await self.puppeteer.closeActiveConnections();
// thay đổi proxy cho tab trình duyệt
await self.proxy.next();
}
}
return results;
}
}
Ví dụ này minh họa sự đơn giản của việc sử dụng các Proxy khác nhau cho mỗi tab, cũng như hoạt động đa luồng (1 luồng = 1 tab trình duyệt)
Mô tả các phương thức
await this.puppeteer.launch(opts?)
Phương thức này tương tự như phương thức .launch của thư viện puppeteer, nó khởi chạy trình duyệt Chromium với các tùy chọn opts cần thiết. Sự khác biệt chính nằm ở việc tích hợp với A-Parser và hỗ trợ Proxy cho mỗi tab, cũng như sự hiện diện của các tùy chọn bổ sung:
logConnections?: boolean
Bật ghi nhật ký cho tất cả các kết nối (bất kể có sử dụng Proxy hay không), đầu ra nhật ký được thực hiện riêng biệt theo từng luồng
stealth?: boolean
Sử dụng plugin puppeteer-extra để ngụy trang Chromium thành Chrome thật
stealthOpts?: any
Các tùy chọn bổ sung cho plugin puppeteer-extra
extraPlugins?: array
Sử dụng các plugin bổ sung, ví dụ như puppeteer-extra/packages

Các tùy chọn khác
Tất cả các tùy chọn khởi chạy khác có thể được xem trong tài liệu gốc của puppeteer
await this.puppeteer.setPageUseProxy(page)
Phương thức này liên kết trang trình duyệt với luồng A-Parser để Proxy hoạt động chính xác, nó cần được gọi ngay sau khi tạo trang:
const page = await browser.newPage();
await this.puppeteer.setPageUseProxy(page);
await this.puppeteer.closeActiveConnections(page?)
Phương thức này cần được gọi sau khi hoàn thành xử lý truy vấn hoặc trước khi thay đổi Proxy để xử lý lần thử tiếp theo
Trình duyệt Chrome theo mặc định để lại các kết nối mở với các trang web mà nó kết nối, phương thức này cho phép kiểm soát số lượng tài nguyên được sử dụng, cũng như giảm tải cho Proxy
Đối số page là tùy chọn, khi được gọi không có đối số, A-Parser sẽ đóng các kết nối cho tab liên kết với luồng hiện tại
await this.puppeteer.logScreenshot()
Phương thức ghi nhật ký ảnh chụp màn hình của trang hiện tại