爬虫进阶开发——之回调函数

回调函数是在爬虫爬取并处理网页的过程中设置的一些系统钩子, 通过这些钩子可以完成一些特殊的处理逻辑.

下图是PHP蜘蛛爬虫爬取并处理网页的流程图,矩形方框中标识了爬虫运行过程中所使用的重要回调函数:

爬虫进阶开发——之回调函数  - 图1

on_start($phpspider)

爬虫初始化时调用, 用来指定一些爬取前的操作

@param $phpspider 爬虫对象

在函数中可以调用requests::set_header($key,$value), requests::set_cookie($key,$value),requests::set_cookies($cookies)

举个栗子:

在爬虫开始爬取之前给所有待爬网页添加一个Header

  1. $spider->on_start = function($phpspider)
  2. {
  3. requests::set_header("Referer", "http://buluo.qq.com/p/index.html");
  4. };

on_status_code($status_code, $url, $content, $phpspider)

判断当前网页是否被反爬虫了, 需要开发者实现

@param $status_code 当前网页的请求返回的HTTP状态码@param $url 当前网页URL@param $content 当前网页内容@param $phpspider 爬虫对象@return $content 返回处理后的网页内容,不处理当前页面请返回false

举个栗子:

  1. $spider->on_status_code = function($status_code, $url, $content, $phpspider)
  2. {
  3. // 如果状态码为429,说明对方网站设置了不让同一个客户端同时请求太多次
  4. if ($status_code == '429')
  5. {
  6. // 将url插入待爬的队列中,等待再次爬取
  7. $phpspider->add_url($url);
  8. // 当前页先不处理了
  9. return false;
  10. }
  11. // 不拦截的状态码这里记得要返回,否则后面内容就都空了
  12. return $content;
  13. };

is_anti_spider($url, $content, $phpspider)

判断当前网页是否被反爬虫了, 需要开发者实现

@param $url 当前网页的url@param $content 当前网页内容@param $phpspider 爬虫对象@return 如果被反爬虫了, 返回true, 否则返回false

举个栗子:

  1. $spider->is_anti_spider = function($url, $content, $phpspider)
  2. {
  3. // $content中包含"404页面不存在"字符串
  4. if (strpos($content, "404页面不存在") !== false)
  5. {
  6. // 如果使用了代理IP,IP切换需要时间,这里可以添加到队列等下次换了IP再抓取
  7. // $phpspider->add_url($url);
  8. return true; // 告诉框架网页被反爬虫了,不要继续处理它
  9. }
  10. // 当前页面没有被反爬虫,可以继续处理
  11. return false;
  12. };

on_download_page($page, $phpspider)

在一个网页下载完成之后调用. 主要用来对下载的网页进行处理.

@param $page 当前下载的网页页面的对象@param $phpspider 爬虫对象@return 返回处理后的网页内容
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

举个栗子:比如下载了某个网页,希望向网页的body中添加html标签,处理过程如下:

  1. $spider->on_download_page = function($page, $phpspider)
  2. {
  3. $page_html = "<div id=\"comment-pages\"><span>5</span></div>";
  4. $index = strpos($page['row'], "</body>");
  5. $page['raw'] = substr($page['raw'], 0, $index) . $page_html . substr($page['raw'], $index);
  6. return $page;
  7. };

on_download_attached_page($content, $phpspider)

在一个网页下载完成之后调用. 主要用来对下载的网页进行处理.

@param $content 当前下载的网页内容@param $phpspider 爬虫对象@return 返回处理后的网页内容

举个栗子:比如下载的网页需去掉前后两边的中括号,并将处理后的数据返回,处理过程如下:

  1. $spider->on_download_attached_page = function($content, $phpspider)
  2. {
  3. $content = trim($content);
  4. $content = ltrim($content, "[");
  5. $content = rtrim($content, "]");
  6. $content = json_decode($content, true);
  7. return $content;
  8. };

on_fetch_url($url, $phpspider)

在一个网页获取到URL之后调用. 主要用来对获取到的URL进行处理.

