Capa

Client-side scraping no Wallabag: contornando paywall e anti-bot

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, urllib e curl produzem 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:

  1. 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.
  2. 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.
  3. 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):

  1. 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).
  2. Em Ajustes → Compartilhamento, ative a opção "Raspar no navegador" (ou "Scrape via Web Extension" na versão em inglês).
  3. No Safari, abra o artigo que o Wallabag server-side falhou em arquivar.
  4. 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.
  5. 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-extra com puppeteer-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.

Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.