首页 > PHP > 读:我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言

读:我用爬虫一天时间“偷了”知乎一百万用户,只为证明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

随便收藏,请保留本文地址!

标签:采集 爬虫 数据处理 截取

相关文章

评论已关闭