Chặn Tài Nguyên trong Playwright: Nó Thực Sự Tiết Kiệm Gì
Tóm tắt:
- "Chặn hình ảnh để tiết kiệm băng thông" hoàn toàn phụ thuộc vào trang. Đo lường trên ba trang, việc chặn hình ảnh, phương tiện và font chữ giảm lưu lượng truyền xuống 67% trên một catalog nhiều hình ảnh, khoảng 25% trên một trang bài báo, và hầu như không có gì trên một danh sách chỉ có văn bản không có hình ảnh.
- Trên trang chỉ có văn bản, stylesheet là tải trọng chính. Việc thêm
stylesheetvào danh sách chặn giảm nó xuống chỉ còn 2,121 byte — chỉ riêng tài liệu HTML — vì ba trong bốn yêu cầu của nó là CSS. - Tiết kiệm byte là đáng tin cậy; tiết kiệm thời gian thì không. Mỗi hồ sơ bị chặn truyền ít hơn, nhưng thời gian thực tế có thể thay đổi cả hai chiều trong các lần chạy, vì việc chặn từng yêu cầu đều có chi phí riêng.
- Việc chặn không tốn dữ liệu. Số lượng phần tử được trích xuất đều giống nhau trong chín lần đo — 20 sản phẩm, 10 câu trích dẫn, 36 đoạn văn — vì vậy không có gì hữu ích bị vứt bỏ.
- Đo lường byte tuyệt đối, không phải phần trăm, và đo nhiều hơn một lần. Một trang ở đây có một cơ sở hạ tầng bimodal mà tiết kiệm đáng chú ý dao động giữa 1% và 65% mà không thay đổi nội dung của nó.
- Bắt đầu miễn phí. Trình duyệt Scraping có một tầng miễn phí, và toàn bộ quy trình đo lường trong bài viết này là chín lần tải trang.
Playwright kết nối với Trình duyệt Scraping không có hình ảnh qua một điểm cuối WebSocket CDP, điều này có nghĩa là một phiên trình duyệt đám mây hoạt động giống như một phiên bản địa phương — và tính phí như một phiên từ xa. Mỗi hình ảnh, font chữ và stylesheet mà một trang rút ra là lưu lượng mà bạn đã trả để chuyển.
Lời khuyên tiêu chuẩn là chặn hình ảnh. Lời khuyên đó được lặp lại ở khắp mọi nơi mà không có số liệu đi kèm, vì vậy bài viết này gắn một con số lên: cùng một tập lệnh, ba trang, ba hồ sơ chặn, byte được đếm trên giao thức.
Chi phí thực sự của một phiên trình duyệt
Một trình duyệt lấy tất cả những gì mà con người sẽ thấy. Đối với việc trích xuất, hầu hết trong số đó là lãng phí — không ai phân tích một font chữ trên web. Trọng lượng trang web trên toàn cầu được theo dõi công khai bởi báo cáo trọng lượng trang của HTTP Archive, và các con số tổng hợp như thế là những gì mà lời khuyên về việc chặn hình ảnh dựa vào.
Tổng hợp chính là nơi mà lời khuyên đó sai. Bạn không khai thác trang trung vị; bạn khai thác một mục tiêu, mà hỗn hợp tài sản của nó có thể không giống như trang trung vị.
Các yêu cầu tiên quyết
- Python 3.9 hoặc mới hơn và
pip install playwright - Một khóa API của Scrapeless trong biến môi trường
SCRAPELESS_API_KEY
Không cần tải trình duyệt cục bộ. connect_over_cdp kết nối với một phiên từ xa, vì vậy playwright install không phải là một phần của quy trình làm việc này.
Kết nối và Đếm Byte
Playwright kết nối qua CDP, và một phiên CDP thô mở trên cùng một trang đếm những gì đi qua dây:
python
browser = await playwright.chromium.connect_over_cdp(ENDPOINT)
page = await browser.new_page()
cdp = await page.context.new_cdp_session(page)
await cdp.send("Network.enable")
transferred = {"bytes": 0}
cdp.on(
"Network.loadingFinished",
lambda event: transferred.__setitem__(
"bytes", transferred["bytes"] + event.get("encodedDataLength", 0)
),
)
encodedDataLength là phép đo quan trọng. Miền mạng của Giao thức DevTools Chrome định nghĩa nó là tổng số byte nhận được cho một yêu cầu, vì vậy nó đếm lưu lượng nén hơn là kích thước tài liệu chưa nén. Đây là những gì mà một proxy đo lường và hóa đơn băng thông phản ánh.
Tổng hợp nó qua Network.loadingFinished cho một số liệu cho mỗi lần tải trang, mà không có ước tính nào trong quy trình.
Thêm Lớp Định tuyến
Chặn là một quyết định định tuyến thực hiện trên mỗi yêu cầu:
python
BLOCKED = {"image", "media", "font"}
async def router(route):
if route.request.resource_type in BLOCKED:
await route.abort()
else:
await route.continue_()
await page.route("**/*", router)
Mỗi yêu cầu bây giờ đều đi qua router, mà hoặc là hủy bỏ nó hoặc cho phép nó tiếp tục — hợp đồng xử lý trong API định tuyến trang của Playwright. Một trình xử lý không thực hiện cả hai sẽ treo yêu cầu cho đến khi điều hướng hết thời gian, vì vậy mỗi nhánh đều phải kết thúc bằng abort hoặc continue_.
resource_type đến từ phân loại của trình duyệt về lý do mà một yêu cầu được thực hiện, cùng khái niệm mà Tiêu chuẩn Fetch gọi là điểm đến của yêu cầu. Việc so khớp theo nó bền vững hơn việc so khớp URL theo phần mở rộng, vì một font được phục vụ từ /assets/a8f3c2 mà không có phần mở rộng vẫn phân loại là font.
Đây là hình ảnh phản chiếu của việc đọc lưu lượng truy cập thay vì dừng nó — để kéo dữ liệu ra khỏi các yêu cầu mà một trang thực hiện, xem chặn API JSON ẩn của một trang.
Đo Lường Ba Trang
Kịch bản đầy đủ tải mỗi mục tiêu dưới ba hồ sơ và in ra số byte, tiết kiệm, thời gian, và số lượng yếu tố vẫn có thể trích xuất. Hai cấu trúc tương phản là đủ để làm nổi bật vấn đề; thêm mục tiêu riêng của bạn vào TARGETS để đo lường theo cách tương tự:
python
import asyncio
import os
import time
from playwright.async_api import async_playwright
ENDPOINT = (
"wss://browser.scrapeless.com/api/v2/browser"
f"?token={os.environ['SCRAPELESS_API_KEY']}&session_ttl=300&proxy_country=US"
)
TARGETS = [
("books.toscrape.com", "https://books.toscrape.com/", "article.product_pod"),
("quotes.toscrape.com", "https://quotes.toscrape.com/", "div.quote"),
]
PROFILES = {
"baseline": set(),
"media-only": {"image", "media", "font"},
"media+css": {"image", "media", "font", "stylesheet"},
}
PAUSE_SECONDS = 5
async def measure(playwright, url, selector, blocked):
"""Tải một trang và trả về (số byte qua mạng, giây, số yếu tố khớp).
Mỗi phép đo có kết nối riêng của nó nên không có bộ nhớ cache HTTP nào được chia sẻ giữa
các hồ sơ; một bộ nhớ cache nóng sẽ làm giảm giá trị byte mà một lần tải lạnh thực sự tốn.
"""
browser = await playwright.chromium.connect_over_cdp(ENDPOINT)
try:
page = await browser.new_page()
cdp = await page.context.new_cdp_session(page)
await cdp.send("Network.enable")
transferred = {"bytes": 0}
cdp.on(
"Network.loadingFinished",
lambda event: transferred.__setitem__(
"bytes", transferred["bytes"] + event.get("encodedDataLength", 0)
),
)
if blocked:
async def router(route):
if route.request.resource_type in blocked:
await route.abort()
else:
await route.continue_()
await page.route("**/*", router)
started = time.monotonic()
await page.goto(url, wait_until="load", timeout=120_000)
elapsed = time.monotonic() - started
matched = await page.locator(selector).count()
return transferred["bytes"], elapsed, matched
finally:
await browser.close()
async def main():
print(f"{'target':22}{'profile':12}{'bytes':>10}{'saved':>7}{'time':>8}{'matched':>9}")
async with async_playwright() as playwright:
for name, url, selector in TARGETS:
baseline_bytes = None
for profile, blocked in PROFILES.items():
# Tách biệt các lần tải: điều này mở một phiên mới cho mỗi phép đo,
# và chín lần tải trang liên tiếp thì không lịch sự với mục tiêu.
await asyncio.sleep(PAUSE_SECONDS)
transferred, elapsed, matched = await measure(playwright, url, selector, blocked)
if baseline_bytes is None:
baseline_bytes = transferred
saved = round((1 - transferred / baseline_bytes) * 100)
print(f"{name:22}{profile:12}{transferred:>10,}{saved:>6}%{elapsed:>7.2f}s{matched:>9}")
if __name__ == "__main__":
asyncio.run(main())
Một lần chạy nó:
text
target profile bytes saved time matched
books.toscrape.com baseline 337,691 0% 4.22s 20
books.toscrape.com media-only 111,939 67% 5.62s 20
books.toscrape.com media+css 76,329 77% 2.56s 20
quotes.toscrape.com baseline 26,731 0% 5.86s 10
quotes.toscrape.com media-only 26,739 0% 5.22s 10
quotes.toscrape.com media+css 2,125 92% 3.12s 10
Giữ hai dòng bị chặn của quotes.toscrape.com trong tâm trí: việc chặn hình ảnh, phương tiện và phông chữ không tiết kiệm gì cả ở đó, và việc chặn bảng kiểu trên hết đã tiết kiệm gần như tất cả. Phần tiếp theo giải thích lý do — và lý do tại sao cùng một trang đôi khi báo cáo tiết kiệm 65% từ hồ sơ phương tiện đó.
Những gì con số nói
Phần trăm là cách tự nhiên để đọc điều này, và chúng là điều ít ổn định nhất trong số đó. Các hồ sơ bị chặn rất dễ lặp lại; các điểm chuẩn thì không, và tỷ lệ phần trăm là tỷ lệ của hai điều này.
Trang danh mục gần như có thể lặp lại hoàn hảo — ba số đo cơ bản của nó nằm trong khoảng 0,004% so với nhau. Bài viết bách khoa toàn thư trải rộng khoảng 7% qua các lần chạy. Và danh sách văn bản hóa ra có hai chế độ: hầu hết các tải khoảng 26,700 byte, nhưng khoảng một tải trong ba tải khoảng 75,820, vì trình duyệt đôi khi kéo các nhị phân phông chữ mà bảng kiểu tham chiếu và đôi khi không. Sự “tiết kiệm” được đo lường từ các phương tiện bị chặn dao động giữa 1% và 65% chỉ trên cơ sở đó, với nội dung thực tế của trang không thay đổi.
Vì vậy, hãy đọc các cột tuyệt đối trước. Các số trung vị của ba lần chạy, với một bài viết bách khoa thực tế được đo lường theo cách tương tự được thêm vào như một điểm dữ liệu thứ ba:
| Trang | Cơ sở | Phương tiện bị chặn | Phương tiện bị chặn + CSS | Các yếu tố được trích xuất |
|---|---|---|---|---|
| books.toscrape.com — danh mục hình ảnh | 337,692 B | 111,970 B | 76,237 B | 20 / 20 / 20 |
| quotes.toscrape.com — danh sách văn bản | 27,004 B | 26,725 B | 2,121 B | 10 / 10 / 10 |
| en.wikipedia.org — bài viết | 473,437 B | 359,075 B | 358,770 B | 36 / 36 / 36 |
Trang danh mục mất hai phần ba trọng lượng của nó do danh sách chặn ba loại, và một mười điểm nữa cho các bảng kiểu — một mức giảm 67% và 77% so với mức cơ sở đủ ổn định để tin tưởng vào những con số đó.
Danh sách văn bản hầu như không thay đổi khi phương tiện bị chặn, sau đó giảm xuống còn 2,121 byte khi các bảng kiểu cũng bị chặn. Con số đó là tài liệu HTML tự nó, và nó giống hệt nhau trong mọi lần chạy của mọi hồ sơ đã chặn CSS. Một phân tích theo yêu cầu giải thích lý do: trang này thực hiện bốn yêu cầu, và ba trong số đó là các bảng kiểu tổng cộng khoảng 24,500 byte so với 2,121 byte mã hóa. Hoàn toàn không có hình ảnh nào trên đó.
Trang bài viết từ bỏ một phần tư cho việc chặn phương tiện và sau đó không có gì đo lường được cho CSS — số trung vị di chuyển 305 byte trong khi các lần chạy media+css dao động trên 29,000. Trên trang này, chặn các bảng kiểu không phải là một sự tiết kiệm.
Việc trích xuất sống sót ở khắp mọi nơi. 20 sản phẩm, 10 câu trích dẫn và 36 đoạn văn đều về dưới mỗi hồ sơ, vì vậy không có bất kỳ sự tiết kiệm nào này làm mất đi một lĩnh vực.
Thời gian cần chú ý đến cảnh báo riêng. Mỗi hồ sơ đều diễn ra nhanh hơn mức cơ sở của nó trong lần chạy được in ở trên, đây chính xác là kết quả sẽ khiến bạn cam kết về việc tăng tốc. Tuy nhiên, trên tập hợp trung vị, điều này không đúng: trang danh mục mất 4.66s ở media+css so với 3.90s cơ sở, và Wikipedia mất 6.57s ở media-only so với 5.86s — cả hai đều chậm hơn trong khi chuyển ít hơn. Việc định tuyến mỗi yêu cầu qua một callback Python thêm một chuyến đi mỗi yêu cầu, và chi phí đó không liên quan đến số byte mà các tài sản bị chặn sẽ mang theo. Hãy coi việc giảm byte là chiến thắng đáng tin cậy và độ trễ là tác dụng phụ cần đo lường cho mỗi mục tiêu.
Bắt đầu không cần thẻ — gói miễn phí bao gồm đo lường chín tải như thế này.
Chọn Hồ Sơ Theo Mục Tiêu
Việc đo lường mất khoảng một phút cho mỗi mục tiêu và thay thế cho công việc đoán:
- Chạy cơ sở và một hồ sơ bị chặn đối với một trang tiêu biểu — một danh mục, không phải trang chủ, vì sự pha trộn tài sản khác nhau theo trang web.
- Chặn
hình ảnh,phương tiện, vàphông chữtheo mặc định. Những điều này chưa bao giờ được phân tích, và nhược điểm là có giới hạn. - Kiểm tra
bảng kiểuriêng thay vì giả định. Nó là lợi ích lớn nhất duy nhất trên một trang ở đây và không liên quan ở một trang khác. Việc trích xuất phụ thuộc vào bố cục là điều cần kiểm tra: các bộ chọn khóa cho các lớp và cấu trúc không bị ảnh hưởng, nhưng bất kỳ điều gì phụ thuộc vào hình học tính toán hoặc khả năng hiển thị đều không. - Không bao giờ chặn
kịch bảnhoặctài liệutrên một trang bạn cần được hiển thị. Một trang được hiển thị bởi khách hàng xây dựng nội dung của nó với các kịch bản mà bạn sẽ vứt bỏ. - Đo lại khi một mục tiêu thiết kế lại. Hồ sơ được điều chỉnh theo sự pha trộn tài sản, và những sự pha trộn tài sản thay đổi.
Khi một trang cần hiển thị nhưng không có tương tác, một cuộc gọi hiển thị HTTP hoàn toàn tránh phiên trình duyệt — trang sản phẩm Scraping Browser và tài liệu kết nối giải thích khi nào một phiên đầy đủ đáng giá.
Khắc Phục Sự Cố
Thời gian điều hướng hết hạn ngay khi định tuyến được bật. Một mã đường dẫn qua bộ xử lý kết thúc mà không gọi abort hoặc continue_. Mỗi nhánh, bao gồm cả đường dẫn ngoại lệ, phải giải quyết được tuyến đường.
Các yêu cầu bị chặn vẫn xuất hiện trong số byte. Network.enable đã được gửi sau khi page.route được đăng ký, hoặc trên một phiên CDP khác so với trang đang được đo. Tạo phiên từ ngữ cảnh của chính trang và kích hoạt miền trước khi điều hướng.
Trang không có nội dung và các bộ chọn không khớp với bất kỳ thứ gì. script hoặc document nằm trong danh sách chặn. Cả hai đều cần thiết trên một trang được tạo ra ở phía khách.
Số byte thay đổi hơn một vài phần trăm giữa các lần chạy. Trang không tải cùng một tài sản ở mỗi lần tải. Trên danh sách văn bản được đo ở đây, các nhị phân phông chữ được tham chiếu bởi một tập tin kiểu đã được tải ở một số lần và không ở những lần khác, làm cho mức cơ sở thay đổi từ khoảng 26,700 byte lên khoảng 75,820. Prend một số trung bình trên nhiều lần chạy, và so sánh byte tuyệt đối thay vì tỷ lệ phần trăm, vì một mức cơ sở di động làm biến dạng tỷ lệ.
Tiết kiệm có vẻ lớn nhưng dữ liệu bị mất. So sánh số lượng đã trích xuất với mức cơ sở trước khi tin tưởng một hồ sơ, như kịch bản ở trên làm. Một hồ sơ cắt giảm byte và dữ liệu không phải là một tối ưu hóa.
Kết luận
Băng thông tăng lên với mỗi trang bạn thu thập, và không giống như hầu hết các chi phí, nó có thể được đo trong khoảng một phút. Hai dòng CDP tạo ra một số mà hoặc biện minh cho một danh sách chặn cho một mục tiêu cụ thể hoặc không.
Những con số ở đây chống lại một mặc định. Chặn hình ảnh là quyết định trên một trang, vừa phải trên một trang khác, và không liên quan trên một trang thứ ba không có hình ảnh, trong khi chiến thắng lớn nhất trong bộ dữ liệu đến từ việc chặn các tập tin kiểu chính xác trên trang thứ ba đó. Chạy mức cơ sở đối với mục tiêu của bạn trước khi áp dụng bất kỳ danh sách chặn nào, bao gồm cả cái này — và chạy nó nhiều hơn một lần, vì một trong ba trang này không đưa ra cùng một câu trả lời hai lần.
Sẵn sàng để đo mục tiêu của riêng bạn? Tạo một tài khoản Scrapeless miễn phí, xuất khóa của bạn, và chạy kịch bản chống lại một trang bạn đã thu thập. Các giới hạn kế hoạch có trên trang giá cả, và cấu hình proxy bao phủ phía chi phí của cùng một hóa đơn.
Câu hỏi thường gặp
H: Các loại tài nguyên nào an toàn để chặn khi thu thập dữ liệu?
image, media, và font đều an toàn trên hầu hết các công việc trích xuất, vì không có bộ phân tích nào đọc chúng. stylesheet an toàn bất cứ khi nào các bộ chọn của bạn phụ thuộc vào các lớp và cấu trúc thay vì bố cục tính toán, điều này bao phủ hầu hết các hoạt động thu thập dữ liệu, và nó là tiết kiệm lớn nhất trong các phép đo này. Để script, document, xhr, và fetch ở một trang xây dựng nội dung ở phía khách.
H: Chặn hình ảnh có thật sự tiết kiệm băng thông không?
Nó hoàn toàn phụ thuộc vào trang, đó là lý do tại sao một con số duy nhất có thể gây nhầm lẫn. Danh sách chặn giống nhau được đo ở đây đã tiết kiệm 67% trên một danh mục hình ảnh, khoảng một phần tư trên một bài viết bách khoa, và cơ bản không tiết kiệm gì trên một danh sách chỉ có văn bản không có hình ảnh. Chạy một mức cơ sở đối với mục tiêu của bạn và so sánh byte tuyệt đối — một phút đo lường vượt xa bất kỳ tỷ lệ phần trăm nào đã công bố.
H: Chặn tài nguyên có làm cho việc thu thập dữ liệu nhanh hơn không?
Ít đáng tin cậy hơn so với việc giảm byte. Mỗi hồ sơ ở đây đã chuyển giao ít hơn mức cơ sở của nó, nhưng thời gian thực tế lại di chuyển theo cả hai hướng, vì việc chuyển mỗi yêu cầu thông qua một trình xử lý tốn một chuyến đi. Trên các trang có ít tài sản nặng, chi phí này có thể lớn hơn các khoản tiết kiệm, vì vậy hãy coi độ trễ là điều cần xác minh thay vì giả định.
H: Tôi nên sử dụng page.route hay Network.setBlockedURLs của CDP?
page.route khớp với loại tài nguyên, đây là điều bạn thường muốn, và giữ logic trong Python nơi dễ dàng thay đổi theo từng mục tiêu. Các lệnh chặn của CDP khớp với mẫu URL, vì vậy chúng phù hợp để chặn một máy chủ cụ thể đã biết — một điểm cuối phân tích, chẳng hạn — thay vì một lớp tài sản nào đó. Hai cái này có thể kết hợp nếu bạn cần cả hai.
H: Chặn tài nguyên có làm cho một trang hoạt động khác biệt so với một trình duyệt thực không?
Có, theo nghĩa là một phiên mà không bao giờ yêu cầu hình ảnh không thực hiện những gì một trình duyệt thường làm. Giữ danh sách chặn ở mức mà việc trích xuất của bạn thực sự cần thiết, và nếu hành vi của một mục tiêu thay đổi sau khi bạn kích hoạt chặn, hãy thu hẹp danh sách và đo lại thay vì mở rộng nó.
H: Làm thế nào để tôi đếm byte mà không có phiên CDP?
Bạn có thể ước lượng nó bằng cách cộng tổng chiều dài của các phản hồi trong một trình xử lý response của Playwright, nhưng đó là kích thước đã được giải nén thay vì byte đã được chuyển, và chúng bỏ lỡ các yêu cầu thất bại hoặc được phục vụ từ bộ đệm. Một lựa chọn thay thế trong trang là thuộc tính transferSize được định nghĩa bởi tiêu chuẩn Thời gian Tài nguyên W3C, đọc qua performance.getEntriesByType("resource"), cái này gần với lượng đúng hơn nhưng chịu sự hạn chế về cùng nguồn khiến nó bằng không đối với các tài sản của bên thứ ba. encodedDataLength từ Network.loadingFinished là thông tin mà chính trình duyệt ghi lại trên mạng cho mỗi yêu cầu bất kể nguồn gốc, vì vậy nó đáng giá với hai dòng thêm nữa.
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.