@param $url 当前获取到的URL@param $phpspider 爬虫对象@return 返回处理后的URL,为false则此URL不入采集队列

栗子1:如果获取到URL包含#filter,不入URL采集队列

  1. $spider->on_fetch_url = function($url, $phpspider)
  2. {
  3. if (strpos($url, "#filter") !== false)
  4. {
  5. return false;
  6. }
  7. return $url;
  8. };

栗子2:把获取到URL的&替换成&,并将处理后的数据返回

  1. $spider->on_fetch_url = function($url, $phpspider)
  2. {
  3. $url = str_replace("&amp;", "&", $url);
  4. return $url;
  5. };

on_scan_page($page, $content, $phpspider)

在爬取到入口url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据(点此查看“url附加数据”实例解析).

@param $page 当前下载的网页页面的对象@param $content 当前网页内容@param $phpspider 当前爬虫对象@return 返回false表示不需要再从此网页中发现待爬url
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

此函数中通过调用$phpspider->add_url($url, $options)函数来添加新的url到待爬队列。

栗子1:实现这个回调函数并返回false,表示爬虫在处理这个scan_url的时候,不会从中提取待爬url

  1. $spider->on_scan_page = function($page, $content, $phpspider)
  2. {
  3. return false;
  4. };

栗子2:生成一个新的url添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url

  1. $spider->on_scan_page = function($page, $content, $phpspider)
  2. {
  3. $array = json_decode($page['raw'], true);
  4. foreach ($array as $v)
  5. {
  6. $lastid = $v['id'];
  7. // 生成一个新的url
  8. $url = $page['url'] . $lastid;
  9. // 将新的url插入待爬的队列中
  10. $phpspider->add_url($url);
  11. }
  12. // 通知爬虫不再从当前网页中发现待爬url
  13. return false;
  14. };

on_list_page($page, $content, $phpspider)

在爬取到入口url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据(点此查看“url附加数据”实例解析).

@param $page 当前下载的网页页面的对象@param $content 当前网页内容@param $phpspider 当前爬虫对象@return 返回false表示不需要再从此网页中发现待爬url
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

此函数中通过调用$phpspider->add_url($url, $options)函数来添加新的url到待爬队列。

栗子1:实现这个回调函数并返回false,表示爬虫在处理这个scan_url的时候,不会从中提取待爬url

  1. $spider->on_list_page = function($page, $content, $phpspider)
  2. {
  3. return false;
  4. };

栗子2:生成一个新的url添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url

  1. $spider->on_list_page = function($page, $content, $phpspider)
  2. {
  3. $array = json_decode($page['raw'], true);
  4. foreach ($array as $v)
  5. {
  6. $lastid = $v['id'];
  7. // 生成一个新的url
  8. $url = $page['url'] . $lastid;
  9. // 将新的url插入待爬的队列中
  10. $phpspider->add_url($url);
  11. }
  12. // 通知爬虫不再从当前网页中发现待爬url
  13. return false;
  14. };

on_content_page($page, $content, $phpspider)

在爬取到入口url的内容之后, 添加新的url到待爬队列之前调用. 主要用来发现新的待爬url, 并且能给新发现的url附加数据(点此查看“url附加数据”实例解析).

@param $page 当前下载的网页页面的对象@param $content 当前网页内容@param $phpspider 当前爬虫对象@return 返回false表示不需要再从此网页中发现待爬url
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

此函数中通过调用$phpspider->add_url($url, $options)函数来添加新的url到待爬队列。

栗子1:实现这个回调函数并返回false,表示爬虫在处理这个scan_url的时候,不会从中提取待爬url

  1. $spider->on_content_page = function($page, $content, $phpspider)
  2. {
  3. return false;
  4. };

栗子2:生成一个新的url添加到待爬队列中,并通知爬虫不再从当前网页中发现待爬url

  1. $spider->on_content_page = function($page, $content, $phpspider)
  2. {
  3. $array = json_decode($page['raw'], true);
  4. foreach ($array as $v)
  5. {
  6. $lastid = $v['id'];
  7. // 生成一个新的url
  8. $url = $page['url'] . $lastid;
  9. // 将新的url插入待爬的队列中
  10. $phpspider->add_url($url);
  11. }
  12. // 通知爬虫不再从当前网页中发现待爬url
  13. return false;
  14. };

