Raspagem da Web com Polars: Da Página ao Vivo para um DataFrame Rápido
Scraping and Proxy Management Expert
TL;DR:
- Polars é uma biblioteca de DataFrame rápida e suportada por Arrow, e combina com o Scrapeless para transformar uma página ao vivo em um quadro tipado e consultável.
- Polars não possui cliente HTTP nem parser HTML, portanto, as etapas de busca e extração vêm do Scrapeless e um parser; Polars assume o controle assim que os registros existem.
- Construa um quadro a partir de uma lista de dicionários com
pl.DataFrame(records), e o esquema é tipado a partir da primeira linha quando você coercivamente define os valores durante a extração. - A API de expressões substitui a indexação encadeada estilo pandas:
pl.col("price_gbp").mean()dentro degroup_by(...).agg(...)é lido e executado de forma limpa. - O caminho preguiçoso,
df.lazy()...collect(), permite que Polars planeje e otimize toda a consulta antes de acessar os dados, que é onde ele se destaca em conjuntos maiores. - Comece no plano gratuito do Scrapeless e aponte a etapa de busca para o seu próprio catálogo.
Polars continua sendo usado em trabalhos de dados porque é rápido e sua API é agradável, mas quase todos os tutoriais lhe entregam um arquivo Parquet que já existe. A raspagem é a situação oposta. Os dados são ao vivo, são HTML, e não têm tipo até que você os torne tipados. Este guia leva um catálogo público de livros de uma URL a um DataFrame Polars que você pode agrupar e agregar, e é honesto sobre a única coisa que o Polars não fará por você: obter a página.
A busca vem da API de Raspagem Universal Scrapeless, que retorna HTML renderizado para uma URL pública. Um parser transforma esse HTML em registros. Polars faz o resto, e faz rápido.
O que o Polars Adiciona a um Fluxo de Trabalho de Raspagem
Polars é uma biblioteca de DataFrame construída no formato de memória columnar Apache Arrow, que é o que torna suas colunas tipadas e agregações agrupadas rápidas. Para dados raspados, o retorno é concreto: uma vez que seus registros estão em um quadro, limpá-los e resumí-los é feito com algumas expressões em vez de um loop escrito à mão, e as colunas mantêm seus tipos. Polars baseia-se no formato columnar Apache Arrow para seu layout em memória, então uma coluna de preços é uma coluna numérica real, não uma lista de strings que você reanalisou em cada operação.
Instalação
Polars lida com o quadro; um parser lida com o HTML. Instale ambos.
bash
pip install polars lxml
Defina sua chave de API Scrapeless uma vez no shell. Use a chave real em tempo de execução e mantenha o espaço reservado fora do seu código-fonte.
bash
export SCRAPELESS_API_KEY="sk_your_key_here"
Buscar, Extrair e Construir um Quadro
A primeira etapa busca a página, captura um registro por produto e constrói o DataFrame. Como Polars não pode buscar ou analisar HTML, esta etapa é onde o Scrapeless e o parser fazem seu trabalho; Polars assume os registros finalizados.
python
import json
import os
import urllib.request
import polars as pl
from lxml import html as lxhtml
API_URL = "https://api.scrapeless.com/api/v2/unlocker/request"
RATINGS = {"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5}
def fetch_html(url: str) -> str:
payload = json.dumps(
{"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": False, "headless": False}}
).encode()
request = urllib.request.Request(
API_URL,
data=payload,
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request, timeout=90) as response:
return json.loads(response.read())["data"]
dom = lxhtml.fromstring(fetch_html("https://books.toscrape.com/"))
records = []
for card in dom.cssselect("article.product_pod"):
records.append(
{
"title": card.cssselect("h3 a")[0].get("title"),
"price_gbp": float(card.cssselect("p.price_color")[0].text_content().strip().lstrip("£")),
"rating": RATINGS[card.cssselect("p.star-rating")[0].get("class").split()[-1]],
"in_stock": "In stock" in card.cssselect("p.instock.availability")[0].text_content(),
}
)
df = pl.DataFrame(records)
print(f"registros extraídos: {df.height}")
print("esquema:", dict(df.schema))
A extração usa o padrão CSS Selectors através do cssselect do lxml, e coage cada valor enquanto o lê: o preço se torna um float, a palavra da avaliação se torna um inteiro, e a disponibilidade se torna um booleano. Essa coação é o hábito importante. Construa os registros com tipos reais do Python e Polars infere um esquema real a partir deles.
text
registros extraídos: 20
esquema: {'title': String, 'price_gbp': Float64, 'rating': Int64, 'in_stock': Boolean}
Vinte cartões de produto na primeira página se tornam um quadro com um esquema digitado: String, Float64, Int64 e Boolean. Nenhuma coluna é deixada como texto que depois precisa ser reanalisada.
Transformar Com Expressões
Polars substitui o indexamento encadeado por uma API de expressões, e essa é a parte que vale a pena aprender corretamente. Uma expressão como pl.col("price_gbp").mean() descreve um cálculo que o Polars executa de forma eficiente, e as expressões se compõem dentro de filter, group_by e agg.
python
resumo = (
df.lazy()
.filter(pl.col("in_stock"))
.group_by("rating")
.agg(pl.len().alias("books"), pl.col("price_gbp").mean().round(2).alias("avg_price"))
.sort("rating")
.collect()
)
print(resumo)
df.lazy() transforma o quadro ansioso em um plano de consulta, a cadeia descreve o trabalho, e .collect() o executa. Neste pequeno conjunto, a diferença é invisível, mas o caminho preguiçoso permite que o Polars analise toda a consulta e pule o trabalho antes de ler um único valor, razão pela qual ele consegue escalar. Os grupos agregados de livros em estoque são classificados pela classificação de estrelas e relatam a contagem e o preço médio por grupo.
text
shape: (5, 3)
┌────────┬───────┬───────────┐
│ rating ┆ books ┆ avg_price │
│ --- ┆ --- ┆ --- │
│ i64 ┆ u32 ┆ f64 │
╞════════╪═══════╪═══════════╡
│ 1 ┆ 6 ┆ 40.02 │
│ 2 ┆ 3 ┆ 36.83 │
│ 3 ┆ 3 ┆ 42.32 │
│ 4 ┆ 4 ┆ 31.1 │
│ 5 ┆ 4 ┆ 39.75 │
└────────┴───────┴───────────┘
O Script Completo
Junte os estágios, adicione mais uma expressão para encontrar o livro mais barato com cinco estrelas e armazene o quadro como Parquet.
python
import json
import os
import urllib.request
import polars as pl
from lxml import html as lxhtml
API_URL = "https://api.scrapeless.com/api/v2/unlocker/request"
RATINGS = {"Um": 1, "Dois": 2, "Três": 3, "Quatro": 4, "Cinco": 5}
def fetch_html(url: str) -> str:
payload = json.dumps(
{"actor": "unlocker.webunlocker", "input": {"url": url, "js_render": False, "headless": False}}
).encode()
request = urllib.request.Request(
API_URL,
data=payload,
headers={"x-api-token": os.environ["SCRAPELESS_API_KEY"], "Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request, timeout=90) as response:
return json.loads(response.read())["data"]
dom = lxhtml.fromstring(fetch_html("https://books.toscrape.com/"))
records = []
for card in dom.cssselect("article.product_pod"):
records.append(
{
"title": card.cssselect("h3 a")[0].get("title"),
"price_gbp": float(card.cssselect("p.price_color")[0].text_content().strip().lstrip("£")),
"rating": RATINGS[card.cssselect("p.star-rating")[0].get("class").split()[-1]],
"in_stock": "Em estoque" in card.cssselect("p.instock.availability")[0].text_content(),
}
)
df = pl.DataFrame(records)
print(f"registros extraídos: {df.height} | colunas: {df.columns}")
resumo = (
df.lazy()
.filter(pl.col("in_stock"))
.group_by("rating")
.agg(pl.len().alias("books"), pl.col("price_gbp").mean().round(2).alias("avg_price"))
.sort("rating")
.collect()
)
print(resumo)
cheapest = df.filter(pl.col("rating") == 5).sort("price_gbp").select("title", "price_gbp").head(1)
print("livro 5 estrelas mais barato:", cheapest.to_dicts())
df.write_parquet("books.parquet")
print(f"tamanho em bytes do parquet: {os.path.getsize('books.parquet')}")
A execução relata cada etapa, terminando com um arquivo Parquet que mantém o esquema para o próximo leitor.
text
registros extraídos: 20 | colunas: ['title', 'price_gbp', 'rating', 'in_stock']
shape: (5, 3)
┌────────┬───────┬───────────┐
│ rating ┆ books ┆ avg_price │
│ --- ┆ --- ┆ --- │
│ i64 ┆ u32 ┆ f64 │
╞════════╪═══════╪═══════════╡
│ 1 ┆ 6 ┆ 40.02 │
│ 2 ┆ 3 ┆ 36.83 │
│ 3 ┆ 3 ┆ 42.32 │
│ 4 ┆ 4 ┆ 31.1 │
│ 5 ┆ 4 ┆ 39.75 │
└────────┴───────┴───────────┘
livro 5 estrelas mais barato: [{'title': 'Set Me Free', 'price_gbp': 17.46}]
tamanho em bytes do parquet: 2233
write_parquet armazena as colunas tipadas no formato de arquivo Apache Parquet, para que o próximo processo leia price_gbp de volta como um float sem re-coerção. O guia do usuário do Polars documenta toda a expressão e a API preguiçosa quando você ultrapassa este exemplo.
O Que o Polars Não Fará
Polars é um motor de dataframe, e esse é o todo limite: ele não tem cliente HTTP e nem analisador HTML. Não fará uma solicitação de URL, não renderizará JavaScript e não transformará tags <article> em linhas. Isso é intencional, e é por isso que este fluxo de trabalho usa duas ferramentas na frente. Scrapeless recupera a página, e um parser lê a marcação em registros. Se você precisa de um tratamento mais profundo do passo de parsing, o guia sobre usando BeautifulSoup para web scraping aborda a extração de HTML em Python, e os mesmos registros alimentam diretamente o pl.DataFrame.
Quando um alvo renderiza seu conteúdo com JavaScript, a primeira busca retorna um shell vazio e o loop de extração não encontra cartões. Defina js_render como True na requisição para que o Scrapeless retorne a página após a execução de seus scripts, e deixe como False quando a marcação já for renderizada pelo servidor, como este catálogo.
Antes de ampliar o rastreamento além da primeira página, leia o robots.txt do alvo e os termos. O Protocolo de Exclusão de Robôs afirma quais caminhos um site pede para que clientes automatizados evitem, e permanecer dentro dele mantém o fluxo de trabalho sustentável. Mantenha o volume limitado e os dados públicos, como este exemplo faz.
Pronto para tentar em seus próprios dados? Crie uma conta gratuita no Scrapeless e altere a URL e os seletores na etapa de busca.
Conclusão
Polars transforma registros extraídos em um quadro tipado e consultável com muito pouco código, desde que algo mais obtenha a página primeiro. Scrapeless busca o HTML, um parser constrói os registros com tipos reais, e Polars agrupa, agrega e armazena através de suas APIs de expressão e preguiçosa. Comece a partir do script completo, troque a URL e os seletores pelo seu próprio alvo e procure o caminho preguiçoso à medida que seus dados crescem.
Comece com o plano gratuito do Scrapeless para buscar suas próprias páginas, e confira os preços do Scrapeless quando planejar um trabalho recorrente.
Perguntas Frequentes
P: O Polars faz scraping de páginas web por conta própria?
Não. Polars é uma biblioteca de DataFrame sem cliente HTTP e sem analisador HTML, então não pode solicitar uma URL ou ler a marcação. Combine-o com uma camada de busca, como o Scrapeless, e um parser como lxml ou BeautifulSoup; o Polars assume o controle uma vez que os registros existem.
P: Por que escolher Polars em vez de pandas para dados extraídos?
Polars é construído no formato columnar Apache Arrow e oferece um motor de consulta preguiçoso, então agregações agrupadas e filtros em grandes quadros são rápidos, e a API de expressão é consistente. Um trade-off é relevante para scraping: pandas tem read_html para tabelas, enquanto o Polars espera que você construa o quadro a partir de registros, o que se adapta melhor a páginas estilo cartão do que apenas às tabelas.
P: Qual é a diferença entre ávido e preguiçoso no Polars?
Um DataFrame ávido executa cada operação imediatamente, enquanto df.lazy() constrói um plano de consulta que só é executado quando você chama .collect(). O caminho preguiçoso permite que o Polars otimize toda a consulta e pule trabalhos desnecessários, razão pela qual ele escala melhor em grandes conjuntos de dados, mesmo que o resultado seja idêntico em pequenos.
P: Por que meus números extraídos aparecem como strings no Polars?
Valores extraídos chegam como texto, e se você os passar para pl.DataFrame sem alterações, o tipo da coluna será String. Force cada valor durante a extração, como o exemplo que converte o preço para float e a palavra da classificação para um inteiro, assim o esquema é tipado a partir da primeira linha e nenhuma coluna precisa ser reanalisada depois.
P: Como construir um DataFrame Polars a partir de uma lista de registros extraídos?
Coleta cada registro como um dicionário com chaves consistentes e passe a lista para pl.DataFrame(records). O Polars infere o esquema a partir dos valores, então valores tipados nos dicionários produzem um quadro tipado, pronto para a API de expressão.
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.



