PID控制器的应用:控制网络爬虫抓取速度
一、初识PID控制器
冬天乡下人喜欢烤火取暖,常见的情形就是四人围着麻将桌,桌底放一盆碳火。有人觉得火不够大,那加点木炭吧,还不够,再加点。片刻之后,又觉得火太大,脚都快被烤熟了,那就取出一些木碳……直到火盆里的火刚刚合适。这样一个看似简单的情形中就包括PID控制系统的四个主要过程:设定目标,测量,比较和执行。结合PID控制器这四个过程来重新阐述上述情形是这样的:人烤火时所期待的最合适的温度是我们设定的目标;每个人都是一个传感器,能感知到温度的大小(虽然只是模糊的感知)——这是一个测量的过程;我们感知到的温度是不是最适合我们,是大还是小——这是一个比较的过程;比较之后,我们知道火是大还是小了,小了就加点木炭,大了就取出部分木炭——这是一个执行的过程。不断重复最后三个过程(如图1所示),直至使人能感觉到温度比较合适。
图1. PID控制过程循环
二、控制网络爬虫速度
为什么要控制爬虫速度,难道像高铁一样快不好吗?其中有两方面原因:一、对于目标网站来说是一种礼貌,如果速度太快,将给服务器造成过大的负载;二、对于本人来说,抓取速度太快将导致致服务器禁止访问,从而丢失大量有效数据,甚至需要重新抓取。那么如何控制爬虫抓取网页的速度,才能使它不至于太快呢?
通常情况下,在每个页面抓取之间设定较大的延时等待以限制最大访问频率,可以保证既不会给服务器造成过重负担,也不会因访问太频繁而被服务器禁止,然而这样的方法将导致网络利用率低,抓取速度慢,对于大量网页的抓取任务来说,往往是无法忍受的。
图2. 网络流畅和网络较差时网页抓取时间对比
图2是一个简化的理想模型,可以很好的说明这个问题,假设某网站允许的最大访问频率为6页/分钟,于是最小时间间隔为10s,这个时间要通过很多次重复试验来确定(网站管理员肯定不会告诉你的)。网络流畅时每个网页读取时间为0.5s,为了保证不被服务器屏蔽,至少要等待9.5s的间隔时间才继续抓取下一个页面,这个9.5s的间隔时间是固定的,即使网络较差时也要等待这么长的时间。网络较差时,网页读取时间为9.5s,再延时等待9.5s,于是每个网页的读取时间变为19s,几乎是网络流畅时的两倍。而实际上网络较差时的理想情况下,只需要等待0.5s,这时便保持和网络流畅时相同的抓取速度,由此可见这种限制最大速度的方法是很低效的。另外延时等待时间对抓取频率的影响是很模糊的,延时1s时抓取频率是100页/分钟,那么延时10s就是10页/分钟吗?很难确定,尤其是在复杂的网络环境中。
要改进上述方法,一种很自然的解决方案就是:使等待时间动态变化,即等于最小时间间隔减去网页读取时间,这样就能保证网页平均抓取时间在网络流畅和网络较差时都为最小时间间隔。这种方法对于单线程的爬虫访问小规模网站来说也许可行,但在多线程分布式的爬虫访问大规模网站时,整体上的抓取时间由许多并行的抓取任务共同决定,并且各种异常情况(页面无效或者连接超时)使得抓取时间更加不可计算,这种方法就显得相当笨拙了。综合考虑各种因素,显然我们需要一种模糊的,不需要精确计算的方法来控制爬虫抓取速度,而且这个速度是很直观的以频率(页/分钟)来表示——PID控制算法就是其中一种。
三、PID控制爬虫速度
PID控制器控制爬虫速度的原理简单来说就是:速度快了,增加延时时间;速度慢了,减小延时时间——是否已经看到了其中与本文开始所描述的烤火情形之间的相似性呢?类比烤火的情形,设想PID控制爬虫速度的方案是:
1)初始化:设定初始延时时间T0和比例系数Kp(典型值-0.05);
2)目标设定:设定爬虫速度S,比如40页/分钟;
3)测量:统计每分钟内爬虫抓取网页的数量n,可能是32,也可能是100;
4)比较:比较n和S的大小;
5)执行:n如果比S大,说明太快,于是增加延时;n如果比S小,说明太慢,于是减小延时。
该方案的公式化表示如下:
Tk=Tk-1+Kp*(S-n) (3.1)
其中k=1, 2, 3 ... ,Tk是第k次设定的延时时间。
不要被表达式吓住了,它所表达的意思其实就是步骤5)所描述的执行过程:速度太快 (S-n小于0,则Kp*(S-n)为正),增加延时 (Tk大于Tk-1);速度太慢 (S-n大于0, Kp*(S-n)为负),减小延时 (Tk小于Tk-1)。
假设初始延时时间T0为1.0s, 比例系数Kp为-0.05,爬虫速度S设置为40页/分钟。如果某次爬虫抓取网页的数量n=100,根据3.1式计算的延时值T1=T0+Kp*(S-n)=1.0+(-0.05)*(40-100)=4.0;下一次可能的测量值n=30,则计算的延时值T2=4.0-0.05*10=3.5。
图3. PID控制曲线
图3是某次实验中PID控制曲线,下面的数据是这次实验的详细记录,第一列([2012/5/21 1:31:00])是当前时间,第二列(80)是前一分钟内爬虫抓取网页的数量n,第三列(3799)是根据3.1式(实际计算中加入了一个积分项,稍后详细说明)计算出来的延时时间(单位为ms),爬虫速度最终稳定在40页/分钟(允许一定的波动)。
[2012/5/21 1:31:00] 80 3799
[2012/5/21 1:32:00] 32 4039
[2012/5/21 1:33:00] 30 3980
[2012/5/21 1:34:00] 30 3720
[2012/5/21 1:35:00] 32 3400
[2012/5/21 1:36:00] 36 3200
[2012/5/21 1:37:00] 36 2920
[2012/5/21 1:38:00] 42 2980
[2012/5/21 1:39:00] 40 2980
[2012/5/21 1:40:00] 40 2980
[2012/5/21 1:41:00] 40 2980
[2012/5/21 1:42:00] 40 2980
[2012/5/21 1:43:00] 40 2980
[2012/5/21 1:44:00] 40 2980
[2012/5/21 1:45:00] 40 2980
[2012/5/21 1:46:00] 40 2980
[2012/5/21 1:47:00] 40 2980
[2012/5/21 1:48:00] 40 2980
[2012/5/21 1:49:00] 39 2910
[2012/5/21 1:50:00] 40 2910
[2012/5/21 1:51:00] 41 2980
[2012/5/21 1:52:00] 39 2930
[2012/5/21 1:53:00] 41 3000
[2012/5/21 1:54:00] 40 3000
[2012/5/21 1:55:00] 39 2930
有了PID控制器,在网络环境允许的范围内,想让爬虫每分钟抓几个网页它就抓几个,是不是很方便?
四、完整的PID控制器
式3.1是一个简化版本的PID控制器,只用到了比例项(P),加入积分项(I)和微分项(D)后才是一个完整的PID控制器。PID是三个英文单词的缩写,分别代表比例单元(Proportion),积分单元(Integral)和微分单元(Differential),每个单元分别对应着一个乘积因子Kp,Ki,Kd。加入积分单元和微分单元后,式3.1扩展为式4.1,其中Sum是误差项(S-n)的积分(其实就是所有误差值的和),Diff是误差项的微分(在单位时间内就是前后两次误差项的差值)。
Tk=Tk-1+Kp*(S-n)+Ki*Sum+Kd*Diff (4.1)
乘积因子Kp、Ki、Kd的设定与具体的应用场景相关,并且需要通过实验来确定其最佳值。若令Ki=0, Kd=0,那么式4.1退化成式3.1。
五、总结
第三节中的实验数据是在Kp=-0.05、Ki=-0.01、Kd=0的条件下获取,其中有一些应用细节上的处理,比如限制最大最小延时时间和限制误差积分项的最大值等。若需要更深入的了解PID控制器,比如PID算法收敛性和鲁棒性的讨论等,请查阅相关资料,本文更加关注的是如何应用PID控制器。
......................................................................................................................................................................................................
原文链接:http://www.cnblogs.com/easystep/archive/2012/05/21/2511468.html
参考:
PID控制器的应用:控制网络爬虫抓取速度的更多相关文章
- 如何利用Python网络爬虫抓取微信朋友圈的动态(上)
今天小编给大家分享一下如何利用Python网络爬虫抓取微信朋友圈的动态信息,实际上如果单独的去爬取朋友圈的话,难度会非常大,因为微信没有提供向网易云音乐这样的API接口,所以很容易找不到门.不过不要慌 ...
- 基于Thinkphp5+phpQuery 网络爬虫抓取数据接口,统一输出接口数据api
TP5_Splider 一个基于Thinkphp5+phpQuery 网络爬虫抓取数据接口 统一输出接口数据api.适合正在学习Vue,AngularJs框架学习 开发demo,需要接口并保证接口不跨 ...
- 利用Python网络爬虫抓取微信好友的签名及其可视化展示
前几天给大家分享了如何利用Python词云和wordart可视化工具对朋友圈数据进行可视化,利用Python网络爬虫抓取微信好友数量以及微信好友的男女比例,以及利用Python网络爬虫抓取微信好友的所 ...
- 利用Python网络爬虫抓取微信好友的所在省位和城市分布及其可视化
前几天给大家分享了如何利用Python网络爬虫抓取微信好友数量以及微信好友的男女比例,感兴趣的小伙伴可以点击链接进行查看.今天小编给大家介绍如何利用Python网络爬虫抓取微信好友的省位和城市,并且将 ...
- 如何利用Python网络爬虫抓取微信好友数量以及微信好友的男女比例
前几天给大家分享了利用Python网络爬虫抓取微信朋友圈的动态(上)和利用Python网络爬虫爬取微信朋友圈动态——附代码(下),并且对抓取到的数据进行了Python词云和wordart可视化,感兴趣 ...
- 使用selenium实现简单网络爬虫抓取MM图片
撸主听说有个网站叫他趣,里面有个社区,其中有一项叫他趣girl,撸主点进去看了下,还真不错啊,图文并茂,宅男们自己去看看就知道啦~ 接下来当然就是爬取这些妹子的图片啦,不仅仅是图片,撸主发现里面的对话 ...
- python网络爬虫抓取动态网页并将数据存入数据库MySQL
简述以下的代码是使用python实现的网络爬虫,抓取动态网页 http://hb.qq.com/baoliao/ .此网页中的最新.精华下面的内容是由JavaScript动态生成的.审查网页元素与网页 ...
- [Python学习] 简单网络爬虫抓取博客文章及思想介绍
前面一直强调Python运用到网络爬虫方面很有效,这篇文章也是结合学习的Python视频知识及我研究生数据挖掘方向的知识.从而简介下Python是怎样爬去网络数据的,文章知识很easy ...
- Python开发网络爬虫抓取某同城房价信息
前言: 苦逼的我从某某城市换到另一个稍微大点的某某城市,面临的第一个问题就是买房,奋斗10多年,又回到起点,废话就不多说了,看看如何设计程序把某同城上的房价数据抓取过来. 方案:方案思路很简单,先把网 ...
随机推荐
- javascript常用字符串函数和本地存储
concat将两个或多个字符的文本组合起来,返回一个新的字符串.var a = "hello";var b = ",world";var c = a.conca ...
- CentOS防火墙开启、关闭与开放指定端口
系统为centos 5.5,部署好Tomcat之后却发现输入114.80.*.*:8080(即ip:8080)却无法显示Tomcat默认的首页.由于以前部署在Win Server的VPS上,Linux ...
- [sh]函数+条件表达式
了解了下shell的函数和case语句: 函数格式: function(){ } 例子: function rsyncstart() { if [ "${status1}X" == ...
- 黑客编程教程(八)编写NT服务
先介绍一下什么是NT服务,实际上就是一个可以在系统启动时自动在一定身份下启动的,伴随着系统长期存在的进程. 一个NT服务有三部分构成: :Service Control Manager(SCM) 每个 ...
- laravel 拾遗 中间件
Problem You want to add middleware to your application but don't know where to begin. Solution C ...
- ORACLE 仿照原表建表语法
用于: 1.修改表前,可用于对原表表结构或表数据的备份 2.仿照原表的表结构建立一张新表 CREATE TABLE T_XXXX_BAK_130810 AS SELECT * FROM T_XXXX ...
- CTreeCtrl 父结点联动子结点CheckBox
实现很简单,直接上代码: void CCheckBoxTreeDlg::OnNMClickTree1(NMHDR *pNMHDR, LRESULT *pResult) { // TODO: 在此添加控 ...
- Windows server 2012公用网络修改为专用网络
普通环境路径如下: [控制面板]--[系统和安全]--[管理工具]--[本地安全策略]--[网络列表管理器策略]--[网络]--[网络位置],设定之后也可以设定一下[用户权限] 域控环境路径如下: 不 ...
- C#中实现Windows系统流氓监控程序
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- android中activity向service中传值
和activity中互相传值类似 在activity中 Intent regIntent = new Intent(this, ChatService.class); regIntent.putEx ...