反爬虫精讲!通过学习这篇让你学会无视百分之85的反爬网站!

背景介绍:

为了平衡社区成员的贡献和索取, 一起帮 引入了 帮帮币 。当用户积分(帮帮点)达到一定数额之后,就会“掉落”一定数量的“帮帮币”。为了增加趣味性,帮帮币“掉落”之后所有用户都可以“捡取”,谁先捡到归谁。

反爬虫精讲!通过学习这篇让你学会无视百分之85的反爬网站!

进群:548377875   即可获取数十套PDF哦!记住是分开私信,不是一起私信!

但这样就产生了一个问题,因为这个“帮帮币”是可以买卖有价值的,所以难免会有恶意用户用爬虫不断的扫描,导致这样的情况出现:

注:经核实, 乔布斯的同学 其实没有用爬虫,就是手工点,点出来的!还能说什么呢?只能表示佩服啊佩服……

所以我们需要一种机制,阻止这种爬虫的行为。

大致思路:

这个问题我们有一个很便利的前提:只有注册用户才能够“捡起”帮帮币。所以,我们不需要通过“封IP”(需获取真实IP)这种方式来阻断爬虫爬行,而是直接封注册用户,非常方便。

那么如何判断一个请求是真实用户,还是爬虫呢?我们决定使用最简单的方法:记录访问频次。当某一个用户的访问频次高于设定值时(比如:5分钟10次),就判定该用户“有爬虫嫌疑”。

此外,为了防止误判(确实有用户手快),我们还应该给用户一个“解锁”的功能:通过输入验证码来确定不是爬虫。

反爬虫精讲!通过学习这篇让你学会无视百分之85的反爬网站!

细节设计:

一个最核心的问题是:用什么来 记录用户的访问频次 ?

数据库?感觉没必要,这个数据又不需要长期保留,访问一次就做一次I/O操作在性能上接受不了,所以我们决定使用内存。

但是,具体需要记录那些数据,又用什么样的数据结构呢?

最后我们选择使用缓存,记录最简单的“用户ID -> 访问次数”键值对,来解决这个问题,因为:

  • 利用缓存的自动清除(expire)特性,清除过期数据,保证记录的访问次数始终是在一定时间内的。
  • 缓存的读写速度很快,性能上没有压力

当然,这里其实还是有那么点问题的。比如,假设缓存时间是5分钟,最多访问次数是10次。0:10,开始缓存访问次数,一直累加,到0:14,共记录访问次数7次,没有问题;然而,一过0:15,缓存被清空,0:16的时候,缓存里只有0:15到0:16这一分钟的数据,没有过去5分钟(从0:11到0:16)的数据。所以用户可以控制一直爬虫,访问9次,然后就歇着,5分钟过后,再继续访问9次,然后再歇5分钟……

唉~~真这么拼,我还真没什么办法?但如果这么一个频次他能接受的话,我其实也无所谓,你就慢慢爬呗。或者,我们后台做更大的监控,把每个用户的每次访问都记录下来,进行统计,找出异常。那时候可能就真的需要数据库了(为了提高性能可以内存里放一个DataTable,定时同步到Database)。但暂时来说,没有这个必要。

此外,还有一个问题,是不是只需要记录用户访问频次?

如果按上述方案,在缓存里记录访问频次,通过缓存数据来判断是否允许继续访问,会有一个问题:缓存到期失效之后,这个用户就又可以自由访问目标页面了!相当于到期自动解锁。

我觉得这还是不科学,如果认定是爬虫,只能是人工解锁(识别码验证)。所以在数据库用户表里添加一个“已锁定”(Locked)字段,如果用户被锁定,Update其为当前时间;未锁定时(解锁后)为NULL。

具体实现:

为了重用,我们需要利用 Authorize Fitler,在它的OnAuthorization()方法里面进行检查和记录。

