写在最前面:本人也是个算法小白,如果有什么不对还望大佬指正,谢谢
算法设计评价基本标准
算法是对特定问题求解步骤的一种描述,如果将问题看作函数,那么算法是吧输入转化为输出
算法:
是对特定问题求解步骤的一种描述,是为了解决一个或者一类问题给出的 一个确定的、有限长的操作序列。 算法的设计依赖于数据的存储结构,因此对确定的问题,应该需求子啊适宜的存储结构上设计出一种效率较高的算法
算法的重要特性:
有穷性:
对于任何一组合法的输入值,在执行有穷步骤之后一定能结束,即算法中的操作步骤为有限个,并且每个步骤都能在有限的时间内完成
确定性:
对于每种情况下所应该执行的路径的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行;并且在确切的条件下只有唯一一条执行流程路径
可行性:
算法中的所有操作都必须足够基本,都可以通过已经实现的基本运算执行有限次实现
有输入:
作为算法加工对象的量值,通常体现为算法中的一组变量。有些输入量需要在算法的执行过程中输入,而有些算法表面上没有输入,但实际上已被嵌入算法之中
有输出:
它是一组与“输入”有确定关系的量值,是算法进行信息加工够得到的结果。这种确定关系即为算法的功能
算法设计目标:
正确性:
算法应满足具体问题的需求,正确反映求解问题对输入、输出加工处理等方面的需求
- 程序中不含语法错误,计算的结果却不能满足规格说明要求。
- 程序对于特定的几组输入数据能够得出满足要求的结果,而对于其他的输入数据的不出正确的计算结果;
- 程序对于精心选择的、经典、苛刻且带有刁难性的几组输入数据能够得到满足需求的结果;(正确的算法)
- 程序对于一切合法的输入数据都能得出满足要求的结果
可读性:
算法处理用于编写程序子啊计算机上执行之外,另一个重要用处是阅读和交流。 算法中加入适当的注释,介绍算法的设计思路、各个模块的功能等一些必要性的说明文字来帮助读者理解算法。 要求: 算法中加入适当的注释,介绍算法的设计思路、各个模块的功能等一些必要性的说明文字来帮助读者理解算法 对算法中出现的各种自定义变量和类型能做到“见名知义”,即读者一看到某个变量(或类型名)就能知道其功能
健壮性:
当输入数据非法时,算法能够适当地做出反应或进行处理,输出表示错误性质的信息并终止执行,而不会产生莫名其妙的输出结果。
时间效率与存储占用量:
一般来说,求解同一个问题若有多种算法,则 执行时间短的算法效率高 占用存储空间少的算法较好 算法的执行时间开销和存储空间开销往往相互制约,对高时间效率和低存储占用的要求只能根据问题的性质折中处理
算法复杂度
算法的时间复杂度:
算法的效率指的是算法的执行时间随问题“规模”(通常用整型量 n 表示)的增长而增长的趋势 “规模”在此指的是输入量的数目,比如在排序问题中,问题的规模可以是被排序的元素数目 假如随着问题规模 n 的增长,算法执行时间的增长率和问题规模的增长率相同则可记为: T(n) = O(f(n))
f(n) 为问题规模 n 的某个函数; T(n)被称为算法的(渐进)时间复杂度(Time Complexity) O 表示法不需要给出运行时间的精确值; 选择 f(n),通常选择比较简单的函数形式,并忽略低次项和系数 常用的有 O(1)、O(logn)、O(n)、O(nlogn)、O(n*n)等等 多项式时间复杂度的关系为: O(1) < O(logn) < O(n) < O(N logn) < O(n²) < O(n³) 指数时间算法的关系为: O(2(n 方))< O(n!) <O(n(n 方))
由于估算算法时间复杂度关心的只是算法执行时间的增长率而不是绝对时间,因此可以忽略一些因素。 方法:从算法中选取一种对于所研究的问题来说是“基本操作”的原操作,以该“基本操作”在算法中重复执行的次数作为算法时间复杂度的依据。 EG:
两个 n x n 的矩阵相乘,求其时间复杂度
1
2 for i in range(10):
for j in range(10):问题规模是矩阵的阶 n,算法的控制结构式三重循环,基本操作是乘法操作 乘法执行次数为 n³,则算法的时间复杂度为 O(n³)
算法的空间复杂度:
算法在执行期间所需要的存储量包括:
- 程序代码所占用空间
- 输入数据所占用空间
- 辅助变量所占用的空间
为了降低复杂度,一个直观的思路是:梳理程序,看其流程中是否有无效的计算或者无效的存储。我们需要从时间复杂度和空间复杂度两个维度来考虑。 常用的降低时间复杂度的方法有递归、二分法、排序算法、动态规划等, 降低空间复杂度的方法,就要围绕数据结构做文章了。 降低空间复杂度的核心思路就是,能用低复杂度的数据结构能解决问题,就千万不要用高复杂度的数据结构。
如何评定一个程序算法的好坏?
简而言之:在符合算法设计标准的前提下,运行的更快、所用计算资源更少。即是更好的算法
请注意这里的比较级别词更!!!,对于算法,个人认为就是获得同一结果的同时探究最优解决之道
探究算法优化(自我一点点小体悟-个人能力有限)
抽象化:将不必要的计算过程去掉
利用高斯算法解决累计求和问题
慎选各数据结构,善用第三方算法
去重: 善用迭代什么的等等 使用字典的特性-不可重复性 布隆过滤器去重(源于哈希算法)
时空转换:将昂贵的时间转化为廉价的空间
当‘优无可优时’,取贵舍廉 空间是可以用钱买的,加个什么内存啊,加个处理器等等什么的 时间是逝去就不在了
以上仅仅个人的一点点小灵感,望大家不喜勿喷
说了这么多,来道简单的算法题目导入(同一计算机,i5, python)
需求如下:累计求和 1-n 的值(1. 为防止误差,验证 10 次; 2. 验证每次计算次数)
1 |
# 在同一空间复杂下(也就是说没有迭代,探究暴力解法与高斯算法) |
Sum is 100010000 Required 0.0010061264 seconds count = 10001 Sum is 100010000 Required 0.0009891987 seconds count = 10001 Sum is 100010000 Required 0.0000000000 seconds count = 10001 Sum is 100010000 Required 0.0010235310 seconds count = 10001 Sum is 100010000 Required 0.0009710789 seconds count = 10001 Sum is 100010000 Required 0.0000000000 seconds count = 10001 Sum is 100010000 Required 0.0009973049 seconds count = 10001 Sum is 100010000 Required 0.0010013580 seconds count = 10001 Sum is 100010000 Required 0.0000000000 seconds count = 10001 Sum is 100010000 Required 0.0019786358 seconds count = 10001 探究可知:n 扩大 10 倍,运算时间也会扩大 10 倍 高斯算法,从 1 累加至 n,等于(首项+尾项)项数/2 直接引用结论: 1+2+3+…+(n-1) +n={(1+n)+(2+(n-1))…}/2 = (n (n + 1))/2
1 |
# Gaussian算法,无迭代算法 |
计算结果如下: 循环计算 10000 次,出去 I/O 所需时间几乎可以不计。 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000000123123000000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.000000000000000000000000000000000000000000000000012332100000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.00000000000000000000000000000000000000000000000000000002310000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.000000000000000000000000000000000000000000000000000000010032000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000012231300000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552 Sum is 49999999999999998486656110625518082973725163772751181324120875475173424217777037767098169202353125934013756207986941204091067867184139242319692520523619938935511795533394990905590906653083564427444224 Required 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 seconds count = 5000000000000000079514455548799590234180404281972640694890663778873919386085190530406734992928407552




