🎯 Um navegador em nuvem personalizável e anti-detecção alimentado por Chromium desenvolvido internamente, projetado para rastreadores web e agentes de IA. 👉Experimente agora
De volta ao blog

Bloqueando Recursos no Playwright: O Que Realmente Economiza

05-Aug-2026

Resumo:

  • "Bloquear imagens para economizar largura de banda" depende totalmente da página. Medido em três páginas, bloquear imagens, mídias e fontes reduziu a transferência em 67% em um catálogo pesado em imagens, cerca de 25% em uma página de artigo, e praticamente nada em uma listagem apenas de texto que não possui imagens.
  • Na página apenas de texto, a folha de estilo era o conteúdo. Adicionar stylesheet à lista de bloqueio reduziu para 2.121 bytes — apenas o documento HTML — porque três das suas quatro requisições são CSS.
  • As economias de bytes são confiáveis; as economias de tempo não são. Cada perfil bloqueado transferiu menos, mas o tempo em relógio variou em ambas as direções entre as execuções, porque interceptar cada requisição tem seu próprio custo.
  • Bloquear não custa dados. A contagem de elementos extraídos foi idêntica em todas as nove medições — 20 produtos, 10 citações, 36 parágrafos — então nada útil foi desperdiçado.
  • Meça bytes absolutos, não porcentagens, e meça mais de uma vez. Uma página aqui tem uma linha de base bimodal que varia sua economia aparente entre 1% e 65% sem que seu conteúdo mude.
  • Comece grátis. O Scraping Browser tem uma camada gratuita, e toda a medição neste post é de nove carregamentos de página.

Playwright conecta-se ao Scraping Browser Scrapingless através de um endpoint WebSocket CDP, o que significa que uma sessão de navegador na nuvem se comporta como uma local — e é cobrada como uma remota. Cada imagem, fonte e folha de estilo que uma página carrega é tráfego que você pagou para movimentar.

O conselho padrão é bloquear imagens. Esse conselho é repetido em toda parte sem um número anexado, então este post anexa um: o mesmo script, três páginas, três perfis de bloqueio, bytes contados pelo protocolo.

Um navegador busca tudo o que um humano veria. Para extração, a maior parte disso é desperdício — ninguém analisa uma fonte da web. O peso da página na web é rastreado publicamente pelo relatório de peso da página do HTTP Archive, e figuras agregadas como essas são sobre as quais o conselho de bloquear imagens é construído.

As agregações são onde esse conselho falha. Você não extrai a página mediana; você extrai um alvo, cuja mistura de ativos pode não parecer nada com a mediana.

Pré-requisitos

  • Python 3.9 ou posterior e pip install playwright
  • Uma chave de API Scrapeless na variável de ambiente SCRAPELESS_API_KEY

Nenhum download local do navegador é necessário. connect_over_cdp se conecta a uma sessão remota, então playwright install não faz parte deste fluxo de trabalho.

Conectar e Contar Bytes

Playwright conecta-se via CDP, e uma sessão CDP "crua" aberta na mesma página conta o que atravessa a rede:

python Copy
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 é a medição que importa. O domínio Network do Protocolo DevTools do Chrome define isso como o total de bytes recebidos por uma requisição, então conta a transferência comprimida em vez do tamanho do documento descomprimido. É isso que um proxy mede e o que uma conta de largura de banda reflete.

Somar isso em Network.loadingFinished fornece um número por carregamento de página, sem estimativas em nenhum lugar do pipeline.

Adicionar a Camada de Roteamento

Bloquear é uma decisão de roteamento feita por requisição:

python Copy
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)

Cada requisição agora passa pelo router, que ou a interrompe ou a permite prosseguir — o contrato do manipulador em a API de roteamento de páginas do Playwright. Um manipulador que não faz nenhuma das duas suspende a requisição até que a navegação atinja o tempo limite, então cada ramificação deve terminar em abort ou continue_.

resource_type vem da própria classificação do navegador sobre por que uma requisição foi feita, o mesmo conceito que o Fetch Standard chama de destino da requisição. A correspondência nisso é mais durável do que combinar URLs por extensão, porque uma fonte servida de /assets/a8f3c2 sem extensão ainda classifica como font.
Esta é a imagem espelhada de ler o tráfego, em vez de interrompê-lo — para extrair dados das requisições que uma página faz, veja interceptando a API JSON oculta de uma página.

Medir Três Páginas

O script completo carrega cada alvo sob três perfis e imprime bytes, economias, tempo e o número de elementos ainda extraíveis. Dois acessórios contrastantes são suficientes para ilustrar o ponto; adicione seu próprio alvo à TARGETS para medi-lo da mesma forma:

python Copy
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):
    """Carrega uma página e retorna (bytes transmitidos, segundos, elementos correspondentes).

    Cada medição obtém sua própria conexão, então nenhum cache HTTP é compartilhado entre
    perfis; um cache aquecido subestimaria os bytes que um carregamento frio realmente custa.
    """
    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():
                # Espaçar os carregamentos: isso abre uma nova sessão por medição,
                # e nove carregamentos de página em sequência não são educados com o alvo.
                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())