on_handle_img($fieldname, $img)

在抽取到field内容之后调用, 对其中包含的img标签进行回调处理

@param $fieldname 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.@param $img 整个img标签的内容@return 返回处理后的img标签的内容

很多网站对图片作了延迟加载, 这时候就需要在这个函数里面来处理

举个栗子:汽车之家论坛帖子的图片大部分是延迟加载的,默认会使用http://x.autoimg.cn/club/lazyload.png 图片url,我们需要找到真实的图片url并替换,具体实现如下:

  1. $spider->on_handle_img = function($fieldname, $img)
  2. {
  3. $regex = '/src="(https?:\/\/.*?)"/i';
  4. preg_match($regex, $img, $rs);
  5. if (!$rs)
  6. {
  7. return $img;
  8. }
  9. $url = $rs[1];
  10. if ($url == "http://x.autoimg.cn/club/lazyload.png")
  11. {
  12. $regex2 = '/src9="(https?:\/\/.*?)"/i';
  13. preg_match($regex, $img, $rs);
  14. // 替换成真是图片url
  15. if (!$rs)
  16. {
  17. $new_url = $rs[1];
  18. $img = str_replace($url, $new_url);
  19. }
  20. }
  21. return $img;
  22. };

on_extract_field($fieldname, $data, $page)

当一个field的内容被抽取到后进行的回调, 在此回调中可以对网页中抽取的内容作进一步处理

@param $fieldname 当前field的name. 注意: 子field的name会带着父field的name, 通过.连接.@param $data 当前field抽取到的数据. 如果该field是repeated, data为数组类型, 否则是String@param $page 当前下载的网页页面的对象@return 返回处理后的数据, 注意数据类型需要跟传进来的$data类型匹配
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

举个栗子:比如爬取知乎用户的性别信息, 相关网页源码如下:

<span class="item gender" ><i class="icon icon-profile-male"></i></span>那么可以这样写:

  1. $configs = array(
  2. // configs的其他成员
  3. 'fields' => array(
  4. array(
  5. 'name' => "gender",
  6. 'selector' => "//span[contains(@class, 'gender')]/i/@class",
  7. ),
  8. ...
  9. )
  10. );
  11. $spider->on_extract_field = function($fieldname, $data, $page)
  12. {
  13. if ($fieldname == 'gender')
  14. {
  15. // data中包含"icon-profile-male",说明当前知乎用户是男性
  16. if (strpos($data, "icon-profile-male") !== false)
  17. {
  18. return "男";
  19. }
  20. // data中包含"icon-profile-female",说明当前知乎用户是女性
  21. elseif (strpos($data, "icon-profile-female") !== false)
  22. {
  23. return "女";
  24. }
  25. else
  26. {
  27. return "未知";
  28. }
  29. }
  30. return $data;
  31. };

on_extract_page($page, $data)

在一个网页的所有field抽取完成之后, 可能需要对field进一步处理, 以发布到自己的网站

@param $page 当前下载的网页页面的对象@param $data 当前网页抽取出来的所有field的数据@return 返回处理后的数据, 注意数据类型需要跟传进来的$data类型匹配
@param $page['url'] 当前网页的URL@param $page['raw'] 当前网页的内容@param $page['request'] 当前网页的请求对象

举个栗子:比如从某网页中得到time和title两个field抽取项, 现在希望把time的值添加中括号后拼凑到title中,处理过程如下:

  1. $spider->on_extract_page = function($page, $data)
  2. {
  3. $title = "[{$data['time']}]" . $data['title'];
  4. $data['title'] = $title;
  5. return $data;
  6. // 返回false不处理,当前页面的字段不入数据库直接过滤
  7. // 比如采集电影网站,标题匹配到“预告片”这三个字就过滤
  8. //if (strpos($data['title'], "预告片") !== false)
  9. //{
  10. // return false;
  11. //}
  12. };