任意点开一篇新闻,看到的结果如下:
我现在需要做到的是在不编写任何 XPath、Selector 的情况下实现下面信息的提取: 对于列表页来说,我要提取新闻的所有标题列表和对应的链接,它们就是图中的红色区域:
这里红色区域分了多个区块,比如这里一共就是 40 个链接,我都需要提取出来,包括标题的名称,标题的 URL。 我们看到页面里面还有很多无用的链接,如上图绿色区域,包括分类、内部导航等,这些需要排除掉。 对于详情页,我主要关心的内容有标题、发布时间、正文内容,它们就是图中红色区域:
其中这里也带有一些干扰项,比如绿色区域的侧边栏的内容,无用的分享链接等。 总之,我想实现某种算法,实现如上两大部分的智能化提取。
然后编写提取代码如下:
编写测试代码如下:
后面会有大规模测试和修正。 项目初版,肯定存在很多不足,希望大家可以多发 Issue 和提 PR。 另外这里建立了一个 Gerapy 开发交流群,之前在 QQ 群的也欢迎加入,以后交流就在微信群了,大家在使用过程遇到关于 Gerapy、Gerapy Auto Extractor 的问题欢迎交流。 这里放一个临时二维码,后期可能会失效,失效后大家可以到公众号「进击的 Coder」获取加群方式。 

上面这行标题,没什么用,又这么难看。 我把偏好设置里面的显示内容都去掉了,设置如下:
但是它总是还显示了一个标题,显示成这个样子:
上面这个标题看得很难受,我想把它改成无任何内容,简洁清爽,如下图所示:
但是现在无论我怎么改偏好设置都不行,总会带上那些信息。 后来搜索了一番发现是 zsh 的问题。 打开
另外注意这里标题处不能完全为空,需要打上一个空格,否则窗口上方会显示「终端」二字。 最后在
完毕。









如果没有加入团队是没有权限向所相关的库进行推送的!!! 如何设置(获得)权限呢?(在仓库中点击设置选项进入如下界面) 
完成以上操作之后在进行推送即可有权限完成推送。



具体的流程可以参考上面的流程图:首先是 fork 一个仓库,然后拉去请求 后 clone 到本地,然后编辑更改。后推送到远程仓库(这里的克隆操作就省略了,具体的可以看上面)
发送即可完成。 然后接收者根据流程图步骤 6 开始操作即可
步骤如下:




版本前进 




系统用户级别的 config 是在系统目录下,如果不想太麻烦的去找直接 cat ~/.git config 即可
选择相对应的版本下载即可,下载完成后打开相对应的安装执行,有选项的选择的建议点击第一个默认配置,就不在此过多赘述啦。
以上的网页地址为:
Ubuntu:



经过一系列测试发现,其实际需操作的 URL 为
基本网站的分析就分析完毕了 注意此处为 POST 请求!!!
仔细经过上述步骤即可进入本次加密的源码详情页
搜索 sign 参数,得知本页面有 15 个 sign,筛选排查过后可得知以下位置为 sign 等参数,赋值加密过程 为什么会大概确定是此处呢? 理由一:var 声明赋值 理由二:md5() 为什么深信此处呢?
根据其语法可知,Javascript










这里一些接口的名称和 URL 我就打码了,这里我可以在 Grafana 中每时每刻都看到每个接口的可用率、响应(包括平均、最快、最慢)时间、状态码等信息,这些信息就是 JMeter 定时检测得到的结果,监控数据转到 Prometheus 里面然后经过 Grafana 可视化出来,并能通过一些指标来实现报警机制。 感兴趣的话,可以继续往下看哈。 为了达成这些功能,我需要解决如下问题:
这里提示几点可能用到的东西:
这里字段名如 jsr223_rt_as_summary 可以自行修改,比如这里我就统一修改为了 jmeter_test_xxx 这样的字段。 配置完成之后,运行 JMeter 之后,我们就能在
这里面就包含了 JMeter 的一些接口测试结果,包括成功次数、失败次数、状态码等等,另外还有 JVM、处理器等各种环境信息。 部署之后把对应的 URL 交由 Prometheus 就可以把监控数据收集到 Prometheus 里面了。
这里就能实时可视化展示出来错误率了,更多的一些配置可以自行修改这些表达式进行定制。 最后我配置成的一些监控面板如下所示:
这样我要是什么时候想看 Service 接口的情况,随时上来看就好了。
不过公司内部已经实现了一套了,对接了公司的员工账号,更加方便,所以我就没有再用这个了。
只需要给模型输入一张带缺口的验证码图片,模型就能输出缺口的轮廓和边界信息。 感兴趣的可以继续向下看具体的实现流程。
标注完了会生成一系列 xml 文件,你需要解析 xml 文件把位置的坐标和类别等处理一下,转成训练模型需要的数据。 在这里我先把我整理的数据集放出来吧,完整 GitHub 链接为:
val_mAP 变化如下:
可以看到 loss 从最初的非常高下降到了很低,准确率也逐渐接近 100%。 另外训练过程中还能看到如下的输出结果:
这里我们可以看到,利用训练好的模型我们就成功识别出缺口的位置了,另外程序还会打印输出这个边框的中心点和宽高信息。 有了这个边界信息,我们再利用某些手段拖动滑块即可通过验证了。本节不再展开讲解。