Uma execução dele:

text Copy
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

Mantenha em mente as duas linhas bloqueadas de quotes.toscrape.com: bloquear imagens, mídias e fontes não economizou nada ali, e bloquear folhas de estilo além disso economizou quase tudo. A próxima seção explica o porquê — e por que a mesma página às vezes relata uma economia de 65% desse mesmo perfil de mídia.

O Que os Números Dizem

As porcentagens são a maneira natural de ler isso, e elas são a coisa menos estável nisso. Perfis bloqueados são altamente repetíveis; as linhas de base não são, e a porcentagem é uma razão entre os dois.
A página do catálogo é quase perfeitamente repetível — suas três medições de linha de base ficaram dentro de 0,004% uma da outra. O artigo da enciclopédia variou cerca de 7% entre as execuções. E a listagem de texto acabou tendo dois modos: a maioria das cargas transfere cerca de 26.700 bytes, mas aproximadamente uma em cada três cargas transfere cerca de 75.820, porque o navegador às vezes puxa os binários da fonte que a folha de estilo referencia e às vezes não. Sua "economia" medida de mídia bloqueada varia entre 1% e 65% somente com base nisso, com o conteúdo real da página inalterado.

Então leia as colunas absolutas primeiro. Medianas de três execuções, com um artigo de enciclopédia do mundo real medido da mesma forma adicionado como um terceiro ponto de dados:

Página Linha de base Mídia bloqueada Mídia bloqueada + CSS Elementos extraídos
books.toscrape.com — catálogo de imagens 337.692 B 111.970 B 76.237 B 20 / 20 / 20
quotes.toscrape.com — listagem de texto 27.004 B 26.725 B 2.121 B 10 / 10 / 10
en.wikipedia.org — artigo 473.437 B 359.075 B 358.770 B 36 / 36 / 36

A página do catálogo perde dois terços de seu peso para uma lista de bloqueio de três tipos, e mais dez pontos para folhas de estilo — uma redução de 67% e 77% contra uma linha de base estável o suficiente para confiar nesses números.

A listagem de texto mal se altera quando a mídia é bloqueada, depois colapsa para 2.121 bytes quando as folhas de estilo também são bloqueadas. Esse número é o documento HTML por si só, e foi idêntico em cada execução de cada perfil que bloqueou CSS. Uma divisão por solicitação explica o porquê: a página faz quatro solicitações, e três delas são folhas de estilo totalizando cerca de 24.500 bytes contra 2.121 bytes de marcação. Não há imagens nela.

A página do artigo cede um quarto para o bloqueio de mídia e depois nada mensurável para CSS — a mediana se moveu 305 bytes enquanto as próprias execuções de media+css variaram em 29.000. Nesta página, bloquear folhas de estilo não é uma economia.

A extração sobreviveu em todos os lugares. 20 produtos, 10 citações e 36 parágrafos retornaram sob cada perfil, então nenhuma dessas economias custou um campo.

Os tempos merecem seu próprio aviso. Cada perfil acabou sendo mais rápido do que sua linha de base na execução impressa acima, que é exatamente o resultado que poderia te tentar a prometer uma aceleração. Através do conjunto de medianas, isso não se sustenta: a página do catálogo levou 4,66s em media+css contra 3,90s de linha de base, e a Wikipedia levou 6,57s em media-only contra 5,86s — ambas mais lentas enquanto transferem menos. Roteamento de cada solicitação através de um retorno de chamada Python adiciona uma viagem de ida e volta por solicitação, e esse custo não está relacionado a quantos bytes os ativos bloqueados teriam carregado. Trate a redução de bytes como a vitória confiável e a latência como um efeito colateral a ser medido por alvo.

Começar não precisa de cartão — o plano gratuito cobre uma medição de nove cargas como esta.

Escolha um Perfil Por Alvo

A medição leva cerca de um minuto por alvo e substitui a adivinhação:

  • Execute a linha de base e um perfil bloqueado contra uma página representativa — uma listagem de categoria, não a página inicial, uma vez que as misturas de ativos diferem em um site.
  • Bloqueie image, media e font por padrão. Estes nunca são analisados, e a desvantagem é limitada.
  • Teste stylesheet separadamente em vez de assumir. Foi a única grande vitória em uma página aqui e irrelevante em outra. A extração dependente de layout é a coisa a ser verificada: seletores vinculados a classes e estrutura não são afetados, mas qualquer coisa que dependa de geometria computada ou visibilidade não é.
  • Nunca bloqueie script ou document em uma página que você precisa renderizar. Uma página renderizada pelo cliente constrói seu conteúdo com os scripts que você estaria descartando.
  • Re-meça quando um alvo redesenhar. O perfil é ajustado para uma mistura de ativos, e as misturas de ativos mudam.

Quando uma página precisa de renderização, mas sem interação, uma chamada de renderização HTTP evita completamente a sessão do navegador — a página do produto Scraping Browser e a documentação de conexão cobrem quando uma sessão completa justifica seu custo.

Solução de Problemas

A navegação expira assim que o roteamento é ativado. Um caminho de código através do manipulador termina sem chamar abort ou continue_. Cada ramo, incluindo o caminho de exceção, deve resolver a rota.

