Cách tối ưu hóa việc sử dụng lưu lượng truy cập trình duyệt của bạn nhiều nhất?

Senior Web Scraping Engineer
Tổng quan
Khi sử dụng Puppeteer để thu thập dữ liệu, tiêu thụ lưu lượng là một yếu tố quan trọng cần xem xét. Đặc biệt khi sử dụng dịch vụ proxy, chi phí lưu lượng có thể tăng đáng kể. Để tối ưu hóa việc sử dụng lưu lượng, chúng ta có thể áp dụng các chiến lược sau:
- Chặn tài nguyên: Giảm tiêu thụ lưu lượng bằng cách chặn các yêu cầu tài nguyên không cần thiết.
- Chặn URL yêu cầu: Giảm thêm lưu lượng bằng cách chặn các yêu cầu cụ thể dựa trên đặc điểm URL.
- Mô phỏng thiết bị di động: Sử dụng cấu hình thiết bị di động để có được phiên bản trang nhẹ hơn.
- Tối ưu hóa toàn diện: Kết hợp các phương pháp trên để đạt được kết quả tốt nhất.
Kế hoạch tối ưu hóa 1: Chặn tài nguyên
Giới thiệu về Chặn tài nguyên
Trong Puppeteer, page.setRequestInterception(true)
có thể bắt mọi yêu cầu mạng được khởi tạo bởi trình duyệt và quyết định tiếp tục (request.continue()
), chấm dứt (request.abort()
), hoặc tùy chỉnh phản hồi (request.respond()
).
Phương pháp này có thể giảm đáng kể tiêu thụ băng thông, đặc biệt phù hợp cho các kịch bản thu thập dữ liệu, chụp màn hình, và tối ưu hóa hiệu suất.
Các loại tài nguyên có thể chặn và Đề xuất
Loại tài nguyên | Mô tả | Ví dụ | Ảnh hưởng sau khi chặn | Khuyến nghị |
---|---|---|---|---|
image |
Tài nguyên hình ảnh | Hình ảnh JPG/PNG/GIF/WebP | Hình ảnh sẽ không được hiển thị | ⭐ An toàn |
font |
Tệp phông chữ | Phông chữ TTF/WOFF/WOFF2 | Sẽ sử dụng phông chữ mặc định của hệ thống | ⭐ An toàn |
media |
Tệp phương tiện | Tệp video/audio | Nội dung phương tiện không thể phát được | ⭐ An toàn |
manifest |
Web App Manifest | Tệp cấu hình PWA | Chức năng PWA có thể bị ảnh hưởng | ⭐ An toàn |
prefetch |
Tài nguyên dự đoán | <link rel="prefetch"> |
Ảnh hưởng tối thiểu đến trang | ⭐ An toàn |
stylesheet |
Tệp CSS | Tệp CSS bên ngoài | Kiểu trang bị mất, có thể ảnh hưởng đến bố cục | ⚠️ Cẩn thận |
websocket |
WebSocket | Kết nối truyền thông thời gian thực | Chức năng thời gian thực bị vô hiệu hóa | ⚠️ Cẩn thận |
eventsource |
Sự kiện gửi từ máy chủ | Dữ liệu push từ máy chủ | Chức năng push bị vô hiệu hóa | ⚠️ Cẩn thận |
preflight |
Yêu cầu tiền kiểm CORS | Yêu cầu OPTIONS | Yêu cầu giữa các nguồn gốc không thành công | ⚠️ Cẩn thận |
script |
Tệp JavaScript | Tệp JS bên ngoài | Chức năng động bị vô hiệu hóa, SPA có thể không hiển thị | ❌ Tránh |
xhr |
Yêu cầu XHR | Yêu cầu dữ liệu AJAX | Không thể lấy dữ liệu động | ❌ Tránh |
fetch |
Yêu cầu Fetch | Yêu cầu AJAX hiện đại | Không thể lấy dữ liệu động | ❌ Tránh |
document |
Tài liệu chính | Chính trang HTML | Trang không thể tải | ❌ Tránh |
Giải thích mức độ khuyến nghị:
- ⭐ An toàn: Việc chặn hầu như không ảnh hưởng đến việc thu thập dữ liệu hoặc hiển thị màn hình đầu tiên; được khuyến nghị chặn theo mặc định.
- ⚠️ Cẩn thận: Có thể làm hỏng kiểu, chức năng thời gian thực, hoặc yêu cầu giữa các nguồn gốc; cần có sự phán đoán kinh doanh.
- ❌ Tránh: Xác suất cao gây ra việc các trang SPA/tin động không hiển thị hoặc không thể lấy dữ liệu bình thường, trừ khi bạn hoàn toàn chắc chắn rằng bạn không cần các tài nguyên này.
Mã ví dụ về Chặn tài nguyên
javascript
import puppeteer from 'puppeteer-core';
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
async function scrapeWithResourceBlocking(url) {
const browser = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
defaultViewport: null
});
const page = await browser.newPage();
// Kích hoạt chặn yêu cầu
await page.setRequestInterception(true);
// Định nghĩa các loại tài nguyên cần chặn
const BLOCKED_TYPES = new Set([
'image',
'font',
'media',
'stylesheet',
]);
// Chặn các yêu cầu
page.on('request', (request) => {
if (BLOCKED_TYPES.has(request.resourceType())) {
request.abort();
console.log(`Đã chặn: ${request.resourceType()} - ${request.url().substring(0, 50)}...`);
} else {
request.continue();
}
});
await page.goto(url, {waitUntil: 'domcontentloaded'});
// Trích xuất dữ liệu
const data = await page.evaluate(() => {
return {
title: document.title,
content: document.body.innerText.substring(0, 1000)
};
});
await browser.close();
return data;
}
// Sử dụng
scrapeWithResourceBlocking('https://www.scrapeless.com')
javascript
.then(data => console.log('Kết quả thu thập dữ liệu:', data))
.catch(error => console.error('Thu thập dữ liệu không thành công:', error));
Kế hoạch Tối ưu hóa 2: Chặn URL Yêu cầu
Ngoài việc chặn theo loại tài nguyên, có thể thực hiện kiểm soát chặn tinh vi hơn dựa trên các đặc điểm của URL. Điều này đặc biệt hiệu quả trong việc chặn quảng cáo, các script phân tích và các yêu cầu bên thứ ba không cần thiết khác.
Chiến lược Chặn URL
- Chặn theo miền: Chặn tất cả các yêu cầu từ một miền cụ thể
- Chặn theo đường dẫn: Chặn các yêu cầu từ một đường dẫn cụ thể
- Chặn theo loại tệp: Chặn các tệp có phần mở rộng cụ thể
- Chặn theo từ khóa: Chặn các yêu cầu có URL chứa các từ khóa cụ thể
Mẫu URL Có thể Chặn Thường Gặp
Mẫu URL | Mô tả | Ví dụ | Khuyến nghị |
---|---|---|---|
Dịch vụ quảng cáo | Các miền mạng quảng cáo | ad.doubleclick.net , googleadservices.com |
⭐ An toàn |
Dịch vụ phân tích | Các script thống kê và phân tích | google-analytics.com , hotjar.com |
⭐ An toàn |
Plugin mạng xã hội | Nút chia sẻ xã hội, v.v. | platform.twitter.com , connect.facebook.net |
⭐ An toàn |
Pixel theo dõi | Pixel theo dõi hành vi người dùng | Các URL chứa pixel , beacon , tracker |
⭐ An toàn |
Tệp truyền thông lớn | Tệp video, âm thanh lớn | Các phần mở rộng như .mp4 , .webm , .mp3 |
⭐ An toàn |
Dịch vụ phông chữ | Dịch vụ phông chữ trực tuyến | fonts.googleapis.com , use.typekit.net |
⭐ An toàn |
Tài nguyên CDN | Tài nguyên CDN tĩnh | cdn.jsdelivr.net , unpkg.com |
⚠️ Cẩn thận |
Ví dụ Mã Chặn URL
javascript
import puppeteer from 'puppeteer-core';
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
async function scrapeWithUrlBlocking(url) {
const browser = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
defaultViewport: null
});
const page = await browser.newPage();
// Bật chặn yêu cầu
await page.setRequestInterception(true);
// Xác định các miền và mẫu URL cần chặn
const BLOCKED_DOMAINS = [
'google-analytics.com',
'googletagmanager.com',
'doubleclick.net',
'facebook.net',
'twitter.com',
'linkedin.com',
'adservice.google.com',
];
const BLOCKED_PATHS = [
'/ads/',
'/analytics/',
'/pixel/',
'/tracking/',
'/stats/',
];
// Chặn các yêu cầu
page.on('request', (request) => {
const url = request.url();
// Kiểm tra miền
if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
request.abort();
console.log(`Chặn miền: ${url.substring(0, 50)}...`);
return;
}
// Kiểm tra đường dẫn
if (BLOCKED_PATHS.some(path => url.includes(path))) {
request.abort();
console.log(`Chặn đường dẫn: ${url.substring(0, 50)}...`);
return;
}
// Cho phép các yêu cầu khác
request.continue();
});
await page.goto(url, {waitUntil: 'domcontentloaded'});
// Trích xuất dữ liệu
const data = await page.evaluate(() => {
return {
title: document.title,
content: document.body.innerText.substring(0, 1000)
};
});
await browser.close();
return data;
}
// Sử dụng
scrapeWithUrlBlocking('https://www.scrapeless.com')
.then(data => console.log('Kết quả thu thập dữ liệu:', data))
.catch(error => console.error('Thu thập dữ liệu không thành công:', error));
Kế hoạch Tối ưu hóa 3: Giả lập Thiết bị Di động
Giả lập thiết bị di động là một chiến lược tối ưu hóa lưu lượng khác vì các trang web di động thường cung cấp nội dung trang nhẹ hơn.
Lợi ích của Giả lập Thiết bị Di động
- Phiên bản trang nhẹ hơn: Nhiều trang web cung cấp nội dung ngắn gọn hơn cho thiết bị di động
- Tài nguyên hình ảnh nhỏ hơn: Phiên bản di động thường tải hình ảnh nhỏ hơn
- CSS và JavaScript đơn giản hóa: Phiên bản di động thường sử dụng các kiểu và script đơn giản hơn
- Giảm quảng cáo và nội dung không cốt lõi: Phiên bản di động thường loại bỏ một số chức năng không cốt lõi
- Phản hồi thích ứng: Nhận bố cục nội dung tối ưu cho màn hình nhỏ
Cấu hình Giả lập Thiết bị Di động
Dưới đây là các tham số cấu hình cho một số thiết bị di động thường được sử dụng:
javascript
const iPhoneX = {
viewport: {
width: 375,
height: 812,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false
}
};
Hoặc sử dụng trực tiếp các phương pháp tích hợp sẵn của puppeteer để giả lập thiết bị di động
javascript
import { KnownDevices } from 'puppeteer-core';
const iPhone = KnownDevices['iPhone 15 Pro'];
javascript
const trình duyệt = await puppeteer.launch();
const trang = await trình duyệt.newPage();
await trang.emulate(iPhone);
Ví dụ Mô Phỏng Thiết Bị Di Động
javascript
import puppeteer, {KnownDevices} from 'puppeteer-core';
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
async function scrapeWithMobileEmulation(url) {
const trình duyệt = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
defaultViewport: null
});
const trang = await trình duyệt.newPage();
// Thiết lập mô phỏng thiết bị di động
const iPhone = KnownDevices['iPhone 15 Pro'];
await trang.emulate(iPhone);
await trang.goto(url, {waitUntil: 'domcontentloaded'});
// Trích xuất dữ liệu
const dữ liệu = await trang.evaluate(() => {
return {
title: document.title,
content: document.body.innerText.substring(0, 1000)
};
});
await trình duyệt.close();
return dữ liệu;
}
// Sử dụng
scrapeWithMobileEmulation('https://www.scrapeless.com')
.then(dữ liệu => console.log('Kết quả thu thập:', dữ liệu))
.catch(error => console.error('Thu thập thất bại:', error));
Ví dụ Tối Ưu Hóa Toàn Diện
Dưới đây là ví dụ toàn diện kết hợp tất cả các phương pháp tối ưu hóa:
javascript
import puppeteer, {KnownDevices} from 'puppeteer-core';
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
async function optimizedScraping(url) {
console.log(`Bắt đầu thu thập tối ưu: ${url}`);
// Ghi lại mức sử dụng lưu lượng
let tổngBytesĐãSửDụng = 0;
const trình duyệt = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
defaultViewport: null
});
const trang = await trình duyệt.newPage();
// Thiết lập mô phỏng thiết bị di động
const iPhone = KnownDevices['iPhone 15 Pro'];
await trang.emulate(iPhone);
// Thiết lập chặn yêu cầu
await trang.setRequestInterception(true);
// Xác định loại tài nguyên cần chặn
const LOẠI_BỊ_CHẶN = [
'image',
'media',
'font'
];
// Xác định miền cần chặn
const MIỀN_BỊ_CHẶN = [
'google-analytics.com',
'googletagmanager.com',
'facebook.net',
'doubleclick.net',
'adservice.google.com'
];
// Xác định đường dẫn URL cần chặn
const ĐƯỜNG_DẪN_BỊ_CHẶN = [
'/ads/',
'/analytics/',
'/tracking/'
];
// Chặn các yêu cầu
trang.on('request', (request) => {
const url = request.url();
const loạiTàiNguyên = request.resourceType();
// Kiểm tra loại tài nguyên
if (LOẠI_BỊ_CHẶN.includes(loạiTàiNguyên)) {
console.log(`Chặn loại tài nguyên: ${loạiTàiNguyên} - ${url.substring(0, 50)}...`);
request.abort();
return;
}
// Kiểm tra miền
if (MIỀN_BỊ_CHẶN.some(miền => url.includes(miền))) {
console.log(`Chặn miền: ${url.substring(0, 50)}...`);
request.abort();
return;
}
// Kiểm tra đường dẫn
if (ĐƯỜNG_DẪN_BỊ_CHẶN.some(đườngDẫn => url.includes(đườngDẫn))) {
console.log(`Chặn đường dẫn: ${url.substring(0, 50)}...`);
request.abort();
return;
}
// Cho phép các yêu cầu khác
request.continue();
});
// Giám sát lưu lượng mạng
trang.on('response', async (response) => {
const headers = response.headers();
const độDàiNộiDung = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
tổngBytesĐãSửDụng += độDàiNộiDung;
});
await trang.goto(url, {waitUntil: 'domcontentloaded'});
// Giả lập cuộn để kích hoạt nội dung tải lười
await trang.evaluate(() => {
window.scrollBy(0, window.innerHeight);
});
await new Promise(resolve => setTimeout(resolve, 1000))
// Trích xuất dữ liệu
const dữ liệu = await trang.evaluate(() => {
return {
title: document.title,
content: document.body.innerText.substring(0, 1000),
links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
text: a.innerText,
href: a.href
}))
};
});
// Xuất thống kê sử dụng lưu lượng
console.log(`\nThống kê Sử dụng Lưu lượng:`);
console.log(`Đã sử dụng: ${(tổngBytesĐãSửDụng / 1024 / 1024).toFixed(2)} MB`);
await trình duyệt.close();
return dữ liệu;
}
// Sử dụng
optimizedScraping('https://www.scrapeless.com')
.then(dữ liệu => console.log('Hoàn thành thu thập:', dữ liệu))
.catch(error => console.error('Thu thập thất bại:', error));
So Sánh Tối Ưu Hóa
Chúng ta thử xóa mã tối ưu hóa từ ví dụ toàn diện để so sánh lưu lượng trước và sau tối ưu hóa. Dưới đây là mã ví dụ không được tối ưu hóa:
javascript
import puppeteer from 'puppeteer-core';
javascript
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
async function optimizedScraping(url) {
console.log(`Bắt đầu lấy dữ liệu tối ưu: ${url}`);
// Ghi lại mức sử dụng lưu lượng
let totalBytesUsed = 0;
const browser = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
defaultViewport: null
});
const page = await browser.newPage();
// Đặt chặn yêu cầu
await page.setRequestInterception(true);
// Chặn các yêu cầu
page.on('request', (request) => {
request.continue();
});
// Giám sát lưu lượng mạng
page.on('response', async (response) => {
const headers = response.headers();
const contentLength = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
totalBytesUsed += contentLength;
});
await page.goto(url, {waitUntil: 'domcontentloaded'});
// Mô phỏng cuộn để kích hoạt nạp nội dung lười
await page.evaluate(() => {
window.scrollBy(0, window.innerHeight);
});
await new Promise(resolve => setTimeout(resolve, 1000))
// Trích xuất dữ liệu
const data = await page.evaluate(() => {
return {
title: document.title,
content: document.body.innerText.substring(0, 1000),
links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
text: a.innerText,
href: a.href
}))
};
});
// Xuất thông tin thống kê mức sử dụng lưu lượng
console.log(`\nThống kê mức sử dụng lưu lượng:`);
console.log(`Đã sử dụng: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
await browser.close();
return data;
}
// Sử dụng
optimizedScraping('https://www.scrapeless.com')
.then(data => console.log('Lấy dữ liệu hoàn tất:', data))
.catch(error => console.error('Lấy dữ liệu thất bại:', error));
Sau khi chạy mã không tối ưu, chúng ta có thể thấy sự khác biệt về lưu lượng một cách trực quan từ thông tin đã in ra:
Tình huống | Lưu lượng đã sử dụng (MB) | Tỉ lệ tiết kiệm |
---|---|---|
Không tối ưu | 6.03 | — |
Tối ưu | 0.81 | ≈ 86.6 % |
Bằng cách kết hợp các phương án tối ưu hóa ở trên, mức tiêu thụ lưu lượng proxy có thể được giảm đáng kể, hiệu quả lấy dữ liệu có thể được cải thiện, và đảm bảo rằng nội dung cốt lõi cần thiết được thu thập.
Tại Scrapless, chúng tôi chỉ truy cập dữ liệu có sẵn công khai trong khi tuân thủ nghiêm ngặt các luật, quy định và chính sách bảo mật trang web hiện hành. Nội dung trong blog này chỉ nhằm mục đích trình diễn và không liên quan đến bất kỳ hoạt động bất hợp pháp hoặc vi phạm nào. Chúng tôi không đảm bảo và từ chối mọi trách nhiệm đối với việc sử dụng thông tin từ blog này hoặc các liên kết của bên thứ ba. Trước khi tham gia vào bất kỳ hoạt động cạo nào, hãy tham khảo ý kiến cố vấn pháp lý của bạn và xem xét các điều khoản dịch vụ của trang web mục tiêu hoặc có được các quyền cần thiết.