🎯 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

Web Scraping com pyquery: Análise de HTML Estilo jQuery em Python

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

23-Jul-2026

Resumo:

  • pyquery oferece a você a API do jQuery em Python. Se você conhece $("div.quote").find("small.author").text(), você já sabe usar o pyquery.
  • O que o pyquery não faz é buscar. Ele encapsula o lxml para análise e seleção; não tem cliente HTTP e não executa JavaScript.
  • A diferença aparece em um script. Um GET simples em uma página de demonstração renderizada por JavaScript retorna 0 nós de citação para o pyquery; o mesmo URL através da Scrapeless Universal Scraping API com js_render retorna todas as 10.
  • A extração é real neste guia. Uma execução ao vivo usou .items(), .find() e .text() para extrair todas as 10 citações com autores e tags do HTML renderizado.
  • As duas camadas permanecem separadas. Scrapeless busca e renderiza; o pyquery seleciona. Nenhum deles alcança o trabalho do outro.
  • Gratuito para começar no lado da busca. Crie sua chave de API Scrapeless em app.scrapeless.com.

O que é o pyquery e o que não é

pyquery é uma biblioteca Python que coloca uma API estilo jQuery sobre o lxml. Você encapsula a marcação em um objeto PyQuery — convencionalmente nomeado d — e então seleciona e navega com as mesmas chamadas que um desenvolvedor front-end já usa: d("selector"), .find(), .eq(), .text(), .attr(), .items(). Para qualquer um que venha do navegador, é o caminho mais curto de "eu sei como consultar o DOM" para "eu posso extrair isso em Python", e como se baseia no lxml, a seleção por baixo é rápida.

É uma biblioteca de análise e seleção, nada mais. O pyquery não tem cliente HTTP, não mantém sessão e não executa JavaScript. Dê a ele uma string e ele constrói um documento consultável; peça-lhe para buscar uma URL e, embora tecnicamente possa puxar uma, usa uma solicitação simples sem renderização, que é a ferramenta errada para qualquer página construída por scripts. Portanto, toda configuração de "web scraping com pyquery" tem duas camadas: algo que retorna HTML renderizado fiel, e o pyquery que seleciona a partir disso. Este guia usa a Scrapeless Universal Scraping API para a primeira camada. O mais amplo tutorial de web scraping em Python cobre o ecossistema circundante.

Instalação

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

bash Copy
pip install "pyquery==2.0.1" requests

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

bash Copy
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"

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 por a especificação de scripting HTML. Um script conta a diferença através do próprio pyquery:

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

import requests
from pyquery import PyQuery as pq

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

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

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:", pq(rendered, parser="html")("div.quote").length)

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

A renderização, desbloqueio e roteamento de proxy acontecem todos do lado do servidor naquela única solicitação POST — a Universal Scraping API é a camada de busca, e a marcação renderizada é do que o pyquery seleciona.

Extraia com a API jQuery

Com HTML real em mãos, o pyquery faz a extração da maneira que o jQuery faria. .items() transforma uma seleção em um iterador de objetos PyQuery, .find() escopo um sub-seletor para cada um, e .text() lê o texto — os seletores seguem a especificação de Seletores W3C, a mesma sintaxe usada pelo jQuery:

python Copy
# extract.py — busque a página renderizada e, em seguida, selecione com a API jQuery
import os

import requests
from pyquery import PyQuery as pq

resp = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
python Copy
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()
d = pq(resp.json().get("data", ""), parser="html")

records = []
for quote in d("div.quote").items():
    records.append({
        "text": quote.find("span.text").text(),
        "author": quote.find("small.author").text(),
        "tags": [pq(tag).text() for tag in quote.find("a.tag").items()],
    })
print("registros:", len(records))
print("primeiro autor:", records[0]["author"])
print("primeiras tags:", records[0]["tags"])

A execução ao vivo selecionou todos os 10 registros, tags e tudo mais:

text Copy
registros: 10
primeiro autor: Albert Einstein
primeiras tags: ['change', 'deep-thoughts', 'thinking', 'world']

Esse é o scraper completo: um POST para buscar e renderizar, um objeto PyQuery para consultar. O iterador .items() é o idiom do pyquery que vale a pena lembrar — é o que transforma uma seleção em objetos por registro nos quais você pode .find(), o análogo direto do .each() do jQuery.

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

Padrões Avançados

  • Sempre itere com .items(). Fazer um loop diretamente sobre uma seleção PyQuery gera elementos lxml brutos, não objetos PyQuery, então .find() quebra. .items() te dá objetos encapsulados com a API completa em cada um.
  • Leia atributos com .attr(). quote.find("a::attr(href)") não é uma sintaxe do pyquery; use quote.find("a").attr("href"), exatamente como no jQuery.
  • Passe parser="html" para páginas reais. Isso seleciona o parser HTML permissivo do lxml, que lida com a marcação malformada que os sites reais usam; o padrão pode ser mais rigoroso do que você deseja.
  • Encadeie, não re-consulte. d("div.quote").eq(0).find("small.author") limita cada etapa ao último, o que é tanto mais rápido quanto mais próximo da forma que o jQuery que você já conhece lê.

Solução de Problemas

  • Zero nós de uma página que você pode ver em um navegador. O conteúdo é renderizado por JavaScript e seu fetch retornou o HTML pré-renderizado. Conte um seletor conhecido da maneira como o primeiro script faz; um documento quase vazio é um problema de fetch, corrigido com js_render, não um problema de seletor.
  • .find() gera AttributeError. Você iterou a seleção diretamente em vez de usar .items(), então você obteve um elemento lxml puro. Altere o loop para for x in sel.items():.
  • .text() retorna tudo concatenado. O pyquery junta o texto descendente. Limite o escopo com um seletor mais específico, ou leia um único nó com .eq(0).text().
  • A codificação parece errada. Passe o texto da resposta para o pyquery primeiro com parser="html"; o lxml lê a codificação declarada do documento quando o parser é o HTML.

Conclusão

O pyquery ganha seu lugar como a camada de seleção que fala jQuery: os mesmos comandos .find(), .text(), e .items() que você conhece do navegador, sobre uma árvore lxml rápida. A camada que decide se tudo isso é possível é o fetch — a contagem de 0 versus 10 do primeiro script confirma — 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 você já pensa.

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

FAQ

Q: O pyquery pode raspar sites por conta própria?

Não realmente. O pyquery pode puxar uma URL, mas o faz com uma solicitação simples e sem renderização de JavaScript, então em uma página moderna ele seleciona de uma marcação vazia. Trate-o como um parser: emparelhe-o com uma camada de fetch — aqui a Scrapeless Universal Scraping API, que renderiza a página do lado do servidor — e o pyquery lida com a seleção do HTML retornado.

Q: O pyquery é o mesmo que o jQuery?

Ele espelha a API do jQuery em Python — d("seletor"), .find(), .text(), .attr(), .items() — mas roda do lado do servidor no lxml, não em um navegador, então seleciona de uma marcação estática e não executa scripts ou lida com eventos. A sintaxe de seleção se transfere; o tempo de execução não.

Q: pyquery ou BeautifulSoup?

Preferência. O pyquery lê como jQuery e é uma adaptação natural se você vem do trabalho front-end; BeautifulSoup tem uma API mais pythonica. Ambos analisam o mesmo HTML, e ambos precisam de uma camada de fetch separada para páginas renderizadas com JavaScript.

Q: O pyquery lida com páginas renderizadas por JavaScript?
Não por conta própria — ele nunca executa scripts. Se o conteúdo carrega após o HTML inicial, um fetch simples entrega ao pyquery um documento vazio. Busque a página através da API Scrapeless com js_render primeiro, depois selecione do HTML renderizado, assim como este guia faz.

P: É legal fazer scraping com pyquery?

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 pelo 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