大家可以看到全站的内容都变成灰色了,包括按钮、图片等等。这时候我们可能会好奇这是怎么做到的呢? 有人会以为所有的内容都统一换了一个 CSS 样式,图片也全换成灰色的了,按钮等样式也统一换成了灰色样式。但你想想这个成本也太高了,而且万一某个控件忘记加灰色样式了岂不是太突兀了。 其实,解决方案很简单,只需要几行代码就能搞定了。
其 CSS 内容为:
果然是这个样式在起作用,而且是全局的效果,因为它是作用在了 html 这个节点之上的。 另外看看 CSDN,它也是用的这个 CSS 样式,其内容为:
比如这里通过 filter 样式改变了图片、颜色、模糊、对比度等等信息。 其所有用法示例如下:
再说回刚才的灰色图像,这里其实就是设置了 grayscale,其用法如下:
这里我们看到,这里除了 IE,其他的 PC、手机端的浏览器都支持了,Firefox 的 PC、安卓端还单独对 SVG 图像加了支持,可以放心使用。
接着我们点击 PyCharm 的 Tools -> Deployment -> Configuration,这里我们可以配置远程 SFTP 服务器,如图所示:
打开之后是这样子,这里选择 SFTP,然后填入服务器的连接信息,如图所示:
在这里可以点「TEST CONNECTION」测试下是否能够连接成功。 OK,配置完了之后,我们已经成功添加好了一台远程服务器了,比如我这里就添加了一台我自己的服务器,Host 为 vm1.cuiqingcai.com。 既然要实现本地和服务器文件同步,那么当然必须要指定本地项目文件夹和远程哪个文件夹同步吧。在哪里指定呢?切换到第二个选项卡,Mappings,如图所示:
这里我们可以通过选择 LocalPath 和 Deployment Path 分别指定本地和远程的文件夹名称。注意这里后者指的是相对服务器工作目录的路径。 好了,就是这样,基本配置就完成了。如果你还想配置某些路径不同步的话,还可以在第三个选项卡 Excluded Paths 里面配置。 接着,还有一些可以配置的地方,点击 Tools -> Deployment -> Options 我们可以配置更多细节,如图所示:
比如这里我就配置了下什么时候上传,这里我改成了按 Ctrl + S 保存的时候再上传,这样我可以自由控制上传的时机。 另外这里还需要把自动上传勾选上,如图所示:
好了,整个都配置好啦。
这样就上传完毕了。 接着我们任意修改一个文件,按保存,即 Ctrl + S,这里就出现了自动上传的日志,提示某个文件被上传成功了。
OK,验证没问题。
点了之后就会提示选择哪个远程服务器,选了之后,下方 Terminal 就弹出来了,和普通的 SSH Shell 一模一样。
OK,接下来要构建镜像,我只需要运行对应的 docker-compose 命令就好了,速度瞬间就上来了,我再也不用看着龟速的下拉速度而发愁了,而不用担心本地机器的资源消耗了。
OK,美滋滋。 构建完了运行之后,直接远程访问就好了。
这些云服务器被用做测试用机、学习用机或者正式生产用机,有些朋友会在上面搭建博客,有些朋友在上面搭建 API。东鸽自己也手握几台服务器:
其中有 3 台用作实际的生产,新书《
一个普通的生产者消费者模式就诞生了。如果数据业务体量比较大,或者说数据业务的出口比较多,那我可能会借用消息中间件来进行调节。
为了保证数据的完整性和可用性,我可能会对服务器进行镜像,那么我就需要 2~3 台服务器。假设每台服务器的成本是 300元/年,数据合作业务的收入是 3W/年,那真是四两拨千斤了。 要合理利用资源,让资源为我们生小钱钱! 服务器配置跟具体业务需求有关,用于测试的服务器通常是 1 核 2G 1M;博客和论坛的服务器带宽稍大一些,带宽通常是 3M ;用于数据合作的服务器内存较大,一般需要为 4G~8G; 除了服务器之外,华为云还有很多神奇的业务。我觉得黑科技真的是太多了。
上次我用华为云的深度学习一体化服务 ModelArts 在线实现了图像深度学习中的图片标注、图片分类预测和模型部署+生成 API,感觉很流畅。
鼠标点点点,配置一丢丢参数,也不用自己写代码,也不用弄 Web 框架,唰唰唰地几下,就可以调用了。 崔庆才崔哥用 ModelArts 实现了爬虫工程师常用到的拼图验证码缺口识别 API。
样本准备了小小几十个,训练完成后的识别准确率很高,这样我们就不用担心缺口位置定位的问题了。
服务器数量多,开销自然就大。如无必要,通常我是不会购买服务器的,但每当云服务器厂商有活动时我都会做一些购买计划。例如我今年打算增加数据业务,并且学习架构方面的知识,那我至少得再买 5 台服务器。趁着华为开年采购季活动,购买一台服务器的成本最低 79元/年。除此之外,华为云还有数据库专场活动、域名建站专场活动、云安全专场活动在等着我们,买买买!
大家相识已久,东鸽感谢大家关注和支持,这次华为云开年采购季活动我自掏腰包购买 3 台 1 核 2G 1M 的服务器(1年期)送给各位粉丝。大家扫码即可参与抽奖,到期自动开奖。 参与要求:参与活动时必须转发上方华为云开年采购季海报,领奖时小编会检查的哦。 
「Pocket」这个软件有一个不错的特性,那就是跨平台,我可以在浏览器、Mac、iPhone、iPad 上使用,看到不错的网站,调出插件或者点击「分享到 Pocket」就可以存进去了,这样就解决了多平台同步问题,另外 「Pocket」还能设置一些标签等等,然后每周或定期我再翻出来整理整理。 说实话,我用「Pocket」好几年了,但总觉得一直达不到我心中完美的标准,怎么说呢?下面总结一下:
哎,一眼难尽啊,左边浓浓的拟物化风格,右边自作聪明出了个阅读模式,也就是自动提取正文内容,结果把文章格式整的这么乱。
所以说,这软件的界面、操作和功能,只能说我不喜欢。不喜欢就不要将就,我将就了这么久,最后还是要放弃它。
大家可以看视频来感受一下:
另外内容还支持各种其他的布局,如列表式:
瀑布流式:
舒服了。 说回功能,这里我先分了几个分组,为「网站」、「代码」、「图片」,我估计我用到的最多的肯定是「网站」这个分组,用来存各种链接的,「代码」和「图片」是为了测试它的上传文件和图片功能而加的,可以用来存代码文件和图片。 分组建好之后,我们可以在分组里面添加收藏夹,看图里面每行前面有个小图标的就是一个收藏夹。没错,每个收藏夹都可以自定义它的小图标,这个功能真的是增色不少! 它提供了非常多的小图标,看:
这个功能,我觉得简直不能更赞!另外这些小图标还可以自己上传,所以你看图里面的 GitHub 图标、Python 图标,都是我从 icons8 上面找到并上传的,毫无违和感! 另外每一个收藏它都会自动生成一张封面图,会自动截取网站当前页面的内容,或者也可以自定义上传图片或者自定义截屏,最后每个收藏项都会变成一张张卡片。 当然添加标签页不在话下。
另外浏览器的插件也做的很精致,提供了 Mini Application 和极简模式,收藏一个页面只需要点一下这个图标就收藏好了。如果是高级版的账号,还支持自动分类。
手机和 iPad 上面的软件我也试了,功能基本类似。怎么保存呢?比如我的 iPhone 上可以把这个软件放到分享的 App 列表里面,这样在浏览器里面点击「分享」,然后选「Raindrop.io」就好了,它会自动弹出一个窗口,让我们选择分类或加标签,体验很不错。
嗯,总之整体体验下来,非常好用。 另外它还支持自动导入书签或从其他的收藏软件里面迁移,大家如果一些网站保存在书签里面的话,如果用了这个,可以快速导入进来,非常方便!
另外还有一些高级的功能大家再探索吧。怎么感觉越写越像个广告文了,但确实这不是广告文,它确实很好用,在这强推给大家!希望它能帮助大家方便管理各种资源,什么博客、公众号、干货、微博、图片、文件统统可以保存到这里并明确清晰地归类啦!
然后 15 分钟过后,大家会针对大家疑虑的点进行讨论。 注意,由于有了 Comments,大家就知道一共有多少需要讨论的点,这个会还剩余多久,每个 Comment 可以讨论多长时间,这样又避免了开会时间无节制的问题。 然后还有一个重要的点,就是每讨论完一个 Comment,就在对应的地方写上解决方案或者附上 Todo List,即具体的实施措施,做到当场讨论问题、当场提出解决方案、当场分配任务计划。 OK,然后整个会就很有目标地开完了,很有侧重点,而且大家都清除的就不需要讨论了。 另外还有一个优点就是,每个人都可以提 Comments,这样每个人都会感到极强的参与感,再也不会出现会上一言不发事不关己高高挂起的状态了。
截止今天(2 月 9 日)晚上 9 点,全国确诊病例达到 37289 例,疑似病例 28942 例,每天还以好几千的数量增加,估计明天早上能突破 4 万。 全国 31 个省区均早已经启动了一级响应,这在过去是从未有过的,随着病毒的传播蔓延,事态的严重性远远超出人们最大胆的想象。现在国家正在举全国之力支援灾区,前线的医疗人员一直在全力以赴救治患者。全国各个地区也在采取可以说是历史上最严格的防疫措施,全国范围内的商场、餐厅、娱乐场所几乎都被关停,各家各户的居民也都在家隔离,企业也延迟复工或者到现在还有很多企业都没有正式复工,口罩物资都已经全网脱销,一罩难求。但没有办法,在这个非常时刻,只要我们每个人都尽上自己的所能,相信疫情肯定会慢慢控制下来,请相信我们的国家。 观察了几天,从数据上来看,可能有这么两个好消息: 
CDC 称,在其流感活动地图上,美国大部分地区为深红色,表明「流感样疾病」活动水平达到最高级。由于美国的流感季还要持续一段时间,因此这一数据可能会继续上升。从地区上看,目前全美 50 个州里,有 48 个州出现流感疫情,其中 32 个州流感活动水平维持高位,人口稠密的纽约、华盛顿和加州无一幸免,纷纷中招。此次流感季将是 10 年来美国最严重的流感季之一,2019 - 2020 流感季中,美国 1900 万人感染流感,至少 1 万人死亡,包括 68 名儿童。预计流感季将持续至 5 月,而 2 月是高峰期。 我有几个亲戚朋友现在就住在美国,因为流感的问题,每天他们也是和我们一样,待在家里不敢出门,同样体会出来了 ”隔离“ 的滋味。
这场火,不仅造成人命伤亡及经济损失,也对自然生态带来毁灭性破坏,使数亿动物遭遇灭顶之灾。已有近 5 亿只动物死于澳洲山火,并据相关报道显示考拉或将功能性灭绝。悉尼大学发布报告,全国约有 10 亿动物被大火波及,其中仅在澳大利亚袋鼠岛的大火中就有超 2 万只考拉死亡,考拉将被列为濒危动物。澳大利亚全境被烧毁的森林面积约 1120 万 公顷,而去年震惊世界的亚马孙雨林大火烧毁森林面积才约 180 万公顷。此外,有 1400 多公里的海岸线都在燃烧,相当从东北烧到了江浙沪。 慢慢地,前几天,山火逐渐逼近了堪培拉。1 月 31 日,因为山火的步步紧逼,澳大利亚政府宣布堪培拉进入紧急状态,这是自 2003 年山火危机之后 17 年以来,堪培拉首次进入紧急状态。2 月 2 日整天,堪培拉均处于高度戒备状态。堪培拉和周边地区的气温一度超过 42 摄氏度。 当时堪培拉就是这么一个状态:
其他的地区山火基本是这么一个状态:
以及那些被烧死的无辜的动物们:
澳大利亚的这场大火也对全球造成了影响。欧洲哥白尼大气监测服务发表数据:澳大利亚已经向大气排放约 4 亿吨二氧化碳,这个数据比全球 116 个二氧化碳排放量最低国家年排放量总和还要高。 不过,就在最近几天,现在澳洲的山火由于一场暴雨的到来,很多地区的火已经灭了。但,这还没完,由于雨过大,洪水又来了。 据《每日邮报》2 月 9 日报道,近日,澳大利亚遭遇了十年来最大降雨,东海岸被大规模破坏,悉尼正在全力应对洪水爆发,火车站都变成了游泳池。澳大利亚气象局预计,在新南威尔士州北部的一些气象站在 48 小时内录得超过 300 毫米的降雨后,降雨将持续到周日。 据报道,周六,在新南威尔士州北部,67 岁的吉尔·萨瑟兰和她 30 岁的侄女汉娜驱车前往位于新南威尔士州北部河流地区的宁宾,途中他们穿过了一条被洪水淹没的道路。然而,他们很快就失去了对汽车的控制,车子灌满了水,沉了下去,完全从视线中消失了。
但好在,大火已经基本停了,但愿这次洪水也能尽快过去,祝好!
军队现已对山区的村镇与交通设施展开全力救助,巴西政府也声称将努力建立一个全国性的灾害预防和早期预警系统。



