百万级别知乎用户数据爬取与分析【永利澳门游戏网站】

永利澳门游戏网站 1

Linux总括文件数量

脚本跑了风姿洒脱段时间后,需求探视到底拿到了多少图片,当数据量很大的时候,张开文件夹查看图片数量就有一点慢。脚本是在Linux情形下运行的,由此可以动用Linux的下令来总结文件数量:

ls -l | grep "^-" | wc -l

其中,
ls -l 是长列表输出该目录下的文件信息(这里的文件可以是目录、链接、设备文件等); grep "^-" 过滤长列表输出信息, "^-" 只保留一般文件,如果只保留目录是 "^d" ; wc -l 是统计输出信息的行数。下面是一个运行示例:

永利澳门游戏网站 2

采纳Redis保存已经访谈过的顾客

抓取客户的经过中,发掘存点客商是生机勃勃度访谈过的,何况他的关切者和关爱了的客商都已获得过了,纵然在数据库的范畴做了再一次数据的管理,可是程序依然会利用curl发乞求,那样重复的出殡诉求就有大多种新的网络花销。还会有一个正是待抓取的客商供给这两天保留在八个地点以便下一遍实行,刚起头是放置数组里面,后来开采要在前后相继里添扩展进程,在多进程编制程序里,子进程会分享程序代码、函数库,但是经过使用的变量与其它进度所运用的通通不一致。差异进度之间的变量是分开的,不可能被别的进度读取,所以是不可能使用数组的。因而就悟出了应用Redis缓存来保存已经处理好的客商以致待抓取的客户。这样每一回实施完的时候都把顾客push到二个already_request_queue队列中,把待抓取的客户(即每一个顾客的关切者和尊崇了的顾客列表)push到request_永利澳门游戏网站,queue里面,然后每一次施行前都从request_queue里pop三个顾客,然后判定是或不是在already_request_queue里面,借使在,则展开下一个,不然就继续施行。

在PHP中使用redis示例:

<?php
    $redis = new Redis();
    $redis->connect('127.0.0.1', '6379');
    $redis->set('tmp', 'value');
    if ($redis->exists('tmp'))
    {
        echo $redis->get('tmp') . "n";
    }

使用curl_multi达成多线程抓取页面

刚开始单进度并且单个curl去抓取数据,速度超慢,挂机爬了叁个晚间只得抓到2W的数目,于是便想到能否在步向新的客商页面发curl央浼的时候一次性央求三个顾客,后来意识了curl_multi那几个好东西。curl_multi那类函数可以完成同时伸手多少个url,并不是二个个央求,那看似于linux系统中二个历程开多条线程施行的职能。上面是选择curl_multi完成二十四线程爬虫的演示:

    $mh = curl_multi_init(); //返回一个新cURL批处理句柄
    for ($i = 0; $i < $max_size; $i++)
    {
        $ch = curl_init();  //初始化单个cURL会话
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_URL, 'http://www.zhihu.com/people/' . $user_list[$i] . '/about');
        curl_setopt($ch, CURLOPT_COOKIE, self::$user_cookie);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.130 Safari/537.36');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        $requestMap[$i] = $ch;
        curl_multi_add_handle($mh, $ch);  //向curl批处理会话中添加单独的curl句柄
    }

    $user_arr = array();
    do {
                    //运行当前 cURL 句柄的子连接
        while (($cme = curl_multi_exec($mh, $active)) == CURLM_CALL_MULTI_PERFORM);

        if ($cme != CURLM_OK) {break;}
                    //获取当前解析的cURL的相关传输信息
        while ($done = curl_multi_info_read($mh))
        {
            $info = curl_getinfo($done['handle']);
            $tmp_result = curl_multi_getcontent($done['handle']);
            $error = curl_error($done['handle']);

            $user_arr[] = array_values(getUserInfo($tmp_result));

            //保证同时有$max_size个请求在处理
            if ($i < sizeof($user_list) && isset($user_list[$i]) && $i < count($user_list))
            {
                $ch = curl_init();
                curl_setopt($ch, CURLOPT_HEADER, 0);
                curl_setopt($ch, CURLOPT_URL, 'http://www.zhihu.com/people/' . $user_list[$i] . '/about');
                curl_setopt($ch, CURLOPT_COOKIE, self::$user_cookie);
                curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.130 Safari/537.36');
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
                $requestMap[$i] = $ch;
                curl_multi_add_handle($mh, $ch);

                $i++;
            }

            curl_multi_remove_handle($mh, $done['handle']);
        }

        if ($active)
            curl_multi_select($mh, 10);
    } while ($active);

    curl_multi_close($mh);
    return $user_arr;

HTTP 429 Too Many Requests

