12bit đã có một bài giới thiệu về Puppeteer ứng dụng vào việc scraping dữ liệu. Bài viết hôm nay, chúng ta cùng tìm hiểu một tính năng nữa của Puppeteer đó là screenshot. Đây là một tính răng rất thú vị và hữu ích, bạn có thể thỏa sức sáng tạo để mà dùng tính năng này.
API
Trước khi đi vào ứng dụng, chúng ta cùng xem qua method screenshot
có những options nào. Các bạn có thể truy cập vào đây
để xem danh sách các method mà Puppeteer cung cấp, mà cụ thể ở đây là screenshot
Cùng xem qua danh sách các options:
path
: Đường dẫn để lưu hình được tạo ra khi sreenshot.type
: Định dạng hình ảnhjpeg
hoặcpng
quality
: Chất lượng ảnh từ0-100
. Không áp dụng option này nếutype
làpng
.fullPage
: Nếu bạn set bằngtrue
thì Puppeteer sẽ chụp toàn bộ trang web.clip
: Không phải lúc nào bạn cũng cần chụp toàn bộ trang web.clip
sẽ giúp bạn chụp một vùng trên trang web mà thôi.omitBackground
: Trang web nào có background màu trắng sẽ bị loại bỏ thay vào đó là một transparency background.encoding
: Định dạng mã hóa của hình,base64
hoặcbinary
.
Giá trị trả về khi bạn gọi tới method sreenshot
đó là một Promise
. Khi resolve giá trị nhận được là string hoặc Buffer tùy vào việc bạn gán encoding
là gì.
📷 Screenshot
Chụp toàn bộ trang web
Sau khi đọc qua API, chúng ta sẽ thử chụp hình một trang web. Giả sử mình muốn chụp toàn bộ trang web https://thien.dev
và lưu thành screenshot.png
Việc cài đặt puppeteer vào khởi tạo browser bạn có thể xem qua ở bài trước nhé.
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://thien.dev')
await page.screenshot({
path: './screenshot.png',
type: 'png',
fullPage: true
})
await browser.close()
})()
Kết quả là:
Chụp một phần
Tiếp theo, thay vì chụp toàn bộ trang web, chúng ta sẽ thử chụp một phần trang web với kích thước 800x400px.
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://thien.dev')
await page.screenshot({
path: './sreenshot.png',
type: 'png',
clip: {
x: 0, y: 0,
width: 800, height: 400
}
})
await browser.close()
})()
Kết quả sẽ là một bức hình với kích thước 800x400px
Encoding
Không phải lúc nào chúng ta cũng muốn lấy một file hình. Sẽ có lúc cần lấy kết quả trả về ở dạng base64. Việc này rất đơn giản, chỉ cần thay đổi option encoding
thành base64
như sau:
(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto('https://thien.dev')
const result = await page.screenshot({
path: './sreenshot.png',
type: 'png',
clip: {
x: 0, y: 0,
width: 800, height: 400
},
encoding: 'base64'
})
console.log(result)
await browser.close()
})()
Kêt quả:
iVBORw0KGgoAAAANSUhEUgAAAyAAAAGQCAYAAABWJQQ0AAAAAXNSR0IArs4c6QAAIABJREFUeJzs3Xd8VfX9x/H395x7k5CEEMIMYQcQEWUPV90o4AT3qnWPOlt3f21tra12WEe1FXdbxQkqirPgRkWWKCPsvUcSRpJ7zvf3xw03uSSBjJsTgq/n48GDc8/6fs64N...
Bạn có thể sử dụng data đó như một data URLs như sau:
body {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAGQCAYAAABWJQQ0AAAAAXNSR0IAr...)
}
Những vấn đề gặp phải
Trong quá trình làm việc với screenshot
mình gặp phải một vài vấn đề có thể bạn cũng sẽ gặp.
Error: Failed to launch chrome!
Việc khởi tạo một browser bằng puppeteer có thể sẽ phải thêm những options khác nhau trên những môi trường OS khác nhau. Nếu bạn gặp lỗi như trên hay thử thêm option vào puppeteer.launch()
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
})
Screenshot với view port
Đôi khi bạn sẽ gặp phải những vấn đề về view port nếu screenshot với width lớn hơn 800px. Vì viewport mặt định puppeteer set là 800px x 600px. Lúc này bạn cần phải set lại view port bằng cách sử dụng page.setViewport()
// ...
await page.goto('https://github.com')
await page.setViewport({ width: 1200, height: 630 })
const result = await page.screenshot({
path: './sreenshot.png',
type: 'png',
clip: {
x: 0, y: 0,
width: 1200, height: 630
}
})
// ...
Kết quả trước và sau khi set view port:
Lỗi font
Có một trường hợp mình gặp phải đó là khi chụp hình thì bị lỗi font đối với những trang có sử dụng webfont. Nguyên nhân là do font chưa kịp load xong thì hình đã được chụp. Để xử lí vấn đề này puppeteer
cung cấp option là waitUntil
. Khi bạn gọi page.goto('url', { waitUntil: 'some-value' })
có nghĩa việc “navigation” vào trang web sẽ đợi khi nào sự kiện ở waitUntil
thực thi xong thì mới trả về kết quả thành công.
Bạn có thể xem qua phần docs của page.goto()
tại đây
để xem waitUntil
có những sự kiện nào.
Trường hợp của mình sử dụng event là networkidle0
tức là sẽ đợi tới khi không còn một connection (cụ thể là connection tới webfont) nào nữa trong khoảng thời gian là ít nhất 500
ms.
Mời bạn xem qua ví dụ trong library tụi mình mới viết social-image-gen
Kết luận
Bạn có thể ứng dụng tính năng sreenshot vào nhiều ngữ cảnh cách khau. Đối với 12bit, tụi mình đã dùng tính năng này để tự động generate social image mỗi khi publish post (vì tụi mình quá lười để thiết kế một tấm hình đại diện cho bài viết 😂). Các bạn có thể tham khảo tạo repo trên GitHub của 12bit.vn
12bitvn/social-image-gen
📷 The tool that generates your social image based on your markdown content.
- Star9
- Fork1
- Issues5
- Updatedthg 9 11, 2022