Solicitações bloqueadas ainda aparecem na contagem de bytes. Network.enable foi enviado após page.route ser registrado, ou em uma sessão CDP diferente da página que está sendo medida. Crie a sessão a partir do próprio contexto da página e ative o domínio antes de navegar.
A página aparece em branco e os seletores não correspondem a nada. script ou document está na lista de bloqueio. Ambos são necessários em uma página renderizada pelo cliente.

Contagens de bytes variam em mais de alguns porcentos entre execuções. A página não está buscando os mesmos ativos a cada carregamento. Na lista de texto medida aqui, os binários de fontes referenciados por uma folha de estilo foram carregados em algumas execuções e não em outras, movendo a linha de base de cerca de 26.700 bytes para cerca de 75.820. Pegue uma mediana em várias execuções e compare bytes absolutos em vez de porcentagens, pois uma linha de base móvel distorce a proporção.

As economias parecem grandes, mas registros desaparecem. Compare a contagem extraída com a linha de base antes de confiar em um perfil, como faz o script acima. Um perfil que corta bytes e registros não é uma otimização.

Conclusão

A largura de banda cresce a cada página que você coleta, e, ao contrário da maioria dos custos, pode ser medida em cerca de um minuto. Duas linhas de CDP produzem um número que justifica ou não uma lista de bloqueio para um alvo específico.

O que os números aqui argumentam contra é um padrão. Bloquear imagens foi decisivo em uma página, moderado em outra e irrelevante em uma terceira que não possui imagens, enquanto a maior economia individual no conjunto veio do bloqueio de folhas de estilo exatamente naquela terceira página. Execute a linha de base contra seu próprio alvo antes de adotar qualquer lista de bloqueio, incluindo esta — e execute-a mais de uma vez, porque uma dessas três páginas não dá a mesma resposta duas vezes.

Pronto para medir seus próprios alvos? Crie uma conta gratuita no Scrapeless, exporte sua chave e execute o script contra uma página que você já coleta. Os limites do plano estão na página de preços, e a configuração de proxy cobre o lado de egress do mesmo custo.

FAQ

Q: Quais tipos de recursos são seguros para bloquear ao raspar?

imagem, mídia e fonte são seguros em quase qualquer trabalho de extração, porque nenhum parser os lê. folha de estilo é seguro sempre que seus seletores dependam de classes e estrutura em vez de layout computado, o que cobre a maioria das raspagens, e foi a maior economia individual nessas medições. Deixe script, document, xhr e fetch em paz em qualquer página que construa conteúdo do lado do cliente.

Q: Quanto de largura de banda o bloqueio de imagens realmente economiza?

Depende totalmente da página, razão pela qual um único número é enganoso. A mesma lista de bloqueio medida aqui economizou 67% em um catálogo de imagens, cerca de um quarto em um artigo de enciclopédia e essencialmente nada em uma lista de texto apenas que não tem imagens. Execute uma linha de base contra seu próprio alvo e compare bytes absolutos — um minuto de medição supera qualquer porcentagem publicada.

Q: Bloquear recursos torna a raspagem mais rápida?

Menos confiavelmente do que reduz bytes. Cada perfil aqui transferiu menos do que sua linha de base, mas os tempos em relógio se moveram em ambas as direções, porque roteirizar cada solicitação através de um manipulador custa uma viagem de ida e volta. Em páginas com poucos ativos pesados, essa sobrecarga pode superar as economias, então trate a latência como algo a ser verificado em vez de assumir.

Q: Devo usar page.route ou CDP Network.setBlockedURLs?

page.route corresponde ao tipo de recurso, que é o que você geralmente deseja, e mantém a lógica em Python, onde é fácil mudar por alvo. Os comandos de bloqueio do CDP correspondem a padrões de URL, então eles servem para bloquear um host específico conhecido — um ponto de extremidade de análise, por exemplo — em vez de uma classe inteira de ativo. Os dois se compõem se você precisar de ambos.

Q: Bloquear recursos fará com que uma página se comporte de forma diferente de um navegador real?

Sim, no sentido de que uma sessão que nunca solicita imagens não está fazendo o que um navegador normalmente faz. Mantenha as listas de bloqueio no que sua extração realmente precisa, e se o comportamento de um alvo mudar após ativar o bloqueio, reduza a lista e re-meça em vez de ampliá-la.

Q: Como conto bytes sem a sessão CDP?
Você pode aproximar isso somando os tamanhos dos corpos de resposta em um manipulador response do Playwright, mas esses são tamanhos descompactados em vez de bytes transferidos, e eles não incluem solicitações que falham ou são servidas do cache. Uma alternativa em página é o atributo transferSize definido pela especificação de Tempo de Recursos do W3C, lido através de performance.getEntriesByType("resource"), que está mais próximo da quantidade correta, mas está sujeito a restrições de origem cruzada que o zeram para ativos de terceiros. O encodedDataLength de Network.loadingFinished é o que o próprio navegador registrou na rede para cada solicitação, independentemente da origem, portanto, vale a pena as duas linhas extras.

Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.

Artigos mais populares

Catálogo