代码本身应该比较简单,if...else...的逻辑:

 ///1. 先根据数据库捡查当前用户是否被锁定
 ///2. 如果被锁定,直接拦截。否则:
 ///3. 在缓存中检查有无当前用户的访问次数记录
 /// 3.1 没有,新建一条他的缓存。否则:
 /// 3.2 检查该用户已访问次数
 /// 3.2.1 如果已到达访问次数限制,拦截并在数据库中锁定该用户。否则
 /// 3.2.2 累加用户的访问次数

精简注释代码如下:

 public class Needlogon : AuthorizeAttribute
 {
 public override void OnAuthorization(AuthorizationContext filterContext)
 {
 HttpContextBase context = filterContext.HttpContext;
 ///Autofac相关操作,获取正取的ISharedService实例
 ISharedService service = AutofacConfig.Container.Resolve();
 _NavigatorModel model = service.Get(); //从数据库获取当前User的信息
 ///截断式编程,减少if...else的{}嵌套
 if (model.Locked.HasValue)
 {
 ///model.Locked 来自数据库用户已经被锁定,拦截
 visitTooMuch(filterContext);
 return;
 }
 string cacheKey = CacheKey.MAX_VISIT + model.Id;
 ///非常有意思,不能直接使用int值类型,必须使用引用类型的
 VisitCounter amount;
 if (context.Cache[cacheKey] == null)
 {
 amount = new VisitCounter { Value = 1 };
 ///新建立一条Cache
 context.Cache.Add(cacheKey,amount,null,DateTime.Now.AddSeconds(Config.Seconds),Cache.NoSlidingExpiration,CacheItemPriority.normal,null);
 }
 else
 {
 amount = context.Cache[cacheKey] as VisitCounter;
 if (amount.Value >= Config.MaxVisit)
 {
 ///在数据库中锁定该用户
 service.LockCurrentUser();
 BaseService.Commit();
 ///立即清除Cache
 context.Cache.Remove(cacheKey);
 visitTooMuch(filterContext);
 return;}
 else
 {
 ///不能使用:currentVisitAmount++;
 ///context.Cache[cacheKey] = currentVisitAmount;
 ///见:https://stackoverflow.com/questions/2118067/cached-item-never-expiring
 amount.Value++;
 }
 }
 }
 }
 public class VisitCounter
 {
 public int Value { get; set; }
 }

仔细观察代码,你会发现两个问题。这就是飞哥我曾经掉的坑啊!o(╥﹏╥)o

1、为什么要引入 VisitCounter类?

缓存里就存放着这个类的实例,而这个类其实就包裹一个int Value;干嘛呢,这是?为什么不直接用int呢?直接把int存到Cache里不行吗?

不行啊!艹。

存进去,没问题;取出来,也没问题;但更新(累加)的时候有问题啊。你怎么更新?

 //取出缓存
 currentVisitAmount = Convert.ToInt32(context.Cache[cacheKey]);
 //累加
 currentVisitAmount++;
 //再存进去
 context.Cache[cacheKey] = currentVisitAmount;

这样不行的,具体的解释看这里: Cached item never expiring 。

简单的说,context.Cache[cacheKey] = currentVisitAmount; 这一句,等于重新插入了一条永不过期的缓存。万万没想到啊!这个bug把飞哥都差点搞疯了,本来cache的调试都非常麻烦,还搞个这种幺蛾子。

所以解决的办法是什么呢?在Cache里存一个引用类型值,然后不改Cache,只改引用类实例里的值就OK了。代码就不重复了。

2、在锁定用户的同时,清除该用户的cache

这里啊,曾经走了点弯路。

我最开始是在解锁用户的时候清除该用户的Cache。

 [Needlogon]
 public ActionResult Unlock()
 {
 string userId = getCurrentUserId();
 string cacheKey = CacheKey.MAX_VISIT + userId;
 HttpContext.Cache.Remove(cacheKey);
 return View(new Imagecodemodel());
 }

结果不知道咋回事,时灵时不灵。我把本地代码,连接服务器数据库,开着Debug模式,一步一会的进去看,OK,没问题;但把本地代码发布到服务器,duang,不行了?!没法调试,只有写log啥的,坑得我不要不要的……