使用curl_multi函数能够同一时候发多少个供给,可是在进行进度中使同不平日候发200个须要的时候,开掘众多倡议不可能回来了,即发掘了丢包的情况。进一层解析,使用
curl_getinfo 函数打字与印刷每种央浼句柄消息,该函数重回四个饱含HTTP
response消息的涉及数组,当中有三个字段是http_code,表示央浼重回的HTTP状态码。见到有点不清个诉求的http_code都是429,那一个再次回到码的意味是出殡和安葬太多乞请了。笔者猜是腾讯网做了防爬虫的防护,于是我就拿任何的网址来做测量检验,开采三遍性发200个供给时没难题的,注解了自家的测度,搜狐在此上头做了防守,即一回性的哀求数量是有约束的。于是笔者不断地压缩须求数量,发以后5的时候就一贯不丢包情形了。表达在这里个程序里三遍性最八只好发5个央浼,固然非常少,但那也是叁遍小进步了。

插入MySQL时再次数据的管理

程序运维了生龙活虎段时间后,开掘成不知凡几顾客的数据是再一次的,由此供给在插入重复顾客数量的时候做拍卖。管理方案如下:

1)插入数据库早先检查数据是不是业已存在数据库;

2)增添独一索引,插入时行使 INSERT INTO ... ON DUPLICATE KEY UPDATE...

3)增添独一索引,插入时利用 INSERT INGNORE INTO...

4)增添唯一索引,插入时选择 REPLACE INTO...

先是种方案是最简易但也是作用最差的方案,因而不选拔。二和四方案的试行结果是同等的,区别的是,在遭逢相像的多少时,
INSERT INTO … ON DUPLICATE KEY UPDATE 是平素更新的,而 REPLACE INTO
是先删除旧的多寡然后插入新的,在这里个进程中,还供给再一次维护索引,所以速度慢。所以在二和四两者间接选举取了第三种方案。而第二种方案,
INSERT INGNORE
会忽视实施INSERT语句现身的不当,不会忽视语法难题,不过忽视主键存在的事态。那样一来,使用
INSERT INGNORE
就越来越好了。最后,思考到要在数据库中著录重复数据的条数,由此在程序中使用了第三种方案。

本次抓取了110万的顾客数据,数据剖析结果如下:

付出前的备选

安装Linux系统(Ubuntu14.04),在VMWare虚构机下安装三个Ubuntu;

设置PHP5.6或上述版本;

安装MySQL5.5或以上版本;

安装curl、pcntl扩展。

在Linux下查看系统的cpu消息

落到实处了多进度编制程序之后,就想着多开几条经过不断地抓取客商的数量,后来开了8调进度跑了一个晚间后发现只好获得20W的数码,未有多大的进级。于是查阅资料开采,依照系统优化的CPU质量调优,程序的最大进度数不可能随意给的,要基于CPU的核数和来给,最大进度数最棒是cpu核数的2倍。由此需求查阅cpu的消息来走访cpu的核数。在Linux下查看cpu的消息的命令:

cat /proc/cpuinfo

结果如下:

永利澳门游戏网站 3

其间,model name表示cpu类型音信,cpu
cores表示cpu核数。这里的核数是1,因为是在虚构机下运转,分配到的cpu核数少之甚少,由此只能开2条经过。最后的结果是,用了四个周末就抓取了110万的客商数据。

PHP总括脚本推行时间

因为想精晓各样进程花销的小运是微微,因而写个函数总括脚本实行时间:

function microtime_float()
{
     list($u_sec, $sec) = explode(' ', microtime());
     return (floatval($u_sec) + floatval($sec));
}

$start_time = microtime_float();

//do something
usleep(100);

$end_time = microtime_float();
$total_time = $end_time - $start_time;

$time_cost = sprintf("%.10f", $total_time);

echo "program cost total " . $time_cost . "sn";

若文中有不允许确的地点,望各位提议以便校勘。

代码托管地址:

使用PHP的curl扩充抓取页面数据

PHP的curl扩充是PHP援救的允许你与各类服务器使用各体系型的磋商实行三番五次和通讯的库。

本程序是抓取博客园的顾客数量,要能访谈客户个人页面,要求客户登入后的能力访谈。当大家在浏览器的页面中点击八个顾客头像链接步入顾客个人基本页面包车型客车时候,之所以能够看见顾客的音信,是因为在点击链接的时候,浏览器帮您将地面包车型客车cookie带上一同提交到新的页面,所以你就能够进来到客户的私人民居房基本页面。因而达成访谈个人页面在此之前须要先获得客户的cookie新闻,然后在历次curl诉求的时候带上cookie新闻。在拿到cookie音讯方面,笔者是用了协调的cookie,在页面中得以看来自个儿的cookie音讯:

永利澳门游戏网站 4

三个个地复制,以”__utma=?;__utmb=?;”那样的花样组成叁个cookie字符串。接下来就能够利用该cookie字符串来发送央浼。

发端的以身作则:

