网站被爬虫拖垮,往往不是因为搜索引擎的正常抓取,而是大量恶意爬虫或空User-Agent请求的疯狂访问。解决的核心手段,就是实施精准的User-Agent过滤与屏蔽策略。这不仅能直接减轻服务器负载,还能节省带宽,提升正常用户的访问体验。具体操作上,你需要通过服务器配置(如Nginx、Apache)或应用程序代码(如PHP、Python)来拦截和拒绝那些没有携带User-Agent标识、或携带了已知恶意爬虫UA的请求。

一、 为什么空UA和恶意爬虫UA危害巨大?

一个正常的浏览器或善意爬虫(如搜索引擎蜘蛛)在发起请求时,都会在HTTP头中清晰地声明自己的身份,即User-Agent字符串。例如,“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36”是常见浏览器的UA。空User-Agent请求,意味着请求头中完全缺失这一字段,这几乎可以断定是非人类流量,通常是自动化攻击脚本、内容抄袭爬虫或漏洞扫描工具的典型特征。它们以极高的并发频率请求页面,消耗服务器CPU和内存资源,却不产生任何价值。而一些恶意爬虫甚至会伪装成常见浏览器的UA,但通过其异常的访问频率和模式(如每秒请求同一接口上百次)也能被识别。放任不管,轻则网站响应变慢,重则直接导致服务器资源耗尽而宕机。

二、 在Web服务器层面进行拦截(Nginx/Apache)

这是最高效、对应用性能影响最小的方式。通过在Web服务器配置中添加规则,可以在请求到达你的应用程序之前就将其阻断。

1. Nginx 配置示例

你可以在Nginx的server或location配置块中,使用if指令结合$http_user_agent变量进行判断。以下配置将屏蔽空UA请求和几个已知的恶意爬虫UA:

server {
    listen 80;
    server_name yourdomain.com;

    # 屏蔽空User-Agent和特定恶意爬虫UA
    if ($http_user_agent ~ ^$) {
        return 403;
    }
    if ($http_user_agent ~* (Scrapy|curl|HttpClient|python-requests|Go-http-client|masscan|sqlmap)) {
        return 403;
    }

    # 你的其他location配置...
    location / {
        proxy_pass http://your_backend;
    }
}

解释:~ ^$是正则匹配空字符串;~*表示不区分大小写的匹配,后面括号内是一些常见的恶意爬虫或工具标识。匹配到则直接返回403禁止状态码。请注意,Nginx官方不鼓励过度使用if,但在UA过滤这个场景下是常见且有效的做法。更复杂的匹配可以结合map指令。

2. Apache 配置示例 (.htaccess文件)

如果你使用Apache服务器,可以在网站根目录的.htaccess文件中添加以下规则:

RewriteEngine On

# 拒绝空User-Agent的访问
RewriteCond %{HTTP_USER_AGENT} ^-?$ [OR]

# 拒绝匹配特定字符串的User-Agent(示例)
RewriteCond %{HTTP_USER_AGENT} (Scrapy|curl|HttpClient|masscan) [NC]
RewriteRule ^.*$ - [F,L]

解释:RewriteCond定义条件,^?$匹配空或仅一个“-”的UA;[OR]表示或;[NC]表示不区分大小写;[F]返回403禁止,[L]表示这是最后一条规则。

三、 在应用程序代码层面进行过滤

当无法直接修改服务器配置,或者需要更动态、更复杂的逻辑时(例如结合数据库或实时风控),可以在应用入口处编写过滤代码。

1. PHP 示例


2. Python (Django框架) 中间件示例

创建一个中间件文件,如block_middleware.py

import re

class BlockUAMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # 编译正则表达式,提升效率
        self.blocked_patterns = [
            re.compile(r'^$'),
            re.compile(r'Scrapy', re.I),
            re.compile(r'curl', re.I),
            re.compile(r'python-requests', re.I),
        ]

    def __call__(self, request):
        user_agent = request.META.get('HTTP_USER_AGENT', '')
        for pattern in self.blocked_patterns:
            if pattern.search(user_agent):
                from django.http import HttpResponseForbidden
                return HttpResponseForbidden('Access Denied.')
        return self.get_response(request)

然后在settings.pyMIDDLEWARE列表中添加这个中间件。

四、 进阶策略与注意事项

单纯的静态UA匹配只是第一道防线。高明的恶意爬虫会频繁更换UA,甚至盗用正常浏览器的UA。因此,你需要结合更多维度的防御:

1. 结合访问频率限制 (Rate Limiting)

这是最关键的一环。即使UA看起来正常,如果同一个IP在极短时间内发起大量请求,也极有可能是爬虫。在Nginx中可以使用limit_req模块,在应用中可以使用Redis记录IP访问次数。例如Nginx配置:

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;

    server {
        location / {
            limit_req zone=one burst=20 nodelay;
            # ... 其他配置
        }
    }
}

这表示每个IP每秒最多10个请求,允许突发20个。

2. 动态维护UA黑名单/白名单

将屏蔽规则存储在数据库或配置文件中,便于动态更新。可以设计一个管理后台,当发现新的恶意爬虫UA时,实时添加进去,而无需重启服务器或修改代码。

3. 识别和挑战“高级”爬虫

一些爬虫会完整模拟浏览器行为(Headless Browser)。对于它们,UA过滤可能失效。这时需要结合行为分析:检查请求是否携带必要的Cookie、是否执行了JavaScript(通过挑战性测试,如简单的JS计算)、访问路径是否具有人类浏览的随机性等。可以考虑引入专门的机器人管理解决方案,通过交互式挑战来区分。

4. 避免误伤:务必设置白名单

在实施屏蔽时,最大的风险是误伤。你必须为重要的合法爬虫设置白名单,例如各大搜索引擎的官方蜘蛛(Baiduspider, Bingbot等)。在Nginx中,可以在屏蔽规则前加入放行规则:

# 首先,放行重要的搜索引擎蜘蛛
if ($http_user_agent ~* (Baiduspider|Googlebot|Bingbot|YandexBot)) {
    set $allow_bot true;
}
# 然后,在屏蔽规则中增加条件,如果$allow_bot不为true,则执行屏蔽
if ($http_user_agent ~ ^$ ) {
    set $block 1;
}
if ($allow_bot != true) {
    # 其他屏蔽条件...
}
# 最后根据$block变量决定是否返回403
if ($block = 1) {
    return 403;
}

五、 监控与日志分析

部署过滤规则后,工作并未结束。你必须持续监控服务器访问日志(通常是access.log),分析403错误的数量和来源IP,评估过滤效果。使用工具如AWStats、GoAccess或ELK堆栈可以可视化这些数据。关注是否有大量请求因新规则被拦截,同时确保正常流量不受影响。定期审查和更新你的UA黑名单,因为恶意爬虫也在不断进化。

总而言之,对抗拖垮网站的爬虫是一场攻防战。屏蔽空UA和已知恶意UA是坚实且必要的第一步,它能过滤掉大部分低层次的垃圾流量。但要构建更稳固的防线,必须将UA过滤、速率限制、IP封禁、行为分析等多种策略分层部署,并辅以持续的监控和灵活的规则调整。这样,你才能确保服务器资源服务于真正的用户,保障网站的稳定与性能。