新闻网站是互联网上最受保护的资源之一。Cloudflare、速率限制、IP封锁——这一切使得新闻爬虫成为一个严峻的技术挑战。在本指南中,我们将讨论如何正确设置代理,以稳定地从新闻门户收集数据,选择适合不同任务的代理类型,以及如何绕过现代保护系统。
为什么新闻网站会封锁爬虫
新闻门户对自动数据收集特别敏感,原因有几个。首先,内容是他们的主要资产,通过广告和订阅进行货币化。大规模爬虫使竞争对手能够复制材料,降低独特访问量。其次,来自机器人的高负载增加了服务器和CDN的开销。
现代新闻网站使用多层保护:
- Cloudflare及类似服务——检查JavaScript、TLS浏览器指纹、行为模式
- 速率限制——限制来自单个IP的请求数量(通常为每分钟10-50个请求)
- 基于User-Agent的封锁——封锁标准库的请求头(Python-requests、curl)
- CAPTCHA——在可疑活动时显示
- 地理封锁——某些新闻门户仅在特定国家可用
新闻网站检测爬虫的典型特征:相同的IP连续发出大量请求、缺少JavaScript、HTTP请求头的非标准顺序、请求速度过快(人无法在一秒内打开10个页面)、缺少cookies和referrer。
重要:爬取新闻网站处于灰色地带。始终检查目标资源的robots.txt和服务条款。对于商业用途,建议使用官方API或签订合作协议。
选择什么类型的代理进行新闻爬虫
选择代理类型取决于任务的规模、预算和目标网站的保护级别。我们将讨论三种主要选项及其在新闻爬虫中的适用性。
| 代理类型 | 速度 | 成本 | 何时使用 |
|---|---|---|---|
| 数据中心代理 | 高(50-100毫秒) | 低 | 没有Cloudflare的网站、大量数据、测试 |
| 住宅代理 | 中等(200-500毫秒) | 高 | 有Cloudflare的网站、严格保护、地理定位 |
| 移动代理 | 中等(300-600毫秒) | 非常高 | 最大保护、新闻网站的移动版本 |
数据中心代理用于新闻爬虫
适合用于没有严格保护的新闻网站:地方出版物、博客、小型信息门户。优点:速度快(在爬取数百个来源时很重要)、成本低(可以租用50-100个IP的池)、连接稳定。
缺点:容易通过ASN(数据中心归属)被检测到,常常已经被大型网站列入黑名单,在70%的情况下无法通过Cloudflare挑战。使用数据中心代理进行大规模爬取RSS源、sitemap.xml、API端点或收集元数据(标题、发布时间)而不加载完整内容。
住宅代理——黄金标准
住宅代理是由互联网服务提供商提供的真实家庭用户的IP地址。对于新闻网站来说,它们看起来就像普通访客,这在处理受保护资源时至关重要。
住宅代理是必需的情况:爬取大型新闻门户(CNN、BBC、路透社、RBK、商业日报)、Cloudflare或类似保护的网站、从特定国家收集数据(地理定位)、需要长时间会话的授权。住宅代理通过Cloudflare的JavaScript检查,具有良好的IP声誉,支持粘性会话(IP在10-30分钟内固定)。
实用建议:使用按时间轮换的住宅代理(粘性会话),而不是按请求轮换。例如,一个IP工作10分钟,收集20-30篇文章,然后更换。这看起来比每个请求更换IP更自然。
移动代理用于特殊情况
移动代理使用移动运营商的IP(MTS、Beeline、Tele2)。它们具有最高的信任度,因为数百万人使用移动互联网阅读新闻。将它们应用于爬取新闻网站的移动版本(通常具有简化的保护)、具有极严格保护的网站、Google News的AMP页面。
移动代理的特点:IP经常自动更换(移动运营商使用CGNAT),一个IP可能同时被数百个用户使用,这使得封锁毫无意义。缺点是价格高,因此仅在最受保护的目标上有选择地使用。
绕过Cloudflare和其他反爬虫系统
Cloudflare是新闻网站爬虫的主要敌人。大约40%的大型新闻门户使用Cloudflare来防止机器人。标准库(requests、urllib)无法通过检查,因为Cloudflare分析TLS指纹、JavaScript执行、HTTP请求头的顺序和行为模式。
绕过Cloudflare的策略
1. 无头浏览器(Selenium、Playwright、Puppeteer)
模拟真实浏览器执行JavaScript。Cloudflare看到正确的Chrome/Firefox TLS指纹并允许请求。缺点:速度慢(每页2-5秒),需要大量资源(RAM、CPU)。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 设置Selenium的代理
chrome_options = Options()
chrome_options.add_argument('--proxy-server=http://username:password@proxy.example.com:8080')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
driver = webdriver.Chrome(options=chrome_options)
driver.get('https://news-site.com/article')
# 等待JavaScript加载
driver.implicitly_wait(10)
html = driver.page_source
driver.quit()
2. 带有TLS指纹的库(curl_cffi、tls-client)
模拟真实浏览器的TLS指纹而不启动无头浏览器。速度比Selenium快10-20倍,但不执行JavaScript。适用于基本Cloudflare检查的网站(没有JS挑战)。
from curl_cffi import requests
proxies = {
'http': 'http://username:password@proxy.example.com:8080',
'https': 'http://username:password@proxy.example.com:8080'
}
response = requests.get(
'https://news-site.com/article',
proxies=proxies,
impersonate='chrome110' # 模拟Chrome 110的TLS指纹
)
print(response.text)
3. Cloudflare绕过服务(scraperapi、scrapingbee)
付费API,自动绕过Cloudflare。您发送URL,它们返回准备好的HTML。优点:无需了解技术细节,自动代理轮换,处理CAPTCHA。缺点:在大规模时成本高(每月从$50起,100K请求)。
正确的HTTP请求头
即使使用代理,发送正确的请求头也很重要,否则网站会通过非标准的User-Agent或缺少Accept-Language来识别机器人。
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Cache-Control': 'max-age=0'
}
定期更新User-Agent——使用最新版本的浏览器。可以在whoer.net或browserleaks.com上检查您的指纹。
设置IP轮换和请求管理
正确的代理轮换是稳定爬虫而不被封锁的关键。新闻网站监控来自单个IP的请求频率,超过限制会导致临时或永久封禁。
代理轮换类型
按请求轮换——每个请求使用新的IP。适合快速爬取大量不同网站,最小化因请求频率导致的封禁风险。缺点:不适合有会话(cookies、授权)的网站,对某些保护可能看起来可疑。
按时间轮换(粘性会话)——一个IP在固定时间内使用(5-30分钟),然后更换。适合爬取一个新闻门户的多个页面,保持cookies和会话,看起来像真实用户的行为。推荐用于大多数新闻爬虫任务。
按地理位置轮换——从不同国家/城市更换IP。用于爬取地理相关内容(地方新闻)、绕过地理封锁。
请求的最佳频率
即使使用代理,也不能请求得太频繁。不同类型网站的安全间隔:
- 大型新闻门户(RBK、商业日报、Vedomosti)——每个IP请求间隔2-5秒
- 中型网站——每个IP请求间隔1-3秒
- 小型博客和地方出版物——每个IP请求间隔0.5-1秒
添加随机延迟(randomization),使请求模式看起来自然:
import time
import random
def fetch_article(url, proxies):
response = requests.get(url, proxies=proxies, headers=headers)
# 随机延迟2到5秒
delay = random.uniform(2, 5)
time.sleep(delay)
return response.text
代理池的轮换示例
如果您有代理列表,可以手动实现简单的轮换:
import itertools
import requests
# 代理池
proxy_list = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080',
]
# 创建无限迭代器
proxy_pool = itertools.cycle(proxy_list)
def get_next_proxy():
proxy = next(proxy_pool)
return {'http': proxy, 'https': proxy}
# 使用
urls = ['https://news1.com/article', 'https://news2.com/article']
for url in urls:
proxies = get_next_proxy()
response = requests.get(url, proxies=proxies, headers=headers)
print(f'通过 {proxies["http"]} 获取 {url}')
代码示例:Python + Scrapy + 代理
Scrapy是一个专业的爬虫框架,开箱即用支持代理、中间件、轮换和错误处理。我们将讨论一个完整的新闻网站爬虫示例,带有代理轮换。
安装依赖
pip install scrapy scrapy-rotating-proxies
使用代理的Scrapy设置(settings.py)
# settings.py
# 启用代理轮换中间件
ROTATING_PROXY_LIST = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080',
]
DOWNLOADER_MIDDLEWARES = {
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
}
# 绕过封锁的设置
CONCURRENT_REQUESTS = 8 # 最多8个并发请求
DOWNLOAD_DELAY = 2 # 请求之间延迟2秒
RANDOMIZE_DOWNLOAD_DELAY = True # 随机延迟
# User-Agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
# 错误重试
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]
新闻爬虫的Spider
# news_spider.py
import scrapy
from datetime import datetime
class NewsSpider(scrapy.Spider):
name = 'news_parser'
# 要爬取的新闻网站列表
start_urls = [
'https://example-news.com/latest',
]
def parse(self, response):
# 解析主页上的文章列表
articles = response.css('article.news-item')
for article in articles:
article_url = article.css('a.title::attr(href)').get()
if article_url:
# 跳转到文章页面
yield response.follow(article_url, callback=self.parse_article)
def parse_article(self, response):
# 提取文章数据
yield {
'url': response.url,
'title': response.css('h1.article-title::text').get(),
'date': response.css('time.published::attr(datetime)').get(),
'author': response.css('span.author::text').get(),
'text': ' '.join(response.css('div.article-body p::text').getall()),
'tags': response.css('a.tag::text').getall(),
'scraped_at': datetime.now().isoformat(),
}
运行爬虫
# 保存为JSON
scrapy crawl news_parser -o news_data.json
# 保存为CSV
scrapy crawl news_parser -o news_data.csv
使用requests + BeautifulSoup的简单爬虫
如果不需要复杂的逻辑,可以使用requests + BeautifulSoup的组合:
import requests
from bs4 import BeautifulSoup
import time
import random
# 设置代理
proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
def parse_news_article(url):
try:
response = requests.get(url, proxies=proxies, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
# 提取数据(选择器取决于网站)
title = soup.find('h1', class_='article-title').text.strip()
date = soup.find('time', class_='published')['datetime']
text = ' '.join([p.text for p in soup.find_all('p', class_='article-text')])
return {
'url': url,
'title': title,
'date': date,
'text': text
}
except Exception as e:
print(f'解析 {url} 时出错:{e}')
return None
# 爬取文章列表
urls = [
'https://news-site.com/article-1',
'https://news-site.com/article-2',
]
for url in urls:
article_data = parse_news_article(url)
if article_data:
print(article_data)
# 请求之间的延迟
time.sleep(random.uniform(2, 4))
新闻爬虫中的常见错误
即使代理设置正确,爬虫也常因技术错误而被封锁。我们将讨论最常见的问题及其解决方案。
错误1:请求频率过高
症状:HTTP 429(请求过多)、IP临时封禁、CAPTCHA。原因:爬虫每秒发出10-50个请求。解决方案:添加延迟(time.sleep()),在Scrapy中使用DOWNLOAD_DELAY,限制CONCURRENT_REQUESTS。
错误2:对所有请求使用一个代理
症状:代理快速被封,即使有延迟。原因:一个IP对一个网站发出数百个请求。解决方案:使用代理池进行轮换,对于大型网站——至少使用10-20个代理,对于粘性会话每10-15分钟更换IP。
错误3:忽视cookies
许多新闻网站在首次访问时设置cookies,并检查后续请求中是否存在。缺少cookies是机器人的标志。解决方案:使用requests.Session()自动保存cookies,在Scrapy中启用COOKIES_ENABLED = True。
import requests
session = requests.Session()
session.proxies = {'http': 'http://proxy.com:8080', 'https': 'http://proxy.com:8080'}
# 第一个请求——获取cookies
response1 = session.get('https://news-site.com')
# 后续请求自动发送cookies
response2 = session.get('https://news-site.com/article')
错误4:错误处理重定向
新闻网站经常使用重定向(301、302)用于移动版本、地区子域、AMP页面。如果爬虫不跟随重定向,它将获得空页面。解决方案:在requests中默认启用(allow_redirects=True),通过response.url检查最终URL。
错误5:在没有JavaScript的情况下爬取动态内容
许多现代新闻网站通过JavaScript(React、Vue)加载内容。requests库获取的是空的HTML骨架,没有文章。解决方案:使用Selenium/Playwright执行JavaScript,检查DevTools中的网络——可能数据通过API加载(可以直接解析JSON)。
扩展:爬取数百个来源
当需要同时爬取不止一个新闻网站,而是数百个来源时(新闻聚合器、媒体监控),需要可扩展的架构。
使用Scrapy Cloud进行分布式爬虫
Scrapy Cloud(由Scrapy的创建者提供)允许在云中运行爬虫,并自动扩展。优点:无需自己的服务器,自动代理轮换,监控和日志。费用:基础计划每月从$9起。
任务队列(Celery + Redis)
对于自我部署,使用Celery——一个分布式任务系统。架构:Redis存储待爬取的URL队列,多个工作者(服务器)从队列中获取任务并并行爬取,每个工作者使用自己的代理池。
# tasks.py
from celery import Celery
import requests
app = Celery('news_parser', broker='redis://localhost:6379/0')
@app.task
def parse_article(url, proxy):
proxies = {'http': proxy, 'https': proxy}
response = requests.get(url, proxies=proxies, timeout=10)
# 解析和保存数据
return response.text
# 将任务添加到队列
urls = ['https://news1.com/article', 'https://news2.com/article']
proxies = ['http://proxy1.com:8080', 'http://proxy2.com:8080']
for url in urls:
proxy = random.choice(proxies)
parse_article.delay(url, proxy) # 异步执行
监控和错误处理
在大规模爬虫中,监控至关重要:处理了多少URL,多少错误,哪些代理被封。使用Sentry跟踪Python错误,使用Grafana + Prometheus监控指标(每秒请求数、响应时间),在ELK Stack(Elasticsearch、Logstash、Kibana)中记录日志。
建议:创建自动检查代理的系统。每5-10分钟通过每个代理发送测试请求到whoer.net或httpbin.org。如果代理无响应或被封——将其从池中排除并添加新的。
优化代理成本
在爬取数百个来源时,代理成本可能达到每月数千美元。优化策略:对简单网站(RSS、API)使用数据中心代理,仅对受保护的网站使用住宅代理,缓存数据——不要重复爬取同一篇文章,在非高峰时段爬取(夜间网站负载较低,封禁风险较小)。
示例:爬取500个新闻网站可以使用80%的数据中心代理(用于RSS和简单网站)和20%的住宅代理(用于前100个受保护的门户)。这将降低成本3-5倍。
结论
爬取新闻网站是一项技术复杂的任务,需要正确选择代理、设置轮换和绕过反爬虫系统。文章的关键结论:对于受保护的新闻门户(Cloudflare、严格的速率限制),使用带有粘性会话的住宅代理;对于大规模爬取数百个来源,适合快速轮换的数据中心代理;务必在请求之间添加延迟(2-5秒)和正确的HTTP请求头;绕过Cloudflare时使用无头浏览器(Selenium、Playwright)或带有TLS指纹的库。
在扩展时使用分布式系统(Celery、Scrapy Cloud)和错误监控。记住,爬取应是道德的——遵守robots.txt,不对服务器造成过大负担,并尊重内容的版权。
如果您计划爬取具有Cloudflare保护的大型新闻门户,建议使用 住宅代理——它们提供高信任度和最低的封锁风险。对于速度和数据量重要的任务(爬取RSS、API端点),适合使用 数据中心代理。