说到爬虫,我认为大部分开发者都能充当矛与盾的制造者。
-
那为什么要制造爬虫?让有价值的信息可被编程处理
-
那为什么要对抗爬虫?让信息的价值尽量被所有者拥有
Part1 反爬虫
除了对访问者进行一些基本的风控,你可能见过一些小众的反爬虫手段,比如:
1 字体动态渲染
比如猫眼电影的案例
好好地数字查看源码确实乱码。这种属于后端动态生成一个字体,用于正确渲染数据供用户查看。
2 乱序段落渲染
比如一个新闻网站的案例
字体不隐藏,但是将段落顺序打乱,然后前端通过计算data-s
将正确的段落顺序还原出来。
这些反爬虫都是利用前端执行Javascript
的能力,让用户能够正常阅读就行。 当然以上这些案例都有解决的办法,能够让用户方便阅读的,还原起来也不难。
所以现在更常用的手段应该是对访问“用户”做风控,来判断访问者到底是不是一个没有恶意的正常人。
不过反爬虫手段也不一定是要阻止恶意手段来获取自己有价值的数据,比如:避免自动化获得我网站上的内容。
Part2 为什么要避免被自动化获得站点内容
你见过这些吗:
图源:https://cloud.tencent.com/developer/article/1591533
或者是这些:
这我自己遇到的😢
你会很奇怪,我网址就是在即时聊天软件里面发一发,有的时候甚至都没发,怎么就被提示了?
因为有东西在不停歇地获取网站的内容,与关键词做匹配来进行一个简单的判断。
⚠️注意:这里不是教你钻空子,除了机器审核外,仍有人工审核的部分,不要存在侥幸心理。
那么我们的需求就很简单了,只要让通过非浏览器的方式(包括不开启Javascript
的浏览器)访问站点的时候无法得到真正的网站内容即可。
比如Cloudflare提供的JS Challenge
,就是一个很不错的案例,让用户在访问前先等个几秒钟。
不过这不是广告,如果优点很多就不会有这篇文章了,在国内套上Cloudflare后,就是减速器;你可能花了大价钱买的优质线路,走了它家的CDN后大家平起平坐(仅限免费版用户),一样缓慢。
但是不是全部的自动化检索内容的访问者的目的都是为了检查你网站有没有害,还有一个搜索引擎。所以我们也要为等待页做好SEO,避免影响网站的搜索情况。
Part3 动手实现一个JS Challenge
3 JS Challenge做了什么?
要实现它之前,我们先了解一下JS Challenge做了什么:
-
在访问网页时,判断用户是否已经被标记为合法,比如Cookies配合Session的数据,如果合法则直接渲染网页内容。
-
如果不合法,则渲染一个等待页面,页面中包含加密的(也可以不加密)的JS代码,运算后能够得到一个唯一的结果,并将这个结果返回服务端与预先存储的结果比对,一致则标记为合法用户一段时间。
也就是说,一般的爬虫手段(比如直接模拟HTTP请求)是无法正常访问网站的,因为无法按预期执行JS代码。
4 那要实现一个什么东西?
很遗憾,我并不知道搜索什么关键词,问GPT也只是在给我推荐网上现成的方案,搜索js challenge
出来的都是编程题目…
所以我不得不想办法自己实现,我大概总结了目标程序要满足以下几个要求:
-
一段计算要花时间的JS代码
-
我需要在服务端提前设置答案
-
答案唯一,且在前端不是那么容易被读取到
所以我扭头想到了一个东西:区块链,这里我就不过多提及了,虽然我要做的东西没有区块链这么复杂,但是有一个东西可能满足以上要求,那就是:算哈希
简单来说,哈希就是一个固定的算法,对唯一的内容生成出一个固定位数且唯一的字符串。如常用的
MD5
算法就是32个由0-9
和a-f
组成的字符串。
Part4 开始实现
5 设定目标
那么什么样的事情能够让我在服务端提前得知,又没那么容易在前端得到答案呢?我的答案是:
服务端生成几位符合条件的字符串
,前端通过暴力计算出末尾是我服务端提供的字符串
,然后前端把字符串交给服务端计算哈希末尾是不是刚刚下发的字符串。
那么用什么方法来计算哈希?JS有一个对象Crypto,浏览器兼容性如下:
那就用这个来实现吧。
主打的就是一个不实测就相信
6 验证算法
当然SHA-1
是一种不安全的加密算法,所以我要求GPT生成代码的时候总是警告我,不过好在最后还是给了我这么一段代码:
async function sha256(str) {
const encoder = new TextEncoder();
const data = encoder.encode(str);
const hash = await crypto.subtle.digest('SHA-256', data);
const hexHash = Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
return hexHash;
}
async function findHashCollision() {
const targetSuffix = 'fff';
const maxLength = 10;
const output = [];
for (let i = 0; i < Math.pow(36, maxLength); i++) {
const str = i.toString(36).padStart(maxLength, '0');
const hash = await sha256(str);
if (hash.endsWith(targetSuffix)) {
output.push(str);
}
}
return output;
}
findHashCollision().then(output => {
console.log(output);
});
不过GPT还是很执着地给了我SHA-256
的实现。他通过不断计算数字来完成碰撞,让我们稍作修改,然后在浏览器上试一下:
const encoder = new TextEncoder();
async function sha1(str) {
const hash = await crypto.subtle.digest('SHA-1', encoder.encode(str));
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
async function work(target) {
const maxLength = 10;
for (let i = 0; i < Number.MAX_SAFE_INTEGER; i++) {
const hash = await sha1(i);
if (hash.endsWith(target)) {
return i;
}
}
}
work('fff').then(output => {
console.log(output);
});
经过测试,找3位是一下就出来了,4位差不多1秒,5位有点为难人了,差不多需要10多秒。
那综合考虑就4位吧。
7 看看通用性怎么样
仅仅测试ffff
或者aaaa
这样一样的字符串我觉得可能不够,那就试一下乱敲的:
9a9a
的速度稍微慢了点,但也是差不多1秒左右。那就验证没啥大问题了:后端随机生成一个字符串,然后让前端碰撞出符合条件的数字。
8 先搞个好看的前端
在真正访问到我们的站点前还是有一点点延迟,所以要整一个友好的等待跳转页面。
但是如果东西写多了可能还没加载完就跳转了,思来想去不如就模仿对着抄Cloudflare的吧🤪
9 后端处理部分
$status = session('challenge');
if($status === "pass")
return $next($request);
if(isset($_REQUEST['_challenge'])){
if (substr(sha1($_REQUEST['_challenge']), -4) === $status){
session(['challenge' => 'pass']);
return $next($request);
}
}
$challenge = substr(sha1(rand()), -4);
session(['challenge' => $challenge]);
return response()->view('common/challenge',['code' => $challenge]);
直接贴代码,实现上还是比较简单的。
10 再加一点细节
当然,除了恶意的机器人,也有好的机器人,比如:搜索引擎
搜索引擎也是一种爬虫,并且人家也没有那么多资源来跑你的JS Challenge,那怎么办?因为产品性质不一样,所以我只提及一点想法:
-
把JS Challenge在HTTP处理的层次放到处理完页面渲染后,这个时候是能获得SEO信息的。
-
放行搜索引擎的特征,不过有绕过的风险,不太清楚Cloudflare是怎么做的,有空研究研究。
所以在我的产品里,我通过IP数据库以及兼容CF传递过来的国家代码:
if(isset($_SERVER["HTTP_CF_IPCOUNTRY"]))
$isoCode = $_SERVER["HTTP_CF_IPCOUNTRY"];
else{
$reader = new Reader(storage_path('app/library/GeoLite2-Country.mmdb'));
$isoCode = $reader->country($request->ip())->country->isoCode;
}
if($isoCode != 'CN'){
session(['challenge' => 'pass']);
return $next($request);
}
这么处理一下,就能够达成这次的目标。
最后:
Python学习资料
如果你想学习Python帮助你实现自动化办公,或者准备学习Python或者正在学习,下面这些你应该能用得上,有需要可以领取。
① Python所有方向的学习路线图,清楚各个方向要学什么东西
② 100多节Python课程视频,涵盖必备基础、爬虫和数据分析
③ 100多个Python实战案例,学习不再是只会理论
④ 华为出品独家Python漫画教程,手机也能学习
⑤历年互联网企业Python面试真题,复习时非常方便
文末有领取方式哦
一、Python所有方向的学习路线
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python课程视频
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
三、Python实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
四、Python漫画教程
用通俗易懂的漫画,来教你学习Python,让你更容易记住,并且不会枯燥乏味。
五、互联网企业面试真题
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
这份完整版的Python全套学习资料已经上传CSDN,朋友们如果需要也可以扫描下方csdn官方二维码或者点击主页和文章下方的微信卡片获取领取方式,【保证100%免费】