Web Scraping com pyquery: Análise de HTML Estilo jQuery em Python
Senior Web Scraping Engineer
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
lxmlpara 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_renderretorna 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
pip install "pyquery==2.0.1" requests
Mantenha sua chave no ambiente, nunca no código fonte:
bash
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
# 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
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
# 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
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
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çãoPyQuerygera elementos lxml brutos, não objetosPyQuery, 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; usequote.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()geraAttributeError. Você iterou a seleção diretamente em vez de usar.items(), então você obteve um elemento lxml puro. Altere o loop parafor 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.



