导读:本期聚焦于张立峰创作的《Python编写Flask接口如何防止爬虫抓取?使用User-Agent与频率限制实战》,敬请观看详情。接口被恶意爬虫刷取会导致服务器带宽耗尽与数据泄露。从请求头中的User-Agent字段入手,可快速识别异常客户端,但伪造成本低。更稳妥的做法是结合频率限制,在固定时间窗口内约束单个IP或令牌的访问次数。本文以Flask为例,演示如何读取请求头、建立UA黑名单、利用内存或Redis实现滑动窗口限流,并分析各方案在单机与分布式环境下的差异,帮助开发者用较小改造成本提升接口抗爬能力。

在基于Flask构建对外提供数据的Web接口时,接口暴露在公网意味着任何客户端都能发起HTTP请求。爬虫程序往往会在短时间内高频调用接口,不仅占用带宽和数据库连接,还可能批量窃取业务数据。通过校验请求头里的User-Agent以及实施访问频率限制,可以在不引入复杂风控系统的前提下,拦截大部分低级爬虫并缓解恶意高频请求。

Python编写Flask接口如何防止爬虫抓取?使用User-Agent与频率限制实战

理解User-Agent在请求中的角色与伪造风险

User-Agent是HTTP协议定义的标准请求头字段,用于告知服务器发起请求的客户端类型、操作系统及版本等信息。在Flask中,可以通过request.headers.get('User-Agent')直接读取该字段。正常浏览器访问时,User-Agent通常包含明确的浏览器标识,例如Mozilla/5.0 (Windows NT 10.0; Win64; x64)前缀。而很多简单爬虫脚本使用Python的requests库时,默认UA为python-requests/2.31.0,这就成了识别线索。

不过必须清楚,User-Agent完全由客户端控制,攻击者可以随意修改。使用curl命令时加上-A参数就能伪装成任意浏览器。因此,仅靠UA做放行或拦截并不可靠,它更适合作为第一道粗筛:把明显为空、包含爬虫特征字符串的请求直接拒绝,减少后续逻辑的资源消耗。对于伪造为浏览器的爬虫,需要配合频率限制来约束行为。

下面示例展示了一个最基础的UA过滤装饰器,将常见爬虫标识加入黑名单,在接口函数执行前先做判断:

from flask import Flask, request, abort
import functools

app = Flask(__name__)

BAD_UA_KEYWORDS = ['python-requests', 'curl', 'scrapy', 'bot']

def block_bad_ua(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        ua = request.headers.get('User-Agent', '')
        for kw in BAD_UA_KEYWORDS:
            if kw in ua.lower():
                abort(403, description='Forbidden User-Agent')
        return func(*args, **kwargs)
    return wrapper

@app.route('/api/data')
@block_bad_ua
def api_data():
    return {'msg': 'ok'}

基于Flask实现单机频率限制方案

频率限制的核心思想是记录每个客户端的请求时间戳,当单位时间内的请求数超过阈值时拒绝服务。在单进程、单实例部署的Flask应用中,使用Python标准库中的collections.defaultdict配合列表存储时间戳即可实现固定窗口限流。例如限制每个IP每六十秒最多访问三十次,每次请求时清理掉超过一分钟前的记录再统计数量。

这种内存方案代码简单、无外部依赖,但存在明显局限:Flask以多线程或多worker模式运行时,各进程内存不共享,限流状态会分散,导致实际允许的总请求数随worker数量倍增。此外,服务重启后限制记录清空,攻击者可以利用重启间隙突破限制。下面的代码演示了在单进程模式下用全局字典实现的限流装饰器:

from flask import Flask, request, abort
from collections import defaultdict
import time

app = Flask(__name__)

REQUEST_RECORDS = defaultdict(list)
MAX_REQUESTS = 30
WINDOW_SECONDS = 60

def rate_limit_ip(func):
    def wrapper(*args, **kwargs):
        client_ip = request.remote_addr
        now = time.time()
        records = REQUEST_RECORDS[client_ip]
        # 清理过期时间戳
        while records and records[0] < now - WINDOW_SECONDS:
            records.pop(0)
        if len(records) >= MAX_REQUESTS:
            abort(429, description='Too Many Requests')
        records.append(now)
        return func(*args, **kwargs)
    return wrapper

@app.route('/api/limited')
@rate_limit_ip
def api_limited():
    return {'msg': 'within limit'}

若希望避免列表频繁弹出带来的性能损耗,也可采用令牌桶思路:为每个IP维护一个令牌数,定时补充。但无论哪种内存方案,都只适用于演示或极小流量内部接口。当项目使用Gunicorn多worker或者部署在多个容器实例时,必须引入集中式存储。

结合Redis的分布式限流与UA联合策略

在分布式环境中,所有Flask实例应连接同一个Redis服务,利用Redis的原子操作实现精确限流。常用做法是使用有序集合记录请求时间,或通过INCR配合EXPIRE做计数器。有序集合方案能支持滑动窗口,避免固定窗口在边界时刻允许双倍流量的问题。同时,我们把UA校验与Redis限流组合进同一个前置钩子,既拦爬虫标识又控访问速率。

具体实现中,可以用redis-py库,在每次请求时用ZREMRANGEBYSCORE删除旧记录,再用ZCARD获取当前窗口计数。若超过限制则返回429,否则用ZADD写入当前时间戳。由于Redis命令执行是单线程模型,多实例并发下也能准确计数。下表对比了三种方案的差异:

方案部署适应性精确度额外依赖
UA黑名单任意低,可伪造绕过
内存限流仅单进程中,重启失效
Redis限流多实例分布式高,滑动窗口Redis服务

以下代码给出了UA加Redis限流的整合示例,假设已创建全局redis_client对象:

import time
from flask import Flask, request, abort
import redis

app = Flask(__name__)
redis_client = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)

BAD_UA = ['python-requests', 'scrapy']
RATE_KEY_PREFIX = 'flask_rate:'
WINDOW = 60
MAX_COUNT = 50

def anti_crawl():
    ua = request.headers.get('User-Agent', '').lower()
    for kw in BAD_UA:
        if kw in ua:
            abort(403)
    ip = request.remote_addr
    key = RATE_KEY_PREFIX + ip
    now = time.time()
    # 移除窗口外记录
    redis_client.zremrangebyscore(key, 0, now - WINDOW)
    count = redis_client.zcard(key)
    if count >= MAX_COUNT:
        abort(429)
    redis_client.zadd(key, {str(now): now})
    redis_client.expire(key, WINDOW)

@app.before_request
def before():
    if request.path.startswith('/api/'):
        anti_crawl()

@app.route('/api/info')
def api_info():
    return {'data': 'protected'}

在实际业务中,还可以将频率限制细化到登录用户令牌而非仅IP,防止同一局域网共用出口IP导致误伤。对于极重要的接口,建议在网关层如Nginx上叠加限流,形成多层防护。通过User-Agent初筛与Redis频率限制的组合,Flask接口能以较低开发成本抵御绝大多数自动化抓取行为。

FlaskUser_Agent频率限制修改时间:2026-08-17 19:16:14

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。