目录

  1. 背景介绍
  2. 实现目标
  3. Nginx 配置详解
  4. 验证服务搭建
  5. 客户端测试实现
  6. 测试结果分析

背景介绍

随着搜索引擎爬虫变得越来越智能,它们的抓取行为也越来越激进。过度频繁的爬虫访问可能会影响网站性能,甚至导致服务器负载过高。本文将详细介绍如何使用 Nginx 的配置来智能限制爬虫访问频率,同时保持对正常用户的友好访问体验。

实现目标

  1. 对主流搜索引擎爬虫(Google、Bing、Facebook、Yandex)进行访问频率限制
  2. 针对爬虫限制:5次/分钟
  3. 针对普通用户限制:100次/分钟
  4. 提供完整的测试验证方案

Nginx 配置详解

1. 基础限流规则配置

首先,我们需要创建一个专门的配置文件来存放限流规则(limit_rules.conf):

/etc/nginx/conf.d/limit_rules.conf

# 识别爬虫 User-Agent
map $http_user_agent $isbot_ua {
    default 0;
    ~(GoogleBot|bingbot|YandexBot|mj12bot|facebookexternalhit) 1;
}

# 仅对爬虫进行限流
map $isbot_ua $limit_bot {
    default "";
    1 $binary_remote_addr;
}

# 定义限流区域
limit_req_zone $limit_bot zone=bots:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=one:10m rate=100r/m;

配置解析:

  • map 指令创建变量映射,用于识别爬虫
  • ~* 表示大小写不敏感的正则匹配
  • limit_req_zone 定义限流区域和速率
  • 10m 表示共享内存区域大小
  • rate 定义请求速率

2. 站点配置

在具体的站点配置中应用限流规则:

/etc/nginx/conf.d/demo-site.conf

server {
    listen 80;
    listen [::]:80;

    server_name IPv4Address;

    # 日志配置
    access_log /var/log/nginx/access.log combined;
    error_log /var/log/nginx/error.log;

    location / {
        # 当超过限制时返回 429 状态码
        limit_req_status 429;

        # 爬虫限制:允许5次/分钟
        limit_req zone=bots burst=3 nodelay;

        # 正常用户限制 允许10次/秒
        limit_req zone=one burst=10;

        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

验证服务搭建

为了验证配置效果,我们创建一个简单的 Flask 应用:

# server/app.py
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/')
def home():
    # 获取客户端IP和User-Agent
    client_ip = request.headers.get('X-Real-IP', request.remote_addr)
    user_agent = request.headers.get('User-Agent', '')

    return jsonify({
        'status': 'success',
        'client_ip': client_ip,
        'user_agent': user_agent,
        'message': 'Request received successfully'
    })

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000)

客户端测试实现

我们编写一个 Python 测试脚本来模拟不同类型的请求:

# client/test_client.py
import requests
import time
from concurrent.futures import ThreadPoolExecutor

# 测试用的User-Agent列表
USER_AGENTS = {
    'normal': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'google': 'Googlebot/2.1 (+http://www.google.com/bot.html)',
    'bing': 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)',
    'facebook': 'facebookexternalhit/1.1',
    'yandex': 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'
}

def make_request(url, user_agent):
    headers = {'User-Agent': user_agent}
    try:
        response = requests.get(url, headers=headers)
        return response.status_code
    except requests.exceptions.RequestException as e:
        return str(e)

def test_rate_limit(url, user_agent_type, num_requests):
    print(f"\nTesting with {user_agent_type} User-Agent")
    user_agent = USER_AGENTS[user_agent_type]

    success_count = 0
    rate_limited_count = 0

    with ThreadPoolExecutor(max_workers=10) as executor:
        futures = []
        for _ in range(num_requests):
            futures.append(executor.submit(make_request, url, user_agent))
            time.sleep(0.1)  # 小延迟,避免请求过于密集

        for future in futures:
            status = future.result()
            if status == 200:
                success_count += 1
            elif status == 429:
                rate_limited_count += 1
            else:
                print(f"Unexpected status code: {status}")


    print(f"Results for {user_agent_type}:")
    print(f"Success: {success_count}")
    print(f"Rate Limited: {rate_limited_count}")

def main():
    url = 'http://localhost:5000/'

    # 测试不同类型的请求
    test_rate_limit(url, 'normal', 120)  # 测试正常用户
    test_rate_limit(url, 'google', 30)   # 测试Google爬虫
    test_rate_limit(url, 'bing', 30)     # 测试Bing爬虫
    test_rate_limit(url, 'facebook', 30) # 测试Facebook爬虫
    test_rate_limit(url, 'yandex', 30)   # 测试Yandex爬虫
    test_rate_limit(url, 'baidu', 30)    # 测试未识别的Baidu爬虫

if __name__ == '__main__':
    main()

测试结果分析

过运行测试脚本,我们可以观察到:

  1. 普通用户请求:
  2. 成功率较高
  3. 仅在短时突发请求时触发限制

  4. 爬虫请求:

  5. 严格遵循每分钟限制
  6. 超出限制时返回 429 状态码

总结

通过本文的配置方案,我们实现了:

  1. 智能识别各大搜索引擎爬虫
  2. 差异化的请求频率限制
  3. 优雅的限流处理机制

这套配置不仅保护了服务器资源,也确保了正常用户的访问体验。建议根据实际情况调整限流参数,找到最适合您网站的配置。

参考资料