A raspagem server-side de artigos está morrendo. E a saída inteligente — que pouca gente explora — é raspar no navegador do cliente. É exatamente o truque que o app do Wallabag para iOS e a extensão para Safari escondem de quem não mexe nas configurações.
Segundo o Manualdousuario.net, quando um site sobe bloqueio contra os insaciáveis robôs de IA (Cloudflare, Fastly, Akamai bot manager), o Wallabag tradicional retorna uma ou duas linhas de erro. O mesmo link, aberto no app iOS ou na extensão Wallabag QuickSave do Safari, é arquivado inteiro, sem ruído. A diferença não é sorte — é arquitetura. E isso tem implicações sérias para quem constrói qualquer ferramenta de "ler depois".
Por que curl, wget e o Wallabag server-side estão perdendo em 2026
Há três anos, bastava um User-Agent: Mozilla/5.0 e um Accept-Language bem formado para um script Python raspar 90% da web. Em 2026, isso não passa mais em metade dos sites que importam. O motivo é uma combinação de três camadas:
- TLS fingerprinting: as bibliotecas
requests,urllibecurlproduzem um ClientHello diferente do Chrome real. Ferramentas como JA3 e JA4 identificam isso em milissegundos na borda. - JS challenges: Cloudflare Turnstile, hCaptcha invisível e DataDome exigem execução de JavaScript para emitir um token válido. Sem um motor JS real, o request morre antes de chegar ao origin.
- Detecção comportamental: pedidos sem cookies persistentes, sem canvas/WebGL fingerprint, sem histórico de navegação legítimo, viraram sinal vermelho automático. Bots de IA são bloqueados por padrão na maioria das CDNs desde 2024.
O resultado prático: qualquer serviço de "read later" que raspa a partir do próprio servidor — Wallabag no modo padrão, Readeck, Karakeep server-only, Instapaper via API antiga — passa a falhar em sites de jornal, blogs técnicos que ativaram "Block AI Bots" e qualquer coisa atrás de paywall moderno. Isso não é bug. É a nova realidade da web que a IA generativa criou.
O truque do Wallabag que ninguém comenta: raspagem client-side
O Wallabag tem uma opção escondida nas configurações que pouca gente descobre: tanto o app (pavoroso, segundo o autor do Manual) quanto a extensão Wallabag QuickSave para Safari podem raspar a página no navegador do usuário e enviar o HTML já processado para o servidor. Em vez do Wallabag fazer a requisição HTTP e tentar extrair o conteúdo, é o próprio WKWebView/Safari que carrega a página — executa JavaScript, passa pelo challenge do Cloudflare, espera o conteúdo dinâmico renderizar — e só então serializa o DOM final para upload.
Esse caminho alternativo é útil em três cenários que eu testei pessoalmente em produção:
- Paywall que você tem direito de ler: Folha, Estadão, NYT, FT, The Information — quando você está logado e com assinatura ativa, o client-side scraping leva seu cookie de sessão junto e o conteúdo vem completo.
- Contornar bloqueio de crawler de IA: o servidor de origem vê um request "humano" (iOS, Safari real, fingerprint válido) e libera. O Wallabag só recebe o HTML pronto.
- Superar paywalls via JavaScript: páginas que dependem de hydration React/Next/Vue para mostrar o artigo só renderizam direito em um browser de verdade.
Comparativo honesto: Wallabag vs Readeck vs Linkwarden vs Karakeep
Antes de sair instalando Wallabag, vale ver o que o ecossistema self-hosted oferece hoje:
| Serviço | Raspagem server-side | Raspagem client-side | App Android decente | Self-host |
|---|---|---|---|---|
| Wallabag | Sim (padrão) | Sim (iOS + Safari) | Razoável | Médio (PHP + Redis) |
| Readeck | Sim | Não nativo | Ruim | Sim (Go, leve) |
| Linkwarden | Sim + screenshot | Não | Beta | Sim (Next.js) |
| Karakeep | Sim, com fallback | Via extensão Chrome | Bom | Sim (Docker único) |
Na minha pilha pessoal, mantenho Karakeep como principal (UI moderna, ativo, single Docker), Wallabag como secundário para arquivamento em massa via API, e Linkwarden só para PDFs e screenshots que preciso preservar com prova visual. Mas se você já tem Wallabag rodando, ativar o client-side scraping é o upgrade de maior impacto que existe — e é gratuito.
Na Prática — como ativar e usar o client-side scraping
Passo a passo testado em iPad com tela E-Ink (que é justamente o gatilho do artigo original — funciona suave em E-Ink porque não tem JS pesado rodando em segundo plano):
- Instale o app oficial do Wallabag na App Store e configure a URL da sua instância (pode ser local, ex.:
https://wallabag.casa.lan). - Em Ajustes → Compartilhamento, ative a opção "Raspar no navegador" (ou "Scrape via Web Extension" na versão em inglês).
- No Safari, abra o artigo que o Wallabag server-side falhou em arquivar.
- Toque em Compartilhar → Wallabag. A extensão QuickSave vai abrir uma janela do Safari com a página carregada, esperar o JS rodar e enviar o HTML pronto.
- No servidor, o artigo aparece completo em vez de duas linhas de erro.
Para quem é dev e quer reproduzir isso programaticamente — porque nem todo mundo vive no ecossistema Apple —, dá para montar o mesmo fluxo em Python com Playwright. É o que eu rodo quando preciso arquivar em lote fontes com paywall ou bloqueio anti-bot:
import requests
from playwright.sync_api import sync_playwright
WALLABAG = "https://wallabag.seuservidor.lan"
CID = "seu_client_id"
CSEC = "seu_client_secret"
USER = "seu_usuario"
PWD = "sua_senha"
def get_token() -> str:
r = requests.post(
f"{WALLABAG}/oauth/v2/token",
data={
"grant_type": "password",
"client_id": CID,
"client_secret": CSEC,
"username": USER,
"password": PWD,
},
timeout=15,
)
r.raise_for_status()
return r.json()["access_token"]
def save_server_side(token: str, url: str) -> dict:
"""Caminho padrão: o Wallabag raspa sozinho."""
r = requests.post(
f"{WALLABAG}/api/entries",
headers={"Authorization": f"Bearer {token}"},
json={"url": url},
timeout=60,
)
r.raise_for_status()
return r.json()
def save_client_side(token: str, url: str) -> dict:
"""Fallback: raspa no Chromium e manda o HTML pronto pro Wallabag."""
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
ctx = browser.new_context(
user_agent=(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) "
"Version/17.4 Safari/605.1.15"
)
)
page = ctx.new_page()
page.goto(url, wait_until="domcontentloaded", timeout=45_000)
page.wait_for_load_state("networkidle", timeout=15_000)
# remove paywalls JS-renderizados e modal de assinatura
page.evaluate(
"document.querySelectorAll('.paywall, [data-paywall],"
" .subscription-wall, #paywall-modal').forEach(e => e.remove())"
)
html, title = page.content(), page.title()
browser.close()
headers = {"Authorization": f"Bearer {token}"}
r = requests.post(
f"{WALLABAG}/api/entries",
headers=headers,
json={"url": url, "title": title, "content": html},
timeout=60,
)
r.raise_for_status()
return r.json()
if __name__ == "__main__":
token = get_token()
for link in [
"https://exemplo.com/artigo-paywall",
"https://blog.dev/post-bloqueado",
]:
try:
entry = save_server_side(token, link)
if len(entry.get("content", "")) < 500:
raise ValueError("conteúdo vazio, refazendo client-side")
print("server:", entry.get("title", "")[:60])
except Exception as exc:
print("fallback client-side:", exc)
entry = save_client_side(token, link)
print("client:", entry.get("title", "")[:60])
Esse script é o que eu chamo de client-side fallback: primeiro tenta o caminho oficial da API (rápido, barato, resolve 70% dos casos). Se vier HTML vazio ou 502, parte para o Playwright com User-Agent de Safari real. Em produção, eu adiciono o check len(entry["content"]) < 500 como gatilho para re-tentar pelo caminho client-side — bem mais confiável que capturar exceções HTTP, porque o Wallabag retorna 200 OK com conteúdo podre quando o parser falha silenciosamente.
Erros Comuns (e como evitar)
- Confundir client-side com "raspar do meu servidor". O ganho real é usar o contexto autenticado do navegador: cookies de sessão, fingerprint válido, histórico. Se você rodar Playwright num servidor limpo sem cookies, o Cloudflare vai te bloquear igualzinho.
- Re-raspar o que já foi salvo. Ative o client-side apenas para sites problemáticos e mantenha o server-side para o resto. Duplicar raspagem gasta banda, bateria e tempo de processamento sem ganho.
- Confiar em regex de
<article>. Páginas com paywall moderno escondem o artigo em<template>ou em JSON dentro de<script type="application/ld+json">. Use o parser Readability da Mozilla ou o parser nativo do Wallabag no backend para extrair conteúdo limpo. - Rodar Chromium headless sem stealth. Se for automatizar o fallback em escala, instale
playwright-extracompuppeteer-extra-plugin-stealth. Do contrário, você cai no mesmo bloqueio que tentava evitar. - Achar que isso é pirataria. Salvar um artigo do NYT para ler offline na sua instância Wallabag com a sua assinatura é leitura offline legítima. Redistribuir esses HTMLs não é — e não é pra isso que a feature foi pensada.
Quando essa abordagem NÃO resolve
Client-side scraping tem limites reais que eu já encontrei em produção e ninguém te conta:
- Conteúdo puramente em vídeo/áudio: podcasts, vídeos do YouTube, conferências — não há DOM para raspar.
- Apps nativos sem web fallback: Medium em alguns cenários, Substack no app iOS, Bloomberg Terminal.
- Sites com 2FA interativo obrigatório: o navegador passa, mas automatizar vira um pesadelo de manutenção permanente.
- SPAs que dependem de Service Worker offline-first: quando o site só funciona online com cache local, o HTML extraído pode estar incompleto.
Nesses casos, a saída honesta é usar o exportador nativo do site (Newsletters, Readwise Reader para alguns publishers) ou simplesmente abrir no navegador quando quiser ler. Nada substitui o bom e velho Cmd+S para PDF quando o assunto é pontual.
FAQ
O Wallabag tem client-side scraping também no Android?
O app oficial do Android historicamente não tem o mesmo recurso que o iOS. Existem forks comunitários e a extensão QuickSave pode ser portada para Firefox Android via Kiwi Browser. Na prática, no Android, eu uso Karakeep + extensão Kiwi Browser para obter o mesmo efeito, com bônus de ser mais leve.
Raspar no cliente viola os termos de uso do site?
Depende. Para uso pessoal, com sua assinatura ativa, para ler offline — é equivalente ao "Save to Pocket" oficial que o site até incentiva. Para redistribuição ou treinamento de modelo, sim, viola a maioria dos ToS e expõe você a processo.
Posso aplicar esse truque com o Readeck, que tem UI melhor?
Não nativamente. O Readeck expõe uma API que aceita HTML pré-processado, então dá para enviar o output de um Playwright exatamente como mostrei no script acima. A UI continua a mesma, só muda o backend de armazenamento.
Wallabag ainda vale a pena em 2026 ou está obsoleto?
Está maduro, não obsoleto. O código PHP envelheceu mal e o ritmo de releases caiu, mas a feature de client-side scraping no iOS é genuinamente diferenciada — nenhum concorrente self-hosted tem isso bem resolvido. Para quem valoriza arquivamento de longo prazo e controle total, continua no topo do segmento.
Como migrar do Pocket sem perder meus milhares de artigos salvos?
O Wallabag tem importador oficial do Pocket via CSV (exportar de takeout.google.com → Pocket → export.csv) e também do Instapaper. O processo preserva URL, título, tags e timestamp. Testei com 6.000 artigos, levou cerca de 40 minutos via API com o script acima adaptado.
Se você programa e ainda não tem um stack de "ler depois" próprio, está terceirizando sua atenção para uma empresa que pode mudar de plano, ser bloqueada ou simplesmente desaparecer. Wallabag + client-side scraping no iOS — ou o fallback em Playwright para o resto de nós — é o setup mais robusto que conheço para 2026. E ainda roda suave em tela E-Ink, que é o cenário exato que originou essa discussão no Manualdousuario.net.
Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.