后来突然发现,这里有“坏代码的味道”:重复。你看这个cacheKey的构建,是不是在 Needlogon.OnAuthorization()里构建过一次?重复使用的代码是不是就应该封装?所以呢,开始呢,是想弄一个方法出来获得cacheKey,比如striing GetVisitLimitCacheKey()啥的,但这个方法要让Controller里的UnLock()和Filter里的OnAuthorization()都能调用,放在哪里呢?

突然灵光一闪:为什么 Cache.Remove 要写在UnLock()里面呢?

其实只要用户被锁定,他的缓存信息就没用了。因为我们已经在数据库中标明了他被Locked,所以Needlogon.OnAuthorization()拦截住他,不需要Cache呀!尽早的清除这个Cache,还能提高那么一点点的性能

最关键的是,这样代码更紧凑了:cacheKe在同一个方法里被使用,cache操作在同一个方法类完成,避免了代码分散耦合,优雅多了!

最后的总结:

最近“老编程之家”的话题比较热,至少我是这么感觉的(我这都是开第三个“老编程之家”的QQ群了,群号拿去:834748431)。

我个人认为,老编程之家“没用了”“干不动了”“没公司要了”……,这些东西肯定是YY出来的。代码的质量在于它的逻辑它的内核,你怎么定义问题怎么解决问题,先有了思路然后才有实现。思路清晰了,实现才有可能优雅。至于什么“喜欢学习新技术”“没有女朋友能加班”,就有些外行了。年轻人真“喜欢”学习新技术吗?哈哈,虽然我大叔了,但也年轻过,你别骗我。问题是年轻人要想往上爬,只能学习新技术。一些需要经验积累的东西,他没法学,难道他来学管理学架构学带团队?至于加班,其实是一种陋习,我这么多QQ群,一到上班时间就热闹起来,一到周末就冷冷清清,你说他们的工作强度有多大,需要天天加班才能完成?而且稍有经验的开发人员都知道,项目的进度,一定程度之后,加人加班都是无效的。每天996,人家究竟是在写代码,还是在写bug,鬼大爷才知道!

当然,即使是老编程之家,好的代码也都是改出来的,“如切如磋,如琢如磨”,需要一个不断打磨的过程。

