🎯 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

parsel Web Scraping: Seletores CSS e XPath em Python

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

23-Jul-2026

TL;DR:

  • parsel seleciona dados de HTML com CSS e XPath, sobre o mesmo documento — é o motor de seleção que o Scrapy utiliza, disponível como uma biblioteca independente.
  • O que parsel não faz é buscar. Ele não possui um cliente HTTP e não executa JavaScript; você fornece a marcação e ele constrói uma árvore consultável.
  • A diferença aparece em um script. Um simples GET em uma página de demonstração renderizada por JavaScript fornece 0 nós de citação ao parsel; a mesma URL obtida através da API de Scraping Universal Scrapeless com js_render fornece todas as 10.
  • CSS e XPath, lado a lado. Uma execução ao vivo obteve os mesmos 10 autores de duas maneiras — sel.css("small.author::text") e sel.xpath("//small[@class='author']/text()") — a partir do HTML renderizado.
  • As duas camadas permanecem separadas. Scrapeless busca e renderiza; parsel seleciona. Nenhum deles interfere no trabalho do outro.
  • Livre para começar do lado da busca. Crie sua chave da API Scrapeless em app.scrapeless.com.

O que parsel é, e o que não é

parsel é uma biblioteca Python para extrair dados de HTML e XML usando seletores CSS e expressões XPath. É a camada de seleção sobre a qual o Scrapy é construído, embalada para que você possa usá-la em qualquer lugar, e envolve o lxml por baixo, de modo que ambas as linguagens de seleção funcionem em relação à mesma árvore de análise rápida. A razão para escolhê-la ao invés de um parser apenas CSS é o XPath: quando um campo é definido pela sua posição, seu texto ou sua relação com um irmão, em vez de uma classe, o XPath expressa isso e o CSS não consegue.

É uma biblioteca de seleção e nada mais. parsel não possui um cliente HTTP, não mantém sessão e não executa JavaScript. Dê-lhe uma string e ele constrói um Selector que você pode consultar; peça-lhe para buscar uma URL e não há um método para isso. Portanto, toda configuração de "scraping web com parsel" consiste em duas camadas: algo que retorna HTML fiel e parsel que seleciona a partir disso. Este guia utiliza a API de Scraping Universal Scrapeless para a primeira camada, porque um cliente HTTP simples retorna a marcação pré-renderizada em qualquer página que construa seu conteúdo com JavaScript. O mais amplo tutorial de scraping web em Python aborda o ecossistema ao redor.

Instalar

parsel e requests são toda a cadeia de ferramentas. A versão contra a qual este guia foi escrito é parsel 1.11.0:

bash Copy
pip install "parsel==1.11.0" requests

Mantenha sua chave no ambiente, nunca no código-fonte:

bash Copy
export SCRAPELESS_API_KEY="sk_sua_chave_scrapeless"

Obtenha HTML que vale a pena analisar

A qualidade da seleção é limitada pela fidelidade da busca, então comece por aí. Em uma página renderizada por JavaScript, a marcação que um cliente HTTP simples recebe não é a marcação que um leitor vê: o conteúdo chega apenas depois que os scripts constroem o DOM, um ciclo de vida definido pela especificação de scripting HTML. Um script conta a diferença através do próprio parsel:

python Copy
# fidelity.py — o que parsel vê: GET simples vs renderização do lado do servidor
import os

import requests
from parsel import Selector

URL = "https://quotes.toscrape.com/js/"

plain = requests.get(URL, timeout=60).text
print("caracteres GET simples:", len(plain), "| nós de citação:", len(Selector(text=plain).css("div.quote")))

resp = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
    headers={"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
    json={"actor": "unlocker.webunlocker", "input": {"url": URL, "js_render": True}},
    timeout=120,
)
resp.raise_for_status()
rendered = resp.json().get("data", "")
print("caracteres renderizados:", len(rendered), "| nós de citação:", len(Selector(text=rendered).css("div.quote")))

A execução imprime 0 nós de citação para a busca simples e 10 para a renderizada:

text Copy
caracteres GET simples: 5806 | nós de citação: 0
caracteres renderizados: 8940 | nós de citação: 10

Renderização, desbloqueio e roteamento de proxy acontecem todos do lado do servidor em um único POST — a API de Scraping Universal é a camada de busca, e a marcação renderizada é o que parsel deve selecionar.

Extrair com CSS e XPath

Com HTML real em mãos, parsel faz a extração. css e xpath ambos retornam um SelectorList; get retorna a primeira correspondência e getall retorna todas as correspondências. O pseudo-elemento ::text e o teste do nó text() ambos puxam texto, então você pode ler o mesmo campo de qualquer maneira — CSS segue a especificação de Seletores W3C e XPath segue a especificação XPath W3C:

python Copy
# extract.py — buscar a página renderizada, então selecionar com CSS e XPath
import os

import requests

from parsel import Selector

resp = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
json={"actor": "unlocker.webunlocker", "input": {"url": "https://quotes.toscrape.com/js/", "js_render": True}},
timeout=120,
)
resp.raise_for_status()
sel = Selector(text=resp.json().get("data", ""))

css_nodes = sel.css("div.quote")
xpath_authors = sel.xpath("//div[@class='quote']//small[@class='author']/text()").getall()
print("nós de citação css:", len(css_nodes))
print("autores xpath:", len(xpath_authors))