军方戒严了城区主要街道,喀土穆国际机场已关闭。中国驻苏丹使馆发出安全警告,提醒中国公民不要靠近机场区域。
印尼国家抗灾署发言人阿古斯在一份声明中说,截至当地时间 6 日下午,灾害还造成 1 人失踪,另有 3.6 万人居住在临时避难所。由于强降雨引发洪灾、山体滑坡等灾害,雅加达周边的 12 个县市已陆续宣布进入为期一到两周的紧急状态,以便救援和物资运输工作展开。
联合国粮食及农业组织已经发出警告,蝗灾将会造成近年来罕见的粮食危机,1900 万人将面临危及生命的饥荒。 这个确实非常严重的,一些更详细的报道大家可以了解澎湃新闻报道的视频:

登录完成之后就可以看到 Splash 的界面了,如图所示。 
以上,便是 Kubernetes 搭建 Splash 的方法。 希望对大家有帮助。

















图 6-4 示例 5 页面 在编写 Python 代码之前,我们需要确定目标数据的元素定位。航空公司名称元素定位如图 6-5 所示。
图 6-5 航空公司名称元素定位结果 航空公司名称包裹在没有属性的 span 标签中,但该 span 标签包裹在 class 属性为 air g-tips 的 div 标签中。接下来我们看一下航站名称的元素定位,定位结果如图 6-6 所示。
图 6-6 航站名称元素定位结果 航站名称包裹在没有属性的 h2 标签中,h2 标签包裹在 class 为 sep-lf 的 div 标签中。 我们再看一下票价的元素定位,定位结果如图 6-7 所示。
图 6-7 票价的元素定位结果 页面中显示的票价为 467,但是在网页中却有两组不同的数字,其中一组是[7, 7, 7],而另一组是 [6, 4],这看起来就有点奇怪了。 难道是网页显示有问题? 按照正常排序来说,这架航班的票价应该是 77 764 才对。我们可以查看第二架航班信息的价格,思考是网页显示问题还是做了什么反爬虫措施。第二架航班的票价元素定位结果如图 6-8 所示。
图 6-8 第二架航班的票价元素定位结果 结果与第一架航班的票价显示有同样的问题:网页显示内容和 HTML 代码中的内容不一致。我们分析一下 HTML 代码,看一看是否能找到什么线索。第一架航班票价的 HTML 代码为:
图 6-9 数字组合推测 5 个数字的组合结果太多了,我们必须找出其中的规律,这样就能知道网页为什么显示 467 而不是 764 或者 776 。在仔细查看过后,发现每个带有数字的标签都设定了样式。第 1 对 b 标签的样式为:
图 6-10 span 标签对和 i 标签对位置图 此时网页中显示的价格应该是 777,但是由于第 2 和第 3 对 b 标签中有值,所以我们还需要计算它们的位置。此时标签位置的变化如图 6-11 所示。
图 6-11 标签位置变化 右侧是标签位置变化后的结果,由于第 2 对 b 标签的位置样式是 left:-32px,所以第 2 对 b 标签中的值 6 就会覆盖原来第 1 对 b 标签中的中的第 2 个数字 7,此时页面应该显示的数字是 767。 按此规律推算,第 3 对 b 标签的位置样式是 left:-48px,这个标签的值会覆盖第 1 对 b 标签中的第 1 个数字 7,覆盖结果如图 6-12 所示,最后显示的票价是 467。
图 6-12 覆盖结果 根据结果来看这种算法是合理的,不过我们还需要对其进行验证,现在将第二架航班的 HTML 值 和 CSS 样式按照这个规律进行推算。最后推算得到的结果与页面显示结果相同,说明这个位置偏移的计算方法是正确的,这样我们就可以编写 Python 代码获取网页中的票价信息了。因为 b 标签包裹在 class 属性为 rel 的 em 标签下,所以我们要定位所有的 em 标签。对应的 Python 代码如下:
图 6-13 去哪儿网航班信息 航班票价对应的 HTML 代码如图 6-14 所示。
图 6-14 去哪儿网航班票价 HTML 代码 去哪儿网航班票价所对应的 HTML 代码结构和 CSS 与我们在示例 5 中见到的类似。我们可以大胆猜测,去哪儿网航班票价的显示规律与示例 5 中所用的方法也是类似的,感兴趣的同学可以按照 6.2.1 节的思路进行票价推算。去哪儿网航班票价中第 1 对 b 标签下的 i 标签数量与 width 是相匹配的,并未出现显示错误的问题。
想要与作者韦世东交流或者参加新书发布活动的朋友可以扫描二维码进群与我互动哦!
图 6-15 示例 6 页面 在编写 Python 代码之前,我们需要确定目标数据的元素定位。在定位过程中,发现一个与以往不同的现象:有些数字在 HTML 代码中并不存在。例如口味的评分数据,其元素定位如图 6-16 所示。
图 6-16 评分数据中口味分数元素定位 根据页面显示内容,HTML 代码中应该是 8.7 才对,但实际上我们看到的却是:
图 6-17 class 属性值和数字的对比 从图 6-17 中可以看出,class 属性值和数字是一一对应的,如属性值 vhk08k 与数字 0 对应。根据这个线索,我们可以猜测每个数字都与一个属性值对应,对应关系如图 6-18 所示。
图 6-18 数字与属性值对应关系 浏览器在渲染页面的时候就会按照这个对应关系进行映射,所以页面中显示的是数字,而我们在 HTML 代码中看到的则是这些 class 属性值。浏览器在渲染时将 HTML 中的 d 标签与数字按照此关系进行映射,并将映射结果呈现在页面中。映射逻辑如图 6-19 所示。
图 6-19 映射逻辑 我们的爬虫代码可以按照同样的逻辑实现映射功能,在解析 HTML 代码时将 d 标签的 class 属性值取出来,然后进行映射即可得到页面中显示的数字。如何在爬虫代码中实现映射关系呢?实际上网页中使用的是“属性名数字”这种结构,Python 中内置的字典正好可以满足我们的需求。我们可以用 Python 代码测试一下,代码如下:
图 6-20 大众点评商家信息页 大众点评的商家信息页主要用于展示消费者对商家的各项评分、商家电话、店铺地址和推荐菜品等。我们可以看一看商家电话或评分的 HTML 代码,如图 6-21 所示。
图 6-21 商家电话 HTML 代码 大众点评中的商家号码并不是全部使用 d 标签代替,其中有部分使用了数字。但是仔细观察一下就可以发现商家号码的数量等于 d 标签数量加上数字的数量,说明 d 标签的 class 属性值与数字也有可能是一一对应的映射关系。感兴趣的同学可以使用示例 6 中的方法,尝试映射大众点评案例中的数字。 如果这种手段的绕过方法这么简单的话,那么它早就被淘汰了,为什么连大众点评这样的大型网站都会使用呢?我们继续往下看,大众点评的商家营业时间部分的 HTML 代码如图 6-22 所示。
图 6-22 大众点评商家营业时间 除了刚才的数字映射之外,大众点评还对中文进行了映射。此时如果按照示例 6 中人为地将 class 值和对应的文字进行映射的话,就非常麻烦了。试想一下,如果网页中所有的文字都使用这种映射反爬虫的手段,那么爬虫工程师要如何应对呢?对所有用到的文字进行映射吗? 这不可能做到,其中要完成映射的包括 10 个数字、26 个英文字母和几千个常用汉字。而且目标网站一旦更改文字的对应关系,那么爬虫工程师就需要重新映射所有文字。面对这样的问题,我们必须找到文字映射规律,并且能够使用 Python 语言实现映射算法。如此一来,无论目标网站文字映射的对应关系如何变化,我们都能够使用这套映射算法得到正确的结果。 这种映射关系在网页中是如何实现的呢?是使用 JavaScript 在页面中定义数组吗?还是异步请求API 拿到 JSON 数据?这都有可能,接下来我们就去寻找答案。
图 6-23 标签背景图 d 标签的背景图中全部都是数字,这些无序的数字共有 4 行。但这好像不是一张大图片,我们查看该图片页面的源代码,内容如图 6-24 所示。
图 6-24 图片页面源代码 源代码中前两行表明这是一个 SVG 文件,该文件中使用 text 标签定义文本, style 标签用于设置文本样式, text 标签定义的文本正是图片页面显示的数字。难道这些无序的数字就是我们在页面中看到的电话号码和评分数字? 除了 class 属性值为 vhkbvu 的 d 标签,其他标签也使用了这个的 CSS 样式,但每对 d 标签的坐标定位都不同。它们的坐标定位如下:
图 6-25 test.svg 显示内容 代码前 3 行声明文件类型,第 4 行~第 5 行定义了 SVG 内容块和画布宽高,第 6 行使用 text 标签定义了一段文本并指定了文本的坐标。这段文本就是我们在浏览器中看到的内容,而代码中的 x 坐标和 y 坐标则用于确定该文本在画布中的位置,坐标规则如下。
图 6-26 x 为 0 时的 test.svg 显示内容 x 的值为 0 时,文本紧贴浏览器左侧。而 x 的值为 10 时,文本距离浏览器左侧有一定的距离,这说明 x 的值能够决定文字所在的位置。现在我们将代码中 x 对应的值改为“10 50 30 40 20 60”(注意这里特意将第 2个数字 20与第 5个数字互换了位置),这样做是为了设定前 6个字符的坐标位置。 此时,第 1 个字符的位置参数为 10,第 2 个字符的位置参数为 50,第 3 个字符的位置参数为 30,以此类推,页面中正常显示的文字顺序应该是:
图 6-27 设定多个 x 值的 svg 图 6-27 中文字顺序与我们猜测的顺序是一样的,这说明 SVG 中每个字符都可以有自己的 x 轴坐标值。y 与 x 同理,每个字符都可以有自己的 y 轴坐标值。虽然我们只设定了 6 个位置参数, svg 中的字符却有 11 个,但没有设定位置参数的字符依然能够按照原文顺序排序。在了解 SVG 基本知识之后,我们回头看一下案例中所使用的 SVG 文件中坐标参数的设定,图 6-23 中的字符与图 6-24 图片页源代码中的字符一一对应,且每个字符都设定了 x 轴的位置参数,而 y 轴则只有 1 个值。 在了解位置参数之后,我们还需要弄清楚字符定位的问题。浏览器根据 CSS 样式中设定的坐标和元素宽高来确定 SVG 中对应数字。x 轴的正方向为从左到右,y 轴的正方向是从上到下,如图 6-28 所示。
图 6-28 SVG x 轴和 y 轴与位置参数的关系 而 CSS 样式中的 x 轴与 y 轴是相反的,也就是说 CSS 样式中 x 轴是负数向右的,y 轴是负数向下的,如图 6-29 所示。
图 6-29 CSS x 轴和 y 轴与位置参数的关系 所以当我们需要在 CSS 中定位 SVG 中的字符位置时,需要用负数表示。我们可以通过一个例子来理解它们的关系,现在需要在 CSS 中定位图 6-30 中第 1 行的第 1 个字符的中心点。
图 6-30 SVG 假设字符大小为 14 px,那么 SVG 的计算规则如下。
图 6-31 映射结果 然后再利用切片特性拿到字符。对应代码如下:
这时候,只要我们点击最前面的复选框,验证码算法会首先利用其「风险分析引擎」做一次安全检测,如果直接检验通过的话,我们会直接得到如下的结果:
如果算法检测到当前系统存在风险,比如可能是陌生的网络环境,可能是模拟程序,会需要做二次校验。它会进一步弹出类似如下的内容:
比如上面这张图,验证码页面会出现九张图片,同时最上方出现文字「树木」,我们需要点选下方九张图中出现「树木」的图片,点选完成之后,可能还会出现几张新的图片,我们需要再次完成点选,最后点击「验证」按钮即可完成验证。 或者我们可以点击下方的「耳机」图标,这时候会切换到听写模式,验证码会变成这样:
这时候我们如果能填写对验证码读的音频内容,同样可以通过验证。 这两种方式都可以通过验证,验证完成之后,我们才能完成表单的提交,比如完成登录、注册等操作。 这种验证码叫什么名字? 这个验证码就是 Google 的 reCAPTCHA V2 验证码,它就属于行为验证码的一种,这些行为包括点选复选框、选择对应图片、语音听写等内容,只有将这些行为校验通过,此验证码才能通过验证。相比于一般的图形验证码来说,此种验证码交互体验更好、安全性会更高、破解难度更大。 许多国外的网站都采用了此种验证码,由于某些原因,在国内其实无法直接使用,但只需要将验证码的域名更换为 recaptcha.net 同样是可以使用的,所以有时候我们在国内某些站点同样能看到它的身影。 其实上文所介绍的验证码仅仅是 reCAPTCHA 验证码的一种形式,是 V2 的显式版本,另外其 V2 版本还有隐式版本,隐式版本在校验的时候不会再显式地出现验证页面,它是通过 JavaScript 将验证码和提交按钮进行绑定,在提交表单的时候会自动完成校验。除了 V2 版本,Google 又推出了最新的 V3 版本,reCAPTCHA V3 验证码会为根据用户的行为来计算一个分数,这个分数代表了用户可能为机器人的概率,最后通过概率来判断校验是否可以通过。其安全性更高、体验更好。 具体的内容大家可以参考 reCAPTCHA 的官方介绍:
破解验证码背后有图像识别算法和大量人力的支撑,如果我们仅仅是简单的图形验证码,其可以通过一些图像识别算法将内容识别出来转化为文本内容。如果是较为复杂的图形验证码或者像 reCAPTCHA 类似的行为验证码,其背后会有人来对验证码进行模拟,然后返回其验证成功后的秘钥,我们利用其结果便可以完成一些验证码的绕过。 当然这种网站肯定是要收费的,按照 1000 次识别为单位,其花费的费用为 0.5 美刀到 2.99 美刀不等,比如非常简单的图形验证码可能就是 0.5 美刀,这种验证码对于其人力和算力资源消耗都是相对较小的,对于复杂的 reCAPTCHA 验证码,就要花 2.99 美刀了,因为识别这么一个验证码并不容易,其背后的人可能需要看好多图,点选好多次才能完成一次成功的验证。 目前我用的服务的收费标准是这样的:
具体的内容或者更新大家可以到其官方说明
在这里我们可以看到账户余额、API KEY、FAQ 等配置。 这里最重要的就是 API KEY 了,它是我们用来使用 2Captcha 的凭证,我们将它复制下来,后面我们会在代码中使用它。
好,准备工作完成了,我们接下来进入正式内容。
在这里可以看到有一个表单,上面有一些输入框,下方是 reCAPTCHA V2 验证码。 要识别这个验证码,第一步便是找到这个验证码 sitekey,这个是验证码的唯一标识。 我们打开浏览器的开发者工具,查看其页面源码,首先找到 reCAPTCHA 的源代码,如下图所示:
可以看到 reCAPTCHA 是对应了一个 iframe,我们看到的 reCAPTCHA 内容都是在 iframe 里面呈现出来的。 这里我们可以观察到在 reCAPTCHA 的源码的最外层的 div 上面有一个字段,叫做 data-sitekey,这就是刚才我们所说的 sitekey,它是验证码的唯一标识,比如这里我先将这个 sitekey 保存下来,这里其值为:
所以,2Captcha 相当于为我们模拟了点选验证码的过程,其最终得到的这个 token 其实就是我们应该赋值给 name 为 g-recaptcha-response 的内容。 那么怎么赋值呢? 很简单,用 JavaScript 就好了。我们可以用 JavaScript 选取到这个 textarea,然后直接赋值即可,代码如下:
可以看到其就是提交了一个表单,其中有一个字段就是 g-recaptcha-response,它会发送到服务端进行校验,校验通过,那就成功了。 所以,如果我们借助于 2Captcha 得到了这个 token,然后把它赋值到表单的 textarea 里面,表单就会提交,如果 token 有效,就能成功绕过登录,而不需要我们再去点选验证码了。 最后我们得到如下成功的页面:
至此,我们就成功地借助 2Captcha 来完成了 reCAPTCHA V2 验证码的识别。
图 6-32 示例 7 页面 在编写代码之前,我们需要确定目标数据的元素定位。定位时,我们在 HTML 中发现了一些奇怪的符号,HTML 代码如下:
图 6-33 字符与数字的映射关系 字符与数字是一一对应的,我们只需要多找一些页面,将 0 ~ 9 数字对应的字符凑齐即可。但如果目标网站的字体是动态变化的呢?映射关系也是变化的呢? 根据 6.3 节的学习和分析,我们知道人为映射并不能解决这些问题,必须找到映射关系的规律,并使用 Python 代码实现映射算法才行。继续往下分析,难道字符映射是先异步加载数据再使用 JavaScript 渲染的?
图 6-34 请求记录 网络请求记录如图 6-34 所示,请求记录中并没有发现异步请求,这个猜测并没有得到证实。CSS 样式方面有没有线索呢?页面中包裹符号的标签的 class 属性值都是 stonefont:
图 6-35 百度字体编辑器界面 打开页面后,将 movie.woff 文件拖曳到百度字体编辑器的灰色区域即可,字体文件内容如图 6-36 所示。
图 6-36 字体文件 movie.woff 预览 该字体文件中共有 12 个字体块,其中包括 2 个空白字体块和 0 ~ 9 的数字字体块。我们可以大胆地猜测,评分数据和票房数据中使用的数字正是从此而来。 由此看来,我们还需要了解一些字体文件格式相关的知识,在了解文件格式和规律后,才能够找到更合理的解决办法。
图 6-37 字形与点的关系 字体文件中不仅包含字形数据和点信息,还包括字符到字形映射、字体标题、命名和水平指标等,这些信息存在对应的表中,所以我们也可以认为 TrueType 字体文件由一系列的表组成,其中常用的表 及其作用如图 6-38 所示。
图 6-38 构成字体文件的常用表及其作用 如何查看这些表的结构和所包含的信息呢?我们可以借助第三方 Python 库 fonttools 将 WOFF 等字体文件转换成 XML 文件,这样就能查看字体文件的结构和表信息了。首先我们要安装 fonttools 库, 安装命令为:
图 6-39 字符到字形映射关系示例 XML 中的字符 0xe339 与网页源代码中的字符 对应,这样我们就确定了 HTML 中的字符码与 movie.woff 字体文件中对应的字形关系。字形数据存储在 glyf 表中,每个字形的数据都是独立的,例如字形 uniE339 的字形数据如下:
图 6-40 字形 uniE339 的轮廓 我们可以在百度字体编辑器中调整点的位置,然后保存字体文件并将新字体文件转换为 XML 格式,相同名称的字形数据如下:
图 6-41 字形数据对比 XML 文件中记录的是字形坐标信息,实际上,我们没有办法直接通过字形数据获得文字,只能从其他方面想办法。虽然目标网站使用多套字体,但相同文字的字形也是相同的。比如现在有 movie.woff 和 food.woff 这两套字体,它们包含的字形如下:
图 6-42 描述相同文字的两个字形 点坐标的数量和坐标值可以作为比较条件吗? 如图 6-43 所示,两个不同文字的字形数据是不一样的。虽然这两种字形的 name 都是 uniE9C7,但是字形数据中大部分 pt 标签 x 和 y 的差距都很大,所以我们可以判定这两个字形描述的并不是 同一个文字。你可能会想到点的数量也可以作为排除条件,也就是说如果点的数量不相同,那么这个 两个字形描述的就不是同一个文字。真的是这样吗?
图 6-43 描述不同文字的字形数据对比 在图 6-44 中,左侧描述文字 7 的字形有 17 个点,而右侧描述文字 7 的字形却有 20 个点。对应的字形信息如图 6-45 所示。
图 6-44 描述相同文字的字形
图 6-45 描述相同文字的字形信息 虽然点的数量不一样,但是它们的字形并没有太大的变化,也不会造成用户误读,所以点的数量并不能作为排除不同字形的条件。因此,只有起止坐标和点坐标数据完全相同的字形,描述的才是相同字符。
界面类似显示如下图所示。
在这里显示了我们已经有的一些 Tampermonkey 脚本,包括我们自行创建的,也包括从第三方网站下载安装的。 另外这里也提供了编辑、调试、删除等管理功能,我们可以方便地对脚本进行管理。 接下来我们来创建一个新的脚本来试试,点击左侧的「+」号,会显示如图所示的页面。
初始化的代码如下:
我们随便输入用户名密码,点击登录按钮,观察一下网络请求的变化。 可以看到如下结果:
看到实际上控制台提交了一个 POST 请求,内容为:
这么多混淆代码,总不能一点点扒着看吧?这得找到猴年马月?那么遇到这种情形,这怎么去找 token 的生成位置呢? 解决方法其实有两种。
这时候如果我们再次点击登录按钮的时候,正好发起一次 Ajax 请求,就进入到断点了,然后再看堆栈信息就可以一步步找到编码的入口了。 点击 Submit 之后,页面就进入了 Debug 状态停下来了,结果如下:
一步步找,我们最后其实可以找到入口其实是在 onSubmit 方法这里。但实际上,我们观察到,这里的断点的栈顶还会包括了一些 async Promise 等无关的内容,而我们真正想找的是用户名和密码经过处理,再进行 Base64 编码的地方,这些请求的调用实际上和我们找寻的入口是没有很大的关系的。 另外,如果我们想找的入口位置并不伴随这一次 Ajax 请求,这个方法就没法用了。 这个方法是奏效的,但是我们先不讲 onSubmit 方法里面究竟是什么逻辑,下一个方法再来讲。
接下来,打开控制台,切换到 Sources 面板,这时候我们可以看到站点下面的资源多了一个叫做 Tampermonkey 的目录,展开之后,发现就是我们刚刚自定义的脚本。
然后输入用户名、密码,点击提交。发现成功进入了断点模式停下来了,代码就卡在了我们自定义的
成功 Hook 住了,这说明 JavaScript 代码在执行过程中调用到了 btoa 方法。 看下控制台,如下图所示。
这里也输出了 window 对象和 btoa 方法,验证正确。 这样,我们就顺利找到了 Base64 编码操作这个路口,然后看看堆栈信息,也已经不会出现 async、Promise 这样的调用,很清晰地呈现了 btoa 方法逐层调用的过程,非常清晰明了了,如图所示。
各个底层的 encode 方法略过,这样我们也非常顺利地找到了 onSubmit 方法里面的处理逻辑:
所以,现在我们知道了 token 和用户名、密码是什么关系了吧。 这里一目了然了,就是对表单进行了 JSON 序列化,然后调用了 encode 也就是 btoa 方法,并赋值为了 token,入口顺利解开。后面,我们只需要模拟这个过程就 OK 了。 所以,我们通过 Tampermonkey 自定义 JavaScript 脚本的方式实现了某个方法调用的 Hook,使得我们快速能定位到加密入口的位置,非常方便。 以后如果观察出来了一些门道,可以多使用这种方法来尝试,如 Hook encode 方法、decode 方法、stringify 方法、log 方法、alert 方法等等,简单而又高效。 以上便是通过 Tampermonkey 实现简单 Hook 的基础操作,当然这个仅仅是一个常见的基础案例,不过从中我们也可以总结出一些 Hook 的基本门道。 后面我们会继续介绍更多相关内容。
图 15-6 主机列表 另外刚才我们提到在 gerapy 目录下有一个空的 projects 文件夹,这就是存放 Scrapy 目录的文件夹,如果我们想要部署某个 Scrapy 项目,只需要将该项目文件放到 projects 文件夹下即可。 比如这里我放了两个 Scrapy 项目,如图 15-7 所示:
图 15-7 项目目录 这时重新回到 Gerapy 管理界面,点击项目管理,即可看到当前项目列表,如图 15-8 所示:
图 15-8 项目列表 由于此处我有过打包和部署记录,在这里分别予以显示。 Gerapy 提供了项目在线编辑功能,我们可以点击编辑即可可视化地对项目进行编辑,如图 15-9 所示:
图 15-9 可视化编辑 如果项目没有问题,可以点击部署进行打包和部署,部署之前需要打包项目,打包时可以指定版本描述,如图 15-10 所示:
图 15-10 项目打包 打包完成之后可以直接点击部署按钮即可将打包好的 Scrapy 项目部署到对应的云主机上,同时也可以批量部署,如图 15-11 所示:
图 15-11 部署页面 部署完毕之后就可以回到主机管理页面进行任务调度了,点击调度即可查看进入任务管理页面,可以当前主机所有任务的运行状态,如图 15-12 所示:
图 15-12 任务运行状态 我们可以通过点击新任务、停止等按钮来实现任务的启动和停止等操作,同时也可以通过展开任务条目查看日志详情,如图 15-13 所示:
图 15-13 查看日志 这样我们就可以实时查看到各个任务运行状态了。 以上便是 Gerapy 的一些功能的简单介绍,使用它我们可以更加方便地管理、部署和监控 Scrapy 项目,尤其是对分布式爬虫来说。 更多的信息可以查看 Gerapy 的 GitHub 地址:
图 15-3 制作镜像 然后输入镜像的一些配置信息,如图 15-4 所示。
图 15-4 镜像配置 最后确认制作镜像即可,稍等片刻即可制作成功。 接下来我们可以创建新的主机,在新建主机时选择已经制作好的镜像即可,如图 15-5 所示。
图 15-5 新建主机 后续配置过程按照提示进行即可。 配置完成之后登录新到云主机,即可看到当前主机 Docker 和 Scrapyd 镜像都已经安装好,Scrapyd 服务已经正常运行。 我们就通过自定义镜像的方式实现了相同环境的云主机的批量部署。