读:我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言
在互联网上流传着这么一篇文章《我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言》,不知道知乎的网站管理人员怎么想的,万一给他的领导知道了……
看了他的源码,发现worker.php不就是简化版的workerman框架么,派生出子进程用于采集。
然后我在他的源码包里找到了phpQuery.php,可是没找到调用的地方,倒是在cls_query.php和user.php找到了一大堆正则表达式,我只能说人才啊,放着这么好的工具不用,非要自己用正则实现。
最厉害的是rolling_curl.php中的实现方法,功能全,支持多线程请求。
数据库方面使用了mysql和redis缓存,一天能扒这么多用户说明性能上不是问题。
感慨:
站在知乎的角度:互联网其实充满了机器人,只要有网页,都是公开的,即使是登陆后才能查看,也会暴露给登陆过的机器人。最简单的办法就是抗DDOS自动封IP,cdn厂商都提供这个功能,另外nginx有限速和屏蔽IP的模块,虽然这些办法无法封锁采集,但也能拖慢采集速度以减小损失。
站在旁观者的角度:代码很厉害啊,几个类收下了,有时间可以上上知乎,很良心的网站,不要拿我们的用户数据做坏事。采集开慢点,不能采集太快,这样影响别人访问网站的速度,采集过度就是攻击行为,被封IP也是活该!
给采集者的一点建议:phpQuery.php真心不错,比自己写的正则表达式要容易太多了,万一采集的网页格式发生变化了改起来很麻烦,正则表达式的成本太高。
刚学PHP的时候用的是火车头采集软件,通过界面设置可以获取到一堆采集的数据,处理起来并不轻松。自从认识phpQuery,再也不用火车头了,缺点是占用内存有点高,用完释放内存会好很多。现在作为一名雇员,不得不从别人的网站采集数据,用到phpQuery的地方太多了。
PS:虽然phpQuery很方便也很强大,但是有些情况下不能正确解析某个页面,我猜那篇偷知乎数据的作者可能是遇到了这样的情况,后来我又掌握了一门新的框架《phpQuery和simple_html_dom DOM解析器对比》,操作是复杂了些但是对页面的兼容性要强于phpQuery。再后来,使用nodejs来分析页面更方便了些,毕竟nodejs才是真正的爬虫王者。
另外,这里贴3个函数代码,可以方便处理采集来的数据。
第1个是str_replace的单次替换版本,PHP自带的str_replace只能全部替换:
//仅替换一次 function str_replace_once($needle, $replace, $haystack) { // Looks for the first occurence of $needle in $haystack // and replaces it with $replace. $pos = strpos($haystack, $needle); if ($pos === false) { return $haystack; } return substr_replace($haystack, $replace, $pos, strlen($needle)); }
第2个是截取函数,在火车头采集软件中有类似的功能,可以去掉左侧字符和右侧字符,如果找不到左侧和右侧字符则默认不截取。
/** * 用于字符串截取,用于截取2个字符串之间的内容,不含边界 * @param string $haystack * @param string $left * @param string $right * @return string */ function subByString($haystack, $left = '', $right = '') { $left_pos = false; $right_pos = false; if($left == '' || ($left_pos = strpos($haystack, $left)) === false) { $start_pos = 0; }else{ $start_pos = $left_pos; } $right_data = substr($haystack, $start_pos + ($left_pos === false ? 0 : strlen($left))); if($right == '' || ($right_pos = strpos($right_data, $right)) === false) { return substr($haystack, $left_pos === false ? 0 : $left_pos+strlen($left)); }else{ $end_pos = $start_pos + $right_pos; return substr($haystack, $left_pos === false ? 0 : $left_pos+strlen($left), $end_pos-$start_pos); } }
第3个是逆向截取函数,依赖上面的subByString,用于逆向截取,实现更变态的功能:
//逆向截取 function get_middle_reverse($haystack, $left = '', $right = '') { $revhtml = strrev($haystack); $revsuffix = strrev($right); $revpreffix = strrev($left); $middle = subByString($revhtml, $revsuffix, $revpreffix); $result = strrev($middle); return $result; }
subByString是一个非常赞的截取函数,只需要传入完整的字符串,左侧字符串(如果没查找到则置为空),右侧字符串(如果没查找到则置为空)进行截取,适合任意编码的字符串(前提是你传入的字符串参数编码一致)。例如:
<?php header('Content-type:text/html;charset=utf-8'); $str = '我是“ABC”长度为3个letter'; //正常截取,左侧和右侧均能查找到 print_r('<pre>'); print_r(subByString($str, '“ABC”长', '3个l')); print_r('</pre>'); print_r('<pre>'); //非正常截取,不传后2个参数则原字符串返回 print_r(subByString($str)); print_r('<pre>'); print_r('</pre>'); //只传入左侧字符,右侧全部返回 print_r(subByString($str, '“ABC”长')); print_r('<pre>'); print_r('</pre>'); //只传入右侧字符,左侧全部返回 print_r(subByString($str, '', '3个l')); print_r('</pre>');
get_middle_reverse是一个更赞的截取函数,可以实现逆向截取。例如:
<?php header('Content-type:text/html;charset=utf-8'); $str = '我是“ABC”长度为3个letter'; print_r('<pre>'); //截取字母l和t之间的字符e,第三个参数定位的是靠左侧的t print_r(subByString($str, 'l', 't')); print_r('</pre>'); print_r('<pre>'); //如果我们在截取过程中需要保证右侧的t是最后一个t则需要用到逆向截取 //打印出et,第三个参数定位的是最右侧的t print_r(get_middle_reverse($str, 'l', 't')); print_r('</pre>');
逆向截取在网页采集中非常管用,因为你需要的数据可能在标识字符串(网页中唯一字符串)的左侧,例如:
<?php header('Content-type:text/html;charset=utf-8'); //最后一页的超链接,如何才能获取30这个数字 $str = '<a href="?page=30">lastPage</a>'; //使用逆向截取,将标识字符串功能转移到第三个参数 print_r('<pre>'); //">lastPage</a>是仅有一次的标识字符串,而page=却多次出现 //打印出30 print_r(get_middle_reverse($str, 'page=', '">lastPage</a>')); print_r('</pre>');
注意:这里的逆向截取依赖strrev函数,我们知道strrev函数仅支持英文字符和英文标点,如果需要中文字符逆向截取请看后面的中文修复方案:
http://blog.zhengshuiguang.com/php/strrev-cn.html
文章链接:http://blog.zhengshuiguang.com/php/phpspider.html
随便收藏,请保留本文地址!
评论已关闭