新闻媒体和媒体平台使用验证码来保护内容免遭自动聚合。媒体监察员、公关公司和研究组织需要以编程方式收集文章。 CaptchaAI 处理跨新闻来源的验证码挑战。
新闻平台验证码
| 来源类型 | 验证码 | 扳机 | 内容 |
|---|---|---|---|
| 主要新闻媒体 | Cloudflare Turnstile | 机器人检测 | 文章、标题 |
| 有线服务(美联社、路透社) | reCAPTCHA v2 | 批量访问 | 突发新闻 |
| 付费出版物 | reCAPTCHA v3 | 访问尝试 | 优质文章 |
| 当地新闻网站 | reCAPTCHA v2 | 速率限制 | 地区新闻 |
| 新闻聚合器 | Cloudflare 验证流程 | 刮擦检测 | 聚合提要 |
| 新闻稿网站 | 图片验证码 | 下载页面 | 公关内容 |
新闻聚合器
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")
基于关键词的新闻监控
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", "web scraping", "automation"],
sources=sources,
proxy="http://user:pass@residential.proxy.com:5000",
)
matches = monitor.scan()
域预算计划
- 根据新鲜度需求、CAPTCHA 压力和内容流的价值为每个源分配爬网预算。
- 首先减慢敏感域的速度,而不是在所有发布商中应用相同的重试模式。
- 跟踪每个域的解决、错过和获取节奏,以便可以根据实际信号调整预算。
故障排除
| 问题 | 原因 | 处理方式 |
|---|---|---|
| Cloudflare 阻止所有请求 | 攻击性机器人检测 | 使用自有服务器基础设施+真实UA |
| 付费墙而不是文章 | 订阅背后的内容 | 检测付费墙、跳过或处理 |
| 每个页面上都有验证码 | IP 标记 | 旋转代理,添加 5 秒以上的延迟 |
| 文章内容为空 | JS 渲染的内容 | 将 Selenium/Puppeteer 用于 SPA 网站 |
| 重复的文章 | 来自多个来源的同一个故事 | 按标题相似度去重 |
常问问题
新闻聚合合法吗?
收集头条新闻和元数据用于研究或监测是常见的做法。未经许可复制完整受版权保护的文章是不可以的。使用片段并链接回原始来源。
如何处理付费内容?
检测付费墙(查找付费墙 CSS 类或有限的内容长度)并对其进行标记。仅访问您有权查看的内容。
哪种代理类型最适合新闻网站?
轮换自有服务器基础设施效果最好。主要新闻媒体使用 Cloudflare,它会积极阻止数据中心 IP。
相关指南
- 轮换自有服务器基础设施
- 社交媒体研究
来自任何来源的汇总新闻 -获取您的 CaptchaAI 密钥并自动化内容收集。