Casos de Uso

Agregação de notícias e mídia com tratamento CAPTCHA

Os meios de comunicação e plataformas de mídia usam CAPTCHAs para proteger o conteúdo da agregação automatizada. Monitores de mídia, empresas de relações públicas e organizações de pesquisa precisam coletar artigos de forma programática. CaptchaAI lida com desafios CAPTCHA em fontes de notícias.


CAPTCHAs em plataformas de notícias

Tipo de fonte CAPTCHA Gatilho Conteúdo
Principais meios de comunicação Cloudflare Turnstile Detecção de bots Artigos, manchetes
Serviços de notícias (AP, Reuters) reCAPTCHA v2 Acesso em massa Notícias de última hora
Publicações com acesso pago reCAPTCHA v3 Tentativas de acesso Artigos premium
Sites de notícias locais reCAPTCHA v2 Limitação de taxa Notícias regionais
Agregadores de notícias Cloudflare Turnstile em staging Detecção de raspagem Feeds agregados
Sites de comunicados de imprensa Imagem CAPTCHA Baixar páginas Conteúdo de relações públicas

Agregador de notícias

import requests
import time
import re
from bs4 import BeautifulSoup
from datetime import datetime
import json

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY, "method": method,
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]
    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Timeout")


class NewsAggregator:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def collect_headlines(self, source_url, section=None):
        """Collect headlines from a news source."""
        url = f"{source_url}/{section}" if section else source_url
        resp = self.session.get(url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, url)

        soup = BeautifulSoup(resp.text, "html.parser")
        articles = []

        for item in soup.select("article, .story, .headline-item, h2 a, h3 a"):
            link = item if item.name == "a" else item.select_one("a")
            if link:
                articles.append({
                    "title": link.get_text(strip=True),
                    "url": self._abs_url(source_url, link.get("href", "")),
                    "source": source_url,
                    "collected_at": datetime.now().isoformat(),
                })

        return articles

    def get_article(self, article_url):
        """Fetch full article content."""
        resp = self.session.get(article_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, article_url)

        soup = BeautifulSoup(resp.text, "html.parser")

        # Remove unwanted elements
        for tag in soup.select("script, style, nav, footer, .ad, .sidebar"):
            tag.decompose()

        content_el = soup.select_one(
            "article, .article-body, .story-body, .entry-content"
        )

        return {
            "url": article_url,
            "title": self._text(soup, "h1, .article-title"),
            "author": self._text(soup, ".author, .byline, [rel='author']"),
            "date": self._text(soup, "time, .publish-date, .article-date"),
            "content": content_el.get_text(separator="\n", strip=True) if content_el else "",
            "word_count": len(content_el.get_text().split()) if content_el else 0,
        }

    def aggregate_sources(self, sources, max_articles_per=20):
        """Aggregate headlines across multiple sources."""
        all_articles = []

        for source in sources:
            try:
                articles = self.collect_headlines(source["url"], source.get("section"))
                all_articles.extend(articles[:max_articles_per])
                print(f"{source['name']}: {len(articles)} headlines")
            except Exception as e:
                print(f"{source['name']}: Error - {e}")
            time.sleep(3)

        return all_articles

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'cf-turnstile',
        ])

    def _solve_and_retry(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)
        sitekey = match.group(1)
        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        token = solve_captcha("userrecaptcha", sitekey, url)
        return self.session.post(url, data={"g-recaptcha-response": token})

    def _text(self, soup, selector):
        el = soup.select_one(selector)
        return el.get_text(strip=True) if el else ""

    def _abs_url(self, base, href):
        if href.startswith("http"):
            return href
        return base.rstrip("/") + "/" + href.lstrip("/")


# Usage
aggregator = NewsAggregator(
    proxy="http://user:pass@residential.proxy.com:5000"
)

sources = [
    {"name": "Tech News A", "url": "https://technews-a.example.com", "section": "latest"},
    {"name": "Business B", "url": "https://business-b.example.com", "section": "tech"},
    {"name": "Industry C", "url": "https://industry-c.example.com"},
]

headlines = aggregator.aggregate_sources(sources)
print(f"Total: {len(headlines)} headlines collected")

Monitoramento de notícias baseado em palavras-chave

class NewsMonitor:
    def __init__(self, keywords, sources, proxy=None):
        self.keywords = [kw.lower() for kw in keywords]
        self.aggregator = NewsAggregator(proxy=proxy)
        self.sources = sources
        self.seen_urls = set()

    def scan(self):
        """Scan for articles matching keywords."""
        headlines = self.aggregator.aggregate_sources(self.sources)
        matches = []

        for article in headlines:
            if article["url"] in self.seen_urls:
                continue

            title_lower = article["title"].lower()
            matched_kws = [kw for kw in self.keywords if kw in title_lower]

            if matched_kws:
                article["matched_keywords"] = matched_kws
                matches.append(article)
                self.seen_urls.add(article["url"])

        return matches

    def continuous_monitor(self, interval_min=30):
        """Run continuous monitoring with alerts."""
        while True:
            matches = self.scan()
            if matches:
                print(f"\n=== {len(matches)} new matches found ===")
                for m in matches:
                    print(f"  [{', '.join(m['matched_keywords'])}] {m['title']}")
                    print(f"    {m['url']}")
            else:
                print(f"No new matches at {datetime.now().strftime('%H:%M')}")

            time.sleep(interval_min * 60)


# Monitor for specific topics
monitor = NewsMonitor(
    keywords=["captcha", "bot detection", "coleta autorizada", "automation"],
    sources=sources,
    proxy="http://user:pass@residential.proxy.com:5000",
)
matches = monitor.scan()

Plano de orçamento de domínio

  • Atribua a cada fonte um orçamento de rastreamento com base nas necessidades de atualização, na pressão do CAPTCHA e no valor do fluxo de conteúdo.
  • Diminua a velocidade dos domínios confidenciais primeiro, em vez de aplicar o mesmo padrão de novas tentativas em todos os editores.
  • Rastreie soluções, erros e cadência de busca por domínio para que o orçamento possa ser ajustado com sinais reais.

Solução de problemas

Problema Causa Correção
Cloudflare bloqueando todas as solicitações Detecção agressiva de bots Use egress de rede autorizado + UA realista
Paywall em vez de artigo Conteúdo por trás da assinatura Detectar acesso pago, pular ou manipular
CAPTCHA em todas as páginas IP sinalizado Gire o proxy, adicione atrasos de mais de 5 segundos
Conteúdo do artigo vazio Conteúdo renderizado em JS Use Selenium/Puppeteer para sites de SPA
Artigos duplicados A mesma história de várias fontes Desduplicar por similaridade de título

Perguntas frequentes

A recolha de manchetes e metadados para investigação ou monitorização é uma prática comum. Reproduzir artigos completos protegidos por direitos autorais sem permissão não é. Use trechos e crie um link para a fonte original.

Como lidar com conteúdo com acesso pago?

Detecte o acesso pago (procure classes CSS do acesso pago ou comprimento de conteúdo limitado) e sinalize-o. Acesse apenas o conteúdo que você está autorizado a visualizar.

Qual tipo de proxy funciona mais adequado para sites de notícias?

Os proxies residenciais rotativos funcionam mais adequado. Os principais meios de comunicação usam o Cloudflare, que bloqueia agressivamente IPs de datacenters.


Guias Relacionados

  • Proxies residenciais rotativos
  • Pesquisa de mídia social

Os comentários estão desativados para este artigo.