当前位置: 首页 > article >正文

分级反爬虫是什么?JAVA实现反爬虫策略

 如何防止网站被爬虫?

1.使用协议条款

robots.txt是一个放置在网站根目录下的文件,可以添加规则来告诉搜索引擎的爬虫 禁止特定目录或文件被抓取

User-agent: *
Disallow: /private/
Disallow: /important/

虽然大多数合规爬虫会遵守这些规则,但恶意爬虫可能会忽视它,所以,robots.txt只是防护的第一步,起到一个威慑和证据的作用

2.限制数据获取条件

可以要求用户登录或提供 API 密钥才能访问特定数据。比如使用OAuth 2.0或 JWT(JSON Web Tokens),确保只有授权用户能够访问敏感数据,有效阻止未经授权的爬虫获取数据。

3.统计访问频率和封禁

可以利用 Redis 或 Caffeine 本地缓存来记录每个 IP 或客户端的请求次数,设置阈值来限制单个 IP 地址的访问频率。当检测到异常流量时,系统可以自动封禁该 IP 地址,或者采取其他的策略。

注意:虽然 Map 也能够统计请求频率,但由于请求不断累加,占用的内存也会持续增长,因为 Map 无法自动释放资源。如果一定要使用内存进行请求频率统计,可以使用 Caffeine 这种具有数据淘汰机制的缓存技术。

4.多级处理策略

为了防止 “误伤”,比起直接对非法爬虫客户端进行封号,可以设定一个更灵活的多级处理策略。

比如当检测到异常流量时,先发出警告;如果爬虫行为继续存在,则采取更严厉的措施,如暂时封禁 IP 地址;如果解封后继续爬虫,再进行永久封禁等处罚。

5.自动告警 + 人工介入

可以开发系统自动告警能力,比如在检测到异常流量或爬虫行为时,系统能自动发出企业微信消息通知。然后网站的管理员就可以及时介入,对爬虫的请求进行进一步分析和处理。

企业的线上系统最好接入全方面的告警,比如接口错误、CPU / 内存占用率过高之类的。

6.爬虫行为分析

非法爬虫和正常用户的行为一般是有区别的,爬虫往往遵循特定的访问模式。比如正常用户每道题目都要看一会儿、看的时间也不一样,而爬虫一般是按照固定的顺序、固定的频率来获取题目,很明显就能识别出来。

7.请求头检测

每个发送到服务器的请求都有请求头信息,可以通过检查请求头中的User-AgentReferer等标识符,对爬虫请求进行拦截。

当然这招只能防菜鸟,因为请求头是可以很轻松地伪造的,只要通过浏览器自带的网络控制台获取到响应正常的请求头信息,就可以绕过检测了

8.自主公开数据

核心就是让攻击者的成本大于实际的收益。比如密码 10 分钟有效,破解密码要花 15 分钟,就不会有人去破解。

用到爬虫场景上,我们的做法是,不做任何限制,直接让所有人不登录也能查看到我们网站的题目数据!而且还提供了题目的各种筛选功能、收藏功能。大多数同学只是为了自己学习,这样一来,就没有必要花时间去爬数据了~

9.溯源技术

虽然题目都是公开的,但有些VIP优质题解是仅会员可见的。如果有用户使用爬虫抓取了这部分数据,可就要小心了!

一般只要你在一个网站登录了,一定会有访问记录,如果你泄露了网站登录后才可见的内容、尤其是付费内容,管理员一定有办法追溯到你是谁

比较常用的溯源技术就是水印、盲水印等。对于刷题网站,是可以通过微信登录的,而且如果你是会员,肯定还有支付记录。这些技术不仅帮助标记数据源,还可以在数据被滥用时追踪其来源,从而增强数据的保护。

10.科普法律

除了上面这些方法外,还可以通过接入反爬服务、接入验证码、增加动态时间戳等方式进一步限制爬虫。但是要记住,爬虫是没有办法完美防御的!因为你无法限制真实的用户,攻击者完全可以模拟真实用户的访问方式来获取你的网站数据,比如找 10 个用户,每人获取几百题。

所以最后一个方法是 —— 科普法律。告知用户未经授权的抓取行为是违法的,可以对爬虫行为起到一定的威慑作用。爬虫是有一定风险的,自己学习倒没问题,但是千万别给人家的网站造成压力了,搞不好就有破坏计算机系统的嫌疑了!

后端开发

对于一个简单的项目,如果主要的目的是反爬虫,而不是应对高并发大流量的请求,所以不需要结合 Sentinel 或 Hotkey 去精确统计流量。

自主实现固定时间窗口( 1分钟 )的访问频率统计就足够了。为了便于项目扩展为分布式,使用 Redis 方案来实现。

核心就是编写通用计数器

/**
 * 基于原子类,通用计数器(可用于实现频率统计、限流、封禁等等)
 */
