Nghịch ngợm với Puppeteer

1. Pupperteer là gì?

Nguyên văn

Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome or Chromium.

Tạm dịch là:

Puppeteer là một thư viện của Node cung cấp API cấp cao để kiểm soát Chrome hoặc Chromium sử dụng giao thức DevTools. Puppeteer mặc định chạy headless, nhưng có thể được định cấu hình để chạy non-headless.

Mình sẽ giải thích một chút: Theo mình hiểu, run headless là việc bạn chạy browser mà không có giao diện. Việc chạy browser như vậy thường để crawl dữ liệu, chụp ảnh màn hình,... chứ không phải để duyệt web.

Một số việc khá hay ho mà bạn có thể làm với Pupperteer là:

  • 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.

Trong bài viết này, mình sẽ cùng các bạn thử dùng puppeteer để chụp ảnh màn hình hoặc xuất file pdf nhé 😃 Các tính năng khác, mình hẹn nhau trong tương lai nhé 😅

2. Một số hàm thông dụng

2.1 puppeteer.launch([options])

Hàm launch([options]) sẽ trả về một instance của brower với options là các cấu hình tùy chọn cho trình duyệt

Một số options:

Options Descrption Type Default
product Tùy chọn browser, có thể là chorme hoặc firefox String
ignoreHTTPSErrors Bỏ qua HTTPs errors trong quá trình điều hướng Boolean false
headless Run browser ở chế độ headless Boolean true
executablePath Text String Text
slowMo Làm chậm các thực thi của puppeteer theo milliseconds Number
defaultViewport Đặt chế độ xem trang gồm: width, height, deviceScaleFactor , isMobie, hasTouch, isLandscape Object
args Đối số bổ sung để lựa chọn phiên bản trình duyệt Array<String>
ignoreDefaultArgs Bỏ qua puppetter.defaultArgs(). Đây là một option khá nguy hiểm, cần cẩn thận khi sử dụng boolean|array<string> false
handleSIGINT Đóng trình duyệt với Ctrl + C boolean true
handleSIGTERM Close the browser process on SIGTERM boolean true
handleSIGHUP Close the browser process on SIGHUP boolean true
timoeout Thời gian milliseconds tối đa để start browser. Đặt 0 nếu không muốn xét timeout Number 30000
dumpio Whether to pipe the browser process stdout and stderr into process.stdout and process.stderr boolean false
userDataDir Đường dẫn tới Thư mục của người dùng String
env Thiết lập một số biến môi trường cho browser Object process.env
devtools Tự động mở DevTools cho mỗi tab. Nếu options này là true thì headless phải là false Boolean
pipe Kết nối với browser thông qua pipe thay vì WebSocket Boolean false
extraPrefsFirefox Một số tùy chọn bổ sung khi dùng firefox Object

Trả về: <Promise<Browser>>

Chi tiết xem tại đây.

2.2 page.goto(url[, options])

Hàm này sẽ điều hướng đến trang của browser đến website mà bạn muốn crawl dữ liệu hoặc chụp ảnh màn hình,...vv

Các tham số đầu vào:

  • url: URL để điều hướng trang.
  • options: Một số tùy chọn khi điều hướng
Options Description Type Default
timeout Thời gian timeout tối đa, tính bằng miliseconds khi điều hướng String 30000
waitUntil Xem xét điều hướng thành công. Có 4 tùy chọn là load, domcontentloaded networkidle0networkidle2 stringArray<string> load
referer Cung cấp referer header String

Trả về: <Promise<?Response>>

Lưu ý: Nếu có nhiều điều hướng, các xử lý sau sẽ được thực hiện ở điều hướng cuối cùng.

Chi tiết xem tại đây.

2.3 page.pdf([options])

Hàm page.pdf() sẽ thực hiện render file pdf từ trang html với các tùy chọn yêu cầu.

Một số options:

Options Descrption Type Default
path Đường dẫn lưu file. Nếu không có path, file sẽ không được lưu. String
scale Tỉ lệ của webpage. Nhận giá trị từ 0.1 đến 2 Number 1
displayHeaderFooter HIển thị header và footer Boolean false
headerTemplate HTML template cho header. Bao gồm: date, title, url, pageNumbertotalPage Object
footerTemplate HTML template cho footer. Options tương tự header. Text
printBackground Tùy chọn in background Boolean false
landscape Hướng giấy Boolean false
pageRanges Phạm vi giấy để in, ví dụ: 1-5, 8, 11-13 Text
format Định dạng, như Letter, A4, ... String Letter
width Chiều dài String Number
height Chiều cao String Number
margin Căn lề Object
preferCSSPageSize Ưu tiên định dạng CSS so với các khai báo tùy chọn Boolean false

Trả về: <Promise<Buffer>>

Chi tiết xem tại đây.

3. Nghịch ngợm thôi nào

Đầu tiên, mình sẽ init một project Node trong folder test

mkdir test
cd test
npm init

Đừng quên cài puppeteer cho project nhé:

npm install pupperteer --save

Giờ thì mình sẽ viết file index.js để nghịch thử ahihi 😀.

3.1 Chụp ảnh màn hình cái nhỉ

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://viblo.asia');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

Gõ lệnh run và chờ thành quả thôi nào:

node index.js

Kết quả:


3.2 Thử xuất một file pdf nha

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://viblo.asia', {waitUntil: 'networkidle2'});
  await page.pdf({
      path: 'viblo.pdf',
      format: 'A4',
      printBackground: true,
    });

  await browser.close();
})();

Kết quả:

3.3 Chụp trang yêu cầu authenticate

Nếu bạn chụp một trang yêu cầu đăng nhập thì sẽ thế nào nhỉ.

Ví dụ: Ở dây, mình muốn chụp màn hình trang viết bài của trang Viblo.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://viblo.asia/publish/post', {waitUntil: 'networkidle2'});
  await page.screenshot({ path: 'viblo-authenticate.png'})

  await browser.close();
})();

Kết quả:

Như trong trường hợp này, chúng ta sẽ chụp được trang đăng nhập. Vì khi brower run và truy cập tới trang viết bài thì router được điều hướng sang trang đăng nhập vì trang mà chúng ta muốn truy cập yêu cầu authenticate.

Cách xử lý là chúng ta sẽ phải xét cookie trước khi truy cập trang web đó. Thử lại lần nữa nào:

const puppeteer = require('puppeteer');

(async () => {
    const cookie = {
        name: 'viblo_auth',
        value: '<your_cookie>', // replace this!
        domain: 'viblo.asia',
        url: 'https://viblo.asia',
        path: '/',
        httpOnly: true,
        secure: true,
      };
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setCookie(cookie);
  
  await page.goto('https://viblo.asia/publish/post', {waitUntil: 'networkidle2'});
  await page.screenshot({ path: 'viblo-authenticate.png'})

  await browser.close();
})()

Kết quả bạn thu được sẽ như vầy nè

Chúc các bạn thành công và hẹn gặp lại các bạn ở các bài viết tiếp theo.

Tài liệu tham khảo:

https://developers.google.com/web/tools/puppeteer/


All Rights Reserved