phpspider
是一个 php 爬虫框架,不得不说功能很 nice。
文档:https://doc.phpspider.org/
github:https://github.com/owner888/phpspider
过时函数
我的环境:
- linux nginx php7.2 宝塔
- php redis 拓展 no-debug-non-zts-20170718/redis.so
- Redis 6.0.6
过时了的函数(我目前发现的):
// core/queue.php:993 //其中 lSize() 过时,应该用 lLen() return self::$links[self::$link_name]->lSize($key);
发现的 bug
children 字段相关
某个字段的 children 字段,当
'source_type' => "attached_url"
那么 attached_url 是不应该进入到请求队列的。
作者在注释中也说明了这点:
可是在 2112 行,调用的 request_url() 方法,确确实实会让 url 加入到队列(当请求失败时),具体代码我就不贴了,太多了不好贴。
我是这样修复的:
先标记一下,这是 attached_url :
$link['url'] = $collect_url; /** * 天蓝二开 * 标记一下 attached_url */ $link['url_type'] = 'attached_url'; $link = $this->link_uncompress($link);
然后在 request_url() 方法中对有这个标记的 $link 单独处理:
public function request_url($url, $link = array()) { ... if ($http_code != 200) { // 如果是301、302跳转, 抓取跳转后的网页内容 if ($http_code == 301 || $http_code == 302) { $info = requests::$info; //if (isset($info['redirect_url'])) if (!empty($info['redirect_url'])) { $url = $info['redirect_url']; requests::$input_encoding = null; $method = empty($link['method']) ? 'get' : strtolower($link['method']); $params = empty($link['params']) ? array() : $link['params']; $html = requests::$method($url, $params); // 有跳转的就直接获取就好,不要调用自己,容易进入死循环 //$html = $this->request_url($url, $link); if ($html && !empty($link['context_data'])) { $html .= $link['context_data']; } } else { return false; } } else { /** * 天蓝二开 * 在这里处理 attached_url 阻止进入队列 * 好像还需要对 采集次数什么的进行处理,麻烦,我这里就不搞了 * 所以可能会导致采集面板中的那些次数有误差 */ if (!empty(self::$configs['max_try']) and $link['url_type'] == 'attached_url') { $now_try_num = 0; do { $now_try_num++; if ($now_try_num < $link['max_try']) { log::error("天蓝二开 Failed to download [attached_url] page {$url}, retry({$now_try_num})"); $html = requests::$method($url, $params); } else { //超过最大次数,没办法,返回假吧 return false; } } while (in_array(requests::$status_code, array('0', '502', '503', '429', '507'))); return $html; } if (!empty(self::$configs['max_try']) and $http_code == 407) { // 扔到队列头部去, 继续采集 $this->queue_rpush($link); log::error("Failed to download page {$url}"); self::$collect_fail++; } elseif (!empty(self::$configs['max_try']) and in_array($http_code, array('0', '502', '503', '429'))) { // 采集次数加一 $link['try_num']++; // 抓取次数 小于 允许抓取失败次数 if ($link['try_num'] < $link['max_try']) { // 扔到队列头部去, 继续采集 $this->queue_rpush($link); } log::error("Failed to download page {$url}, retry({$link['try_num']})"); } else { log::error("Failed to download page {$url}"); self::$collect_fail++; } log::error("HTTP CODE: {$http_code}"); return false; } } ... }
重试次数无效
设置了 max_try 但是还是尝试一次:
2020-08-26 14:20:52 [debug] Find scan page: $url
2020-08-26 14:20:53 [error] Failed to download page $url, retry(1)
2020-08-26 14:20:53 [error] HTTP CODE: 0
这样之后就没有下文了!不会进行重试,上面的 (1) 只是尝试次数,而非 重试次数。
bug 定位:
public function queue_rpush($link = array(), $allowed_repeat = false) { ... if (self::$use_redis) {...} else { /** * 天蓝注 * 作者说 self::$collect_urls 是【要抓取的URL数组】 * 在这段代码中,只要 url 入队了,就会被加入 $collect_urls * 那么当 url 下载失败,重新入队时,就会因为 * !array_key_exists($key, self::$collect_urls) === false * 而无法入队 * 这显然不合适 * * 上面使用 redis 的分支也存在相似问题 * * 解决办法:在 重试url 入队之前,将 url 移出 self::$collect_urls */ $key = md5($url); if (!array_key_exists($key, self::$collect_urls)) { self::$collect_urls_num++; self::$collect_urls[$key] = time(); array_unshift(self::$collect_queue, $link); $status = true; } } return $status; }
结语
phpspider 是一款很棒的开源产品,希望开发者可以尽快修复,越做越好!