“一起帮”的代码,需要打磨的地方,其实还有很多很多。但是呢,这里面有一个成本的问题。群里经常有网友吐槽他们公司的代码烂,要是早些年呢,我也会和他们一起吐槽,吐吐更健康。但现在,还是有点兔死狐悲的感觉。我就在想:哪一天,可能我的代码也会被人这样吐槽吧,哈哈……

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
说到Pooling,相信学习过CNN的朋友们都不会感到陌生。Pooling在中文当中的意思是“池化”,在神经网络当中非常常见,通常用的比较多的一种是Max Pooling,具体操作如下图: 结合图像理解,相信你也会大概明白其中的本意。不过Pooling并不是只可以选取2x2的窗口大小,即便是3x3,
记得大一学Python的时候,有一个题目是判断一个数是否是复数。当时觉得比较复杂不好写,就琢磨了一个偷懒的好办法,用异常处理的手段便可以大大程度帮助你简短代码(偷懒)。以下是判断整数和复数的两段小代码: 相信看到这里,你也有所顿悟,能拓展出更多有意思的方法~
文章目录 3 直方图Histogramplot1. 基本直方图的绘制 Basic histogram2. 数据分布与密度信息显示 Control rug and density on seaborn histogram3. 带箱形图的直方图 Histogram with a boxplot on t
文章目录 5 小提琴图Violinplot1. 基础小提琴图绘制 Basic violinplot2. 小提琴图样式自定义 Custom seaborn violinplot3. 小提琴图颜色自定义 Control color of seaborn violinplot4. 分组小提琴图 Group
文章目录 4 核密度图Densityplot1. 基础核密度图绘制 Basic density plot2. 核密度图的区间控制 Control bandwidth of density plot3. 多个变量的核密度图绘制 Density plot of several variables4. 边
首先 import tensorflow as tf tf.argmax(tenso,n)函数会返回tensor中参数指定的维度中的最大值的索引或者向量。当tensor为矩阵返回向量,tensor为向量返回索引号。其中n表示具体参数的维度。 以实际例子为说明: import tensorflow a
seaborn学习笔记章节 seaborn是一个基于matplotlib的Python数据可视化库。seaborn是matplotlib的高级封装,可以绘制有吸引力且信息丰富的统计图形。相对于matplotlib,seaborn语法更简洁,两者关系类似于numpy和pandas之间的关系,seabo
Python ConfigParser教程显示了如何使用ConfigParser在Python中使用配置文件。 文章目录 1 介绍1.1 Python ConfigParser读取文件1.2 Python ConfigParser中的节1.3 Python ConfigParser从字符串中读取数据
1. 处理Excel 电子表格笔记(第12章)(代码下载) 本文主要介绍openpyxl 的2.5.12版处理excel电子表格,原书是2.1.4 版,OpenPyXL 团队会经常发布新版本。不过不用担心,新版本应该在相当长的时间内向后兼容。如果你有新版本,想看看它提供了什么新功能,可以查看Open
1. 发送电子邮件和短信笔记(第16章)(代码下载) 1.1 发送电子邮件 简单邮件传输协议(SMTP)是用于发送电子邮件的协议。SMTP 规定电子邮件应该如何格式化、加密、在邮件服务器之间传递,以及在你点击发送后,计算机要处理的所有其他细节。。但是,你并不需要知道这些技术细节,因为Python 的
文章目录 12 绘图实例(4) Drawing example(4)1. Scatterplot with varying point sizes and hues(relplot)2. Scatterplot with categorical variables(swarmplot)3. Scat
文章目录 10 绘图实例(2) Drawing example(2)1. Grouped violinplots with split violins(violinplot)2. Annotated heatmaps(heatmap)3. Hexbin plot with marginal dist
文章目录 9 绘图实例(1) Drawing example(1)1. Anscombe’s quartet(lmplot)2. Color palette choices(barplot)3. Different cubehelix palettes(kdeplot)4. Distribution
Python装饰器教程展示了如何在Python中使用装饰器基本功能。 文章目录 1 使用教程1.1 Python装饰器简单示例1.2 带@符号的Python装饰器1.3 用参数修饰函数1.4 Python装饰器修改数据1.5 Python多层装饰器1.6 Python装饰器计时示例 2 参考 1 使
1. 用GUI 自动化控制键盘和鼠标第18章 (代码下载) pyautogui模块可以向Windows、OS X 和Linux 发送虚拟按键和鼠标点击。根据使用的操作系统,在安装pyautogui之前,可能需要安装一些其他模块。 Windows: 不需要安装其他模块。OS X: sudo pip3
文章目录 生成文件目录结构多图合并找出文件夹中相似图像 生成文件目录结构 生成文件夹或文件的目录结构,并保存结果。可选是否滤除目录,特定文件以及可以设定最大查找文件结构深度。效果如下: root:[z:/] |--a.py |--image | |--cat1.jpg | |--cat2.jpg |
文章目录 VENN DIAGRAM(维恩图)1. 具有2个分组的基本的维恩图 Venn diagram with 2 groups2. 具有3个组的基本维恩图 Venn diagram with 3 groups3. 自定义维恩图 Custom Venn diagram4. 精致的维恩图 Elabo
mxnet60分钟入门Gluon教程代码下载,适合做过深度学习的人使用。入门教程地址: https://beta.mxnet.io/guide/getting-started/crash-course/index.html mxnet安装方法:pip install mxnet 1 在mxnet中使
文章目录 1 安装2 快速入门2.1 基本用法2.2 输出图像格式2.3 图像style设置2.4 属性2.5 子图和聚类 3 实例4 如何进一步使用python graphviz Graphviz是一款能够自动排版的流程图绘图软件。python graphviz则是graphviz的python实