$url = 'http://www.zhihu.com/people/mora-hu/about'; //此处mora-hu代表用户ID
$ch = curl_init($url); //初始化会话
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_COOKIE, $this->config_arr['user_cookie']);  //设置请求COOKIE
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  //将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);  
$result = curl_exec($ch);
return $result;  //抓取的结果

运维方面包车型地铁代码能够得到mora-hu客户的私家基本页面。利用该结果再采纳正则表明式对页面举行管理,就能够博拿到姓名,性别等所须要抓取的新闻。

多进程编制程序中Redis和MySQL连接难点

在多进度条件下,程序运营了黄金年代段时间后,开采数目不能够插入到数据库,会报mysql
too many connections的荒诞,redis也是那样。

下边这段代码会进行破产:

<?php
     for ($i = 0; $i < 10; $i++) {
          $pid = pcntl_fork();
          if ($pid == -1) {
               echo "Could not fork!n";
               exit(1);
          }
          if (!$pid) {
               $redis = PRedis::getInstance();
               // do something     
               exit;
          }
     }

根本原因是在各种子进度成立时,就已经三回九转了父进度后生可畏份完全同样的正片。对象能够拷贝,但是已开立的接连无法被拷贝成几个,由此发生的结果,正是逐意气风发进度都应用同二个redis连接,各干各的事,最后发生莫明其妙的冲突。

杀鸡取蛋办法:
>程序无法完全保障在fork进度在此以前,父进程不会创制redis连接实例。因而,要缓慢解决这一个主题素材只可以靠子进程本人了。试想一下,倘若在子进度中获得的实例只与当前经过有关,那么那一个难题就不设有了。于是施工方案正是某些校订一下redis类实例化的静态格局,与近日进程ID绑定起来。

改换后的代码如下:

<?php
     public static function getInstance() {
          static $instances = array();
          $key = getmypid();//获取当前进程ID
          if ($empty($instances[$key])) {
               $inctances[$key] = new self();
          }

          return $instances[$key];
     }

图表防盗链

在对回到结果开展正则管理后输出个人新闻的时候,发现在页面中输出客户头像时不能够张开。经过查看资料获知,是因为天涯论坛对图片做了防盗链管理。技术方案就是呼吁图片的时候在伏乞头里冒充三个referer。

在运用正则表明式获取到图片的链接之后,再发三遍倡议,当时带上图片央浼的来源于,表明该哀告来自和讯网址的转折。具体育赛事举个例子下:

function getImg($url, $u_id)
{
    if (file_exists('./images/' . $u_id . ".jpg"))
    {
        return "images/$u_id" . '.jpg';
    }
    if (empty($url))
    {
        return '';
    }
    $context_options = array(  
        'http' =>  
        array(
            'header' => "Referer:http://www.zhihu.com"//带上referer参数
      )
  );

    $context = stream_context_create($context_options);  
    $img = file_get_contents('http:' . $url, FALSE, $context);
    file_put_contents('./images/' . $u_id . ".jpg", $img);
    return "images/$u_id" . '.jpg';
}

爬取越多客商

抓取了友好的个人消息后,就须要再探问客户的关切者和爱惜了的客户列表获取越来越多的顾客消息。然后大器晚成层黄金时代层地访谈。能够观看,在个人基本页面里,有七个链接如下:

永利澳门游戏网站 5

此间有三个链接,三个是关爱了,另一个是关怀者,以“关心了”的链接为例。用正则相配去相配到对应的链接,获得url之后用curl带上cookie再发二回号召。抓取到客商关心了的用来列表页之后,能够赢得上边的页面:

永利澳门游戏网站 6

剖判页面包车型地铁html布局,因为若是得到顾客的音信,所以只须求框住的这一块的div内容,顾客名都在那中间。能够看看,客商关注了的页面包车型大巴url是:

永利澳门游戏网站 7

昔不这段时间的顾客的那些url大致是相似的,分化之处就在于顾客名那里。用正则相称拿到客户名列表,二个多少个地拼url,然后再每种发央求(当然,二个多少个是异常慢的,上面有施工方案,这一个稍后会聊起)。步入到新客商的页面之后,再另行上边的步骤,就疑似此持续循环,直达到到你所要的数据量。

行使PHP的pcntl扩大达成多进度

改用了curl_multi函数完成四线程抓取客商音信之后,程序运维了几个晚间,最终收获的数目有10W。还不能达到和睦的优越对象,于是便继续优化,后来发觉php里面有三个pcntl扩充能够完毕多进程编程。上边是多编制程序编制程序的演示:

//PHP多进程demo
//fork10个进程
for ($i = 0; $i < 10; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        echo "Could not fork!n";
        exit(1);
    }
    if (!$pid) {
        echo "child process $i runningn";
        //子进程执行完毕之后就退出,以免继续fork出新的子进程
        exit($i);
    }
}

//等待子进程执行完毕,避免出现僵尸进程
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child $status completedn";
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注