@Slf4j
@Service
public class CounterManager {

    @Resource
    private RedissonClient redissonClient;

    /// 三个重载的计数方法
    //原子计数器(缓存键,时间间隔,时间间隔单位,计数器缓存过期时间)
    public long incrAndGetCounter(String key, int timeInterval, TimeUnit timeUnit,
                                  long expirationTimeInSeconds) {
        if (StrUtil.isBlank(key)) {
            return 0;
        }
        long timeFactor;  //时间片
 // (比如时间间隔1min,当前时间7:05,基准时间7:00,则当前时间片为5,即第五个时间间隔内,所有在这个时间片内的访问次数同+)
        switch (timeUnit) {
            case SECONDS:
                //获取1970年1.1到当前过的秒数(时间戳) // 时间间隔
                //表示当前时间所在的时间片位置(比如)
                timeFactor = Instant.now().getEpochSecond() / timeInterval;
                break;
            case MINUTES:
                timeFactor = Instant.now().getEpochSecond() / timeInterval / 60;
                break;
            case HOURS:
                timeFactor = Instant.now().getEpochSecond() / timeInterval / 3600;
                break;
            default:
                throw new IllegalArgumentException("不支持的单位");
        }
        //动态生成 Redis Key
        String redisKey = key + ":" + timeFactor;

        // Lua 脚本
        String luaScript =
                // 判断键是否存在,存在则自增,不存在则设置值并设置过期时间,并返回自增后的值
                "if redis.call('exists', KEYS[1]) == 1 then " +
                        "  return redis.call('incr', KEYS[1]); " +
                        "else " +
                        "  redis.call('set', KEYS[1], 1); " +
                        "  redis.call('expire', KEYS[1], ARGV[1]); " +
                        "  return 1; " +
                        "end";

        // --执行 Lua 脚本--
        //获取脚本执行器
        RScript script = redissonClient.getScript(IntegerCodec.INSTANCE);
       // 执行脚本并返回结果
        Object countObj = script.eval(
                RScript.Mode.READ_WRITE,  // 表示脚本需要读写权限
                luaScript,
                RScript.ReturnType.INTEGER, //指定返回值类型为整数
                //传递给脚本的Key列表,这里是用户key
                Collections.singletonList(redisKey),
                expirationTimeInSeconds  //传递给脚本的参数列表(这里是过期时间)
        );
        return (long) countObj;
    }
}

然后编写一个校验爬虫并分级处理的逻辑

@Resource
private CounterManager counterManager;
//检测loginUserId的爬虫
private void crawlerDetect(long loginUserId) {
    // 调用多少次时告警
    final int WARN_COUNT = 10;
    // 调用多少次时封号
    final int BAN_COUNT = 20;
    // 拼接访问 key
    String key = String.format("user:access:%s", loginUserId);
    // 统计一分钟内访问次数,180 秒过期
    long count = counterManager.incrAndGetCounter(key, 1, TimeUnit.MINUTES, 180);
    // 是否封号
    if (count > BAN_COUNT) { //次数>阈值
        // 踢下线
        StpUtil.kickout(loginUserId);
        // 封号
        User updateUser = new User();
        updateUser.setId(loginUserId);
        updateUser.setUserRole("ban");
        userService.updateById(updateUser);
        throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "访问次数过多,已被封号");
    }
    // 是否告警
    if (count == WARN_COUNT) {
        // 可以改为向管理员发送邮件通知
        throw new BusinessException(110, "警告:访问太频繁");
    }
}

最后在需要校验爬虫的接口上调用该方法就可以了


http://www.kler.cn/a/601172.html

相关文章:

  • K8S学习之基础五十:k8s中pod时区问题并通过kibana查看日志
  • uniapp中$emit的使用方法
  • RWEQ 模型深度讲解:结合 Python、ArcGIS 等实现土壤风蚀归因分析
  • GitHub和Gitee上的一些AI项目
  • Zbrush插件安装
  • HarmonyOS NEXT 实现拖动卡片背景模糊效果
  • 独立组网和非独立组网
  • Linux 多线程-生产消费者模型线程池线程单例模式其他
  • 力扣刷题39. 组合总和
  • github怎么关闭issue禁止评论设置步骤
  • [ChatGPT 高级进阶系列] 用ChatGPT加速前端开发:高级思维链提示全解析
  • 【漫话机器学习系列】161.验证曲线(Validation Curve)
  • 括号合法题
  • 在CentOS系统上运行Ruby on Rails应用的详细步骤
  • 借助可视化,快速洞察数据背后的商机
  • 游戏如何检测GG修改器
  • 英伟达「虚拟轨道+AI调度」专利:开启自动驾驶3.0时代的隐形革命
  • 表单查询、多表查询
  • 3.24前端模拟面试
  • 如何下载 Postman?快速指南!