Nếu bạn trả tiền cho lưu lượng proxy theo GB, sự khác biệt giữa trình duyệt headless và khách hàng HTTP thông thường có thể tốn kém hơn 15-20 lần cho cùng một tập dữ liệu 1000 trang. Trong bài viết này - các đo lường thực tế về mức tiêu thụ lưu lượng cho Playwright, Puppeteer và thư viện requests của Python, mã cho các bài kiểm tra và các cách làm việc để giảm khối lượng dữ liệu mà không mất nội dung.
Tại sao mức tiêu thụ lưu lượng lại quan trọng cho việc phân tích
Hầu hết các nhà cung cấp proxy, bao gồm cả các pool cư trú và di động, tính phí lưu lượng theo GB, chứ không phải theo số lượng yêu cầu. Điều này có nghĩa là công cụ mà bạn sử dụng để phân tích trang web ảnh hưởng trực tiếp đến ngân sách của dự án. Trình duyệt headless tải trang hoàn toàn: HTML, CSS, JavaScript, hình ảnh, phông chữ, script phân tích, banner quảng cáo và tracker. Khách hàng HTTP như requests chỉ tải những gì bạn đã yêu cầu rõ ràng - thường là tài liệu HTML thuần túy.
Sự khác biệt đặc biệt rõ ràng khi nhìn ở quy mô lớn. Nếu bạn phân tích thẻ sản phẩm trên Wildberries hoặc Ozon, thu thập giá của đối thủ hoặc theo dõi kết quả tìm kiếm của Google, khối lượng 1000 trang là mức tiêu chuẩn hàng ngày cho một script. Khi làm việc với hàng trăm nghìn trang mỗi tháng, việc tiết kiệm lưu lượng trở thành một khoản chi phí đáng kể, đặc biệt là khi sử dụng proxy cư trú, nơi giá GB cao hơn so với các trung tâm dữ liệu.
Sự phức tạp thêm là các trang web hiện đại đang được bảo vệ tích cực khỏi bot: kiểm tra việc render JavaScript, hành vi chuột, fingerprint canvas. Điều này buộc các nhà phát triển phải chuyển từ các yêu cầu HTTP đơn giản sang các trình duyệt đầy đủ như Playwright hoặc Puppeteer, mà "nặng" hơn nhiều trong lưu lượng. Hiểu được các con số chính xác giúp tính toán ngân sách cho proxy và chọn công cụ phù hợp cho nhiệm vụ cụ thể.
Phương pháp đo lưu lượng
Để so sánh công bằng, tôi đã sử dụng cùng một danh sách 1000 URL - thẻ sản phẩm có độ phức tạp trung bình với hình ảnh, script phân tích và một số widget bên ngoài (cấu trúc điển hình cho trang web thương mại điện tử). Đo lưu lượng được thực hiện thông qua trình giám sát mạng hệ thống và các công cụ ghi log yêu cầu tích hợp trong mỗi công cụ.
Các điều kiện quan trọng của thí nghiệm:
- Cache trình duyệt đã bị tắt - mỗi trang được tải "từ đầu", như khi làm việc qua việc xoay vòng proxy với các IP khác nhau
- Chế độ headless được bật trong tất cả các bài kiểm tra trình duyệt - đây là cách mà hầu hết các script sản xuất hoạt động
- Không có việc chặn tài nguyên trong kịch bản cơ bản - để cho thấy mức tiêu thụ "thuần túy" mà không có tối ưu hóa
- Cùng một mạng và cùng một tập trang cho cả ba công cụ
Cách tiếp cận này cung cấp các con số có thể so sánh, có thể áp dụng cho trường hợp của bạn - nhân với số lượng trang trong dự án của bạn và chia cho khối lượng của gói proxy.
requests: mức tiêu thụ lưu lượng tối thiểu
Thư viện requests trong Python chỉ tải nội dung của phản hồi HTTP - những gì bạn đã yêu cầu rõ ràng. Không có JavaScript, không có hình ảnh, không có yêu cầu bổ sung đến CDN. Khối lượng trung bình của một trang HTML thẻ sản phẩm trong bài kiểm tra của tôi khoảng 180-250 KB HTML không nén.
import requests
proxies = {
"http": "http://user:pass@proxy_host:port",
"https": "http://user:pass@proxy_host:port",
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
total_bytes = 0
urls = load_urls_from_file("urls.txt") # danh sách 1000 liên kết
for url in urls:
response = requests.get(url, headers=headers, proxies=proxies, timeout=15)
total_bytes += len(response.content)
print(f"Tổng cộng đã tải: {total_bytes / 1024 / 1024:.2f} MB")
Trên 1000 trang, mức tiêu thụ cuối cùng là 190-230 MB - tức là ít hơn 0,25 GB. Đây là lựa chọn tiết kiệm nhất, nhưng nó có một hạn chế nghiêm trọng: nếu trang web render nội dung qua JavaScript (React, Vue, tải động giá), requests sẽ nhận được khung trang trống mà không có dữ liệu cần thiết. Đối với HTML tĩnh hoặc các trang web có SSR, đây là lựa chọn lý tưởng về tỷ lệ lưu lượng và kết quả.
Puppeteer: headless Chrome nặng bao nhiêu
Puppeteer điều khiển động cơ Chromium thực tế, vì vậy nó tải trang hoàn toàn: HTML, CSS, phông chữ, hình ảnh, script theo dõi, iframe quảng cáo. Ngay cả trong chế độ headless, trình duyệt thực hiện tất cả các yêu cầu mạng mà người dùng bình thường sẽ thực hiện trong Chrome.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--proxy-server=http://proxy_host:port']
});
const page = await browser.newPage();
await page.authenticate({ username: 'user', password: 'pass' });
let totalBytes = 0;
page.on('response', async (response) => {
try {
const buffer = await response.buffer();
totalBytes += buffer.length;
} catch (e) {}
});
const urls = require('./urls.json'); // 1000 liên kết
for (const url of urls) {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
}
console.log(`Tổng lưu lượng: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
await browser.close();
})();
Trong bài kiểm tra của tôi, khối lượng trung bình của một trang qua Puppeteer là 2,8-4,5 MB, tùy thuộc vào số lượng hình ảnh và script bên ngoài. Trên 1000 trang, kết quả là 3,1-4,2 GB - gấp 15-18 lần so với requests. Phần lớn lưu lượng đến từ hình ảnh (thường chiếm 40-55% khối lượng trang) và các script phân tích, quảng cáo và widget chat bên ngoài (20-30%).
Playwright: lưu lượng trong các trình duyệt khác nhau
Playwright hoạt động theo cách tương tự, nhưng hỗ trợ ba động cơ - Chromium, Firefox và WebKit. Mức tiêu thụ lưu lượng giữa chúng khác nhau: WebKit trong chế độ headless truyền thống tiết kiệm hơn một chút nhờ xử lý nội dung media khác, trong khi Firefox đôi khi tải nhiều dữ liệu hơn do sự khác biệt trong việc cache tài nguyên giữa các yêu cầu.
from playwright.sync_api import sync_playwright
total_bytes = 0
def handle_response(response):
global total_bytes
try:
body = response.body()
total_bytes += len(body)
except Exception:
pass
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
proxy={"server": "http://proxy_host:port", "username": "user", "password": "pass"}
)
page = browser.new_page()
page.on("response", handle_response)
urls = load_urls_from_file("urls.txt")
for url in urls:
page.goto(url, wait_until="networkidle", timeout=30000)
print(f"Tổng lưu lượng: {total_bytes / 1024 / 1024:.2f} MB")
browser.close()
Trên Chromium qua Playwright, kết quả gần giống với Puppeteer - 2,9-4,3 GB trên 1000 trang, điều này hợp lý vì cả hai công cụ sử dụng cùng một động cơ. Trên WebKit, mức tiêu thụ thấp hơn 10-15%, khoảng 2,6-3,7 GB, trong khi trên Firefox - cao hơn một chút, 3,3-4,6 GB. Sự khác biệt được giải thích bởi các khác biệt trong việc xử lý phông chữ, giải mã hình ảnh và hành vi của stack mạng của mỗi động cơ trình duyệt.
Bảng so sánh: GB trên 1000 trang
Dưới đây là bảng tổng hợp cho tất cả các tùy chọn đã được thử nghiệm, với việc làm tròn đến các khoảng thực tế. Các con số này áp dụng cho một trang thương mại điện tử trung bình với hình ảnh và bộ script bên ngoài điển hình - trên các trang tin tức hoặc landing page có video, mức tiêu thụ sẽ cao hơn.
| Công cụ | Lưu lượng trên 1000 trang | JS-rendering | Vượt qua phát hiện bot |
|---|---|---|---|
| requests (Python) | 0,19-0,23 GB | Không | Yếu |
| Playwright (WebKit) | 2,6-3,7 GB | Có | Trung bình |
| Puppeteer (Chromium) | 3,1-4,2 GB | Có | Trung bình |
| Playwright (Chromium) | 2,9-4,3 GB | Có | Tốt |
| Playwright (Firefox) | 3,3-4,6 GB | Có | Trung bình |
Kết luận chính: nếu trang web không yêu cầu render JavaScript để lấy dữ liệu cần thiết, requests tiết kiệm lưu lượng gấp 15-20 lần so với bất kỳ giải pháp trình duyệt nào. Nhưng nếu nội dung được tải động hoặc trang web kiểm tra hành vi của trình duyệt một cách tích cực - bạn sẽ phải trả tiền cho lưu lượng render của trình duyệt.
Cách giảm mức tiêu thụ lưu lượng từ 5-10 lần
Ngay cả khi bạn cần một trình duyệt đầy đủ, mức tiêu thụ lưu lượng có thể giảm mạnh mà không mất dữ liệu cần thiết. Dưới đây là các phương pháp làm việc mà tôi đã kiểm tra trên cùng một tập 1000 trang.
1. Chặn hình ảnh, phông chữ và media. Hình ảnh thường chiếm hơn một nửa khối lượng của trang, và chúng không cần thiết cho việc phân tích dữ liệu văn bản.
await page.route('**/*', (route) => {
const type = route.request().resourceType();
if (['image', 'font', 'media'].includes(type)) {
route.abort();
} else {
route.continue();
}
});
Phương pháp này hoạt động tương tự trong Playwright và Puppeteer và giảm lưu lượng từ 40-60% mà không mất HTML và dữ liệu văn bản.
2. Chặn các miền bên ngoài. Các mạng quảng cáo, phân tích, widget chat tải các script và hình ảnh riêng của họ, mà bạn không cần. Bạn có thể lọc các yêu cầu theo miền, chỉ để lại tài nguyên chính và CDN của nó.
3. Sử dụng "domcontentloaded" thay vì "networkidle". Việc chờ tải đầy đủ mạng khiến trình duyệt phải chờ tất cả các yêu cầu nền, bao gồm phân tích và tải lười. Nếu dữ liệu xuất hiện trong DOM sớm hơn - chuyển sang sự kiện sớm hơn sẽ tăng tốc độ phân tích và giảm tải bổ sung không cần thiết.
4. Cache các tài nguyên tĩnh giữa các yêu cầu. Nếu trang web sử dụng các tệp CSS/JS giống nhau trên tất cả các trang, cache trình duyệt được bật (khác với các điều kiện trong bài kiểm tra của chúng tôi) tiết kiệm một khối lượng đáng kể khi duyệt qua một số lượng lớn URL của cùng một miền.
5. Phương pháp kết hợp. Nhiều nhóm thường thử requests trước, và chỉ nếu dữ liệu không đủ - họ chuyển sang các trang cụ thể qua Playwright hoặc Puppeteer. Điều này kết hợp mức tiêu thụ lưu lượng cơ bản thấp với khả năng render ở những nơi thực sự cần thiết.
Với việc chặn tài nguyên một cách hợp lý, mức tiêu thụ lưu lượng của Puppeteer và Playwright giảm từ 3-4 GB xuống 0,6-1,2 GB trên 1000 trang - sự khác biệt trở nên ít hơn đáng kể so với requests, trong khi vẫn giữ được khả năng làm việc với JS-rendering và bảo vệ chống bot.
Cách chọn proxy cho khối lượng lưu lượng
Tính toán lưu lượng trực tiếp ảnh hưởng đến việc chọn loại proxy. Đối với các yêu cầu HTTP nhẹ qua requests trên các trang tĩnh, proxy trung tâm dữ liệu là lựa chọn tốt - chúng nhanh, rẻ về lưu lượng và đủ nếu trang web không kiểm tra các tín hiệu hành vi.
Nếu nhiệm vụ yêu cầu render đầy đủ qua Playwright hoặc Puppeteer để vượt qua các hệ thống chống bot - chẳng hạn như khi thu thập giá trên các thị trường hoặc theo dõi kết quả tìm kiếm - thì hợp lý hơn khi sử dụng proxy cư trú. Chúng ít bị chặn hơn theo IP-reputation, điều này rất quan trọng khi mỗi yêu cầu "nặng" vài megabyte và việc lấy lại dữ liệu do bị chặn sẽ tốn kém.
Đối với các kịch bản mà trang web kiểm tra rất nghiêm ngặt sự phù hợp giữa IP và user-agent (dịch vụ ngân hàng, ứng dụng với xác minh di động), bạn nên xem xét proxy di động - mặc dù có chi phí lưu lượng cao hơn, chúng cung cấp độ tin cậy tối đa cho địa chỉ IP và giảm thiểu số lượng yêu cầu lặp lại do bị chặn.
Hướng dẫn thực tiễn: hãy tính toán khối lượng lưu lượng theo công thức "khối lượng một trang × số lượng trang × hệ số lặp lại do lỗi và chặn" và so sánh GB cuối cùng với gói của nhà cung cấp. Việc tối ưu hóa tài nguyên, như đã mô tả ở trên, thường mang lại nhiều tiết kiệm hơn so với việc chọn loại proxy rẻ hơn - nhưng sự kết hợp giữa công cụ đúng và proxy đúng mang lại hiệu quả tối đa.
Kết luận
requests vẫn là công cụ tiết kiệm nhất về lưu lượng - khoảng 0,2 GB trên 1000 trang, nhưng không phù hợp cho các trang web có nội dung động. Puppeteer và Playwright cung cấp render đầy đủ và vượt qua bảo vệ tốt hơn, nhưng mức tiêu thụ lưu lượng tăng lên đến 3-4,5 GB cho cùng 1000 trang. Việc chặn hình ảnh, phông chữ và các miền bên ngoài giảm thiểu khoảng cách này từ 3-5 lần, giữ lại dữ liệu cần thiết.
Trước khi bắt đầu phân tích quy mô lớn, hãy tính toán khối lượng lưu lượng dự kiến với công cụ đã chọn và đưa nó vào ngân sách cho proxy. Nếu nhiệm vụ yêu cầu render JavaScript và khả năng chống lại các hệ thống chống bot, hãy bắt đầu với một bài kiểm tra trên một tập nhỏ các trang qua proxy cư trú - điều này sẽ cho phép bạn đánh giá chính xác mức tiêu thụ GB thực tế trước khi triển khai trên toàn bộ dữ liệu.