Nếu bạn thường phải làm các task liên quan đến browser automation thì Puppeteer là tool không thể bỏ qua.
Mọi người đều biết hiện tại Chrome là trình duyệt có thị phần lớn nhất. Hồi đầu năm nay thì Chrome được bổ sung tính năng mới là Chrome Headless (Kể từ bản Chrome 59). Nôm na là bạn có thể chạy Chrome mà không thực sự phải chạy Chrome. :D
Tiếp liền sau đó thì Google Chrome team cũng cho ra đời Puppeteer, một Nodejs library giúp bạn có thể control được Chrome Headless.
Khỏi phải nói về độ khủng của mấy tool này trong lĩnh vực browser automation, bằng chứng là khi được chính thức giới thiệu mà một loạt các tools tương tự khác thông báo ngừng phát triển, điển hình là PhantomJS
Bài viết này sẽ tập trung giới thiệu sơ bộ Puppeteer. Còn các bạn muốn tìm hiểu sâu hơn về Chrome headless thì có thể vào đây.
Puppeteer là gì
Nôm na thì Puppeteer là Nodejs libary chính chủ, giúp bạn điều khiển headless Chrome. Những gì mà bạn làm được bằng giao diện người dùng trên Chrome thì bạn đều có thể làm bằng Puppeteer. Bạn có thể xem mã nguồn của Puppeteer trên Github.
Từ đó mở ra một chân trời những thứ hay ho mà bạn có thể làm được với Puppeteer.
Puppeteer có thể làm gì?
- Chụp ảnh màn hình hoặc xuất file pdf của các trang.
- Crawl một SPA (Single-Page Application) và xuất ra nội dung pre-rendered (ví dụ như “SSR” (Server-Side Rendering)).
- Tự động gửi form, test giao diện và nhập dữ liệu từ bàn phím,…
- Tạo môi trường testing tự động cập nhật. Chạy bản thử nghiệm trong Chorme với các tính năng mới nhất và phiên bản javascript mới nhất.
- Ghi lại timeline trace cho website của bạn giúp phát hiện sớm các vấn đề về hiệu năng.
- Test Chorme Extensions. …
Sử dụng puppeteer
Cài đặt bằng npm:
$ npm install --save puppeteer
Excute example-file.js:
node example-file.js
Examples
Chụp ảnh màn hình
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
Tạo PDF
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {
waitUntil: 'networkidle2',
});
await page.pdf({ path: 'hn.pdf', format: 'a4' });
await browser.close();
})();
Lấy thông số màn hình của trang
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
};
});
console.log('Dimensions:', dimensions);
await browser.close();
})();
Bạn có thể tìm hiểu thêm tại đây.
Case study
Tự động cập nhật OKR (remaining):
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null,
});
const page = await browser.newPage();
await page.setCacheEnabled(false);
await page.setViewport({ width: 1366, height: 768});
// go to goals login page
await page.goto('https://goal.sun-asterisk.vn/login');
await page.click('a.m-login__btn');
// wait redirect to wsm page
await page.waitForSelector('a.wsm-btn.btn-login');
await page.click('a.wsm-btn.btn-login');
// fill username/pwd wsm login page
await page.waitForSelector('.login-content #devise-login-form');
await page.$eval('#user_email', (el, username) => el.value = username, '******');
await page.$eval('#user_password', (el, password) => el.value = password, '******');
await page.evaluate(() => document.querySelector('#wsm-login-button').click())
// wait page show modal
await page.waitForSelector('#core-values-modal #close-core-values-modal span', {
visible: true,
});
await page.click('#core-values-modal #close-core-values-modal span');
// wait login to wsm and redirect to goal objectives page
await page.waitForSelector('#myobjectives');
const objectives = await page.$$('#myobjectives .objectiveItem .align-middle .obj-name a');
// Go to each objective detail page
for (const objective of objectives) {
const href = await page.evaluate(e => e.href, objective);
const objectiveDetailPage = await browser.newPage();
await objectiveDetailPage.goto(href);
await objectiveDetailPage.evaluate(() => {
let unchangedBtns = document.getElementsByClassName('unchanged');
for (let unchangedBtn of unchangedBtns)
// click remaining item buttons
await unchangedBtn.click();
});
await new Promise(resolve => setTimeout(resolve, 1000));
// close tab
await objectiveDetailPage.close();
}
// close browser
await browser.close();
})();