records = []
for quote in css_nodes:
records.append({
"texto": quote.css("span.text::text").get(),
"autor": quote.xpath(".//small[@class='author']/text()").get(),
"tags": quote.css("a.tag::text").getall(),
})
print("primeiro autor:", records[0]["autor"])
print("primeiras tags:", records[0]["tags"])

A execução ao vivo selecionou todos os 10 registros, com CSS e XPath retornando os mesmos autores:

text Copy
nós de citação css: 10
autores xpath: 10
primeiro autor: Albert Einstein
primeiras tags: ['mudança', 'pensamentos profundos', 'pensando', 'mundo']

Esse é o scraper completo: um POST para buscar e renderizar, um Selector para consultar. Misturar CSS para os campos fáceis e XPath para os posicionais — quote.xpath("...") é executado relativo a cada nó — é o padrão que mantém um scraper legível à medida que as páginas se tornam complicadas.

Obtenha sua chave de API no plano gratuito: app.scrapeless.com

Padrões Avançados

  • Use XPath quando o CSS esgotar suas opções. Selecionar por texto (//a[contains(text(), "Próximo")]), por posição ((//tr)[2]), ou por um eixo (following-sibling::td) é território do XPath; o CSS não tem equivalente.
  • Cadencie seletores relativos a um nó. quote.css(...) e quote.xpath(".//...") ambos se restringem a esse nó — o . inicial no XPath mantém a referência, que é a diferença entre "autores dentro desta citação" e "todos os autores na página".
  • Use get(default="") para evitar None. sel.css("span.missing::text").get(default="") retorna uma string vazia em vez de None, o que impede que um loop sobre registros irregulares quebre na página estranha.
  • Extraia atributos com ::attr() ou @. sel.css("a::attr(href)") e sel.xpath("//a/@href") ambos leem um atributo; use a linguagem que o restante do seletor já está utilizando.

Solução de Problemas

  • Zero nós de uma página que você consegue ver em um navegador. O conteúdo é renderizado em JavaScript e sua busca retornou o HTML pré-renderizado. Conte um seletor conhecido da mesma forma que o primeiro script faz; uma árvore quase vazia é um problema de busca, resolvido com js_render, e não um problema de seletor.
  • get() retorna None. O seletor não correspondeu a nada. Verifique a marcação renderizada, confirme a classe ou caminho e passe um default para que o código subsequente não quebre na falha.
  • XPath retorna elementos quando você queria texto. Adicione /text() ao caminho ou .get() em um seletor CSS ::text; um caminho de elemento nu retorna o nó, não sua string.
  • XPath relativo captura a página inteira. Um caminho começando com // é absoluto mesmo quando chamado em um nó. Prefixe com ..//small — para restringi-lo ao elemento atual.

Conclusão

parsel ganha seu lugar como a camada de seleção que fala ambos os dialetos: CSS para os casos comuns, XPath para aqueles que o CSS não consegue alcançar, sobre uma árvore suportada por lxml. A camada que decide se qualquer uma dessas opções é possível é a busca — a contagem de 0 contra 10 do primeiro script confirma isso — e um POST renderizado pelo servidor fecha a lacuna. Conecte os dois e as dez citações da página de demonstração chegam como registros limpos, selecionados da maneira que melhor lê.

Crie uma conta gratuita no Scrapeless para obter uma chave de API, e a documentação do desenvolvedor cobre os parâmetros unlocker.webunlocker. Verifique preços do Scrapeless quando planejar um trabalho recorrente.

FAQ

Q: O parsel pode raspar sites sozinho?

Não. O parsel seleciona dados de HTML que você já possui; ele não possui cliente HTTP e não executa JavaScript. Combine-o com uma camada de busca — aqui a API Universal Scraping do Scrapeless, que renderiza a página do lado do servidor — e o parsel cuida da extração da marcação retornada.

Q: Qual é a diferença entre parsel e seletores do Scrapy?
Nenhum, na prática — parsel é o mecanismo de seleção que o Scrapy utiliza, lançado como uma biblioteca independente. Se você usou response.css ou response.xpath em uma aranha Scrapy, isso é parsel. Usá-lo diretamente permite que você mantenha a API de seleção sem adotar todo o framework.

P: Devo usar CSS ou XPath com parsel?

Ambos, conforme necessário. CSS é mais curto para seleção baseada em classe e tag; XPath lida com seleção por texto, posição ou eixo, o que o CSS não pode expressar. Parsel executa ambos contra a mesma árvore, então misture-os por campo.

P: O parsel lida com páginas renderizadas em JavaScript?

Não por conta própria — ele nunca executa scripts. Se o conteúdo carregar após o HTML inicial, um simples fetch entrega ao parsel uma árvore vazia. Busque a página através da API Scrapeless com js_render primeiro, depois selecione do HTML renderizado, como este guia faz.

P: É legal fazer scraping com parsel?

A biblioteca de seleção não altera as regras de coleta. Busque apenas páginas públicas, respeite os termos do site e as diretrizes de robôs padronizadas por o Protocolo de Exclusão de Robôs, mantenha os volumes limitados e trate quaisquer dados pessoais de acordo com as leis que se aplicam a você.

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