PHP 利用 Curl Functions 可以完成各种传送文件操作,比如模拟浏览器发送GET,POST请求等等,受限于php语言本身不支持多线程,所以开发爬虫程序效率并不高,这时候往往需 要借助Curl Multi Functions 它可以实现并发多线程的访问多个url地址。既然 Curl Multi Function如此强大,能否用 Curl Multi Functions 来写并发多线程下载文件呢,当然可以,下面给出我的代码:

示例1:爬取内容写入文件

<?php
$urls = array(	'http://mimvp.com',
				'http://proxy.mimvp.com',
				'http://money.mimvp.com',
				'http://domain.mimvp.com',
				);

$save_file = './save_file.txt';
$sf = fopen($save_file, "a");

// 初始化
$mh = curl_multi_init();
foreach ($urls as $i => $url) {
	$conn[$i] = curl_init($url);
	curl_setopt($conn[$i], CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"); 
	curl_setopt($conn[$i], CURLOPT_HEADER, 0);				// 将头文件的信息作为数据流输出
	curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1);		// 返回文件流信息, 1 - 文件; 0 - 屏幕
	curl_setopt($conn[$i], CURLOPT_CONNECTTIMEOUT, 60);		// 设置连接超时
	curl_setopt($conn[$i], CURLOPT_FILE, $sf); 				// 设置爬取内容写入文件
	curl_multi_add_handle($mh, $conn[$i]);					
}

// 执行
do {
	curl_multi_exec($mh, $active);
} while($active);

// 清理结束
foreach ($urls as $i => $url) {
	curl_multi_remove_handle($mh, $conn[$i]);
	curl_close($conn[$i]);
}
curl_multi_close($mh);

fclose($sf);
?>

 

示例2:爬取内容先放入变量,再写入文件

<?php
$urls = array(	'http://mimvp.com',
				'http://proxy.mimvp.com',
				'http://money.mimvp.com',
				'http://domain.mimvp.com',
				);

$save_file = './save_file.txt';
$sf = fopen($save_file, "a");

// 初始化
$mh = curl_multi_init();
foreach ($urls as $i => $url) {
	$conn[$i] = curl_init($url);
	curl_setopt($conn[$i], CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"); 
	curl_setopt($conn[$i], CURLOPT_HEADER, 0);				// 将头文件的信息作为数据流输出
	curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, true);		// 设置不将爬取代码写到浏览器,而是转化为字符串; 返回文件流信息, 1 - 文件; 0 - 屏幕
	curl_setopt($conn[$i], CURLOPT_CONNECTTIMEOUT, 60);		// 设置连接超时
	curl_multi_add_handle($mh, $conn[$i]);					
}

// 执行
do {
	curl_multi_exec($mh, $active);
} while($active);

// 获得内容先存入变量,再写入文件
foreach ($urls as $i => $url) {
	$data = curl_multi_getcontent($conn[$i]);	// 获得爬取的内容
	fwrite($sf, $data);							// 将内容写入文件,也可存入数据库
}

// 清理结束
foreach ($urls as $i => $url) {
	curl_multi_remove_handle($mh, $conn[$i]);
	curl_close($conn[$i]);
}
curl_multi_close($mh);

fclose($sf);
?>

 

curl_multi 使用步骤

第一步:调用curl_multi_init
第二步:循环调用curl_multi_add_handle
这一步需要注意的是,curl_multi_add_handle的第二个参数是由curl_init而来的子handle。
第三步:持续调用curl_multi_exec
第四步:根据需要循环调用curl_multi_getcontent获取结果
第五步:调用curl_multi_remove_handle,并为每个字handle调用curl_close
第六步:调用curl_multi_close

各函数作用:

curl_multi_init()
初始化一个curl批处理句柄资源。

curl_multi_add_handle()
向curl批处理会话中添加单独的curl句柄资源。curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。

curl_multi_exec()
解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。

curl_multi_remove_handle()
移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。

curl_multi_close()
关闭一个批处理句柄资源。

curl_multi_getcontent()
在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。

curl_multi_info_read()
获取当前解析的curl的相关传输信息。

 

Maximum execution time of 30 seconds exceeded解决办法

错误提示:

Fatal error:  Maximum execution time of 30 seconds exceeded in /xxx/lib/mimvp_check.php on line 35

解决办法:

报错一:内存超限

利用循环分批导入,每个循环内部开始处使用sleep(5);语句,做延迟执行,防止服务器内存同一时间占用过多,sleep数字根据实际情况修改;

每个循环内部结束地方使用 ob_flush();刷新输出缓冲
flush();将当前为止程序的所有输出发送到用户的浏览器
两者必须同时使用来刷新输出缓冲

报错二:30秒运行超时的错误Maximum execution time of 30 seconds exceeded

解决办法:

方法一,修改php.ini文件

max_execution_time = 30; Maximum execution time of each script, in seconds

把它设置成需要的值,例如: 600(10分钟),如果设置成0,就是永不过期。

方法二,修改php执行文件,文件开头添加一行

<?   set_time_limit(0);  ?>

 

 

参考推荐

PHP 多线程的应用实例

PHP CURL 使用代理访问服务器

PHP Pthread多线程操作

PHP语言本身是不支持多线程

PHP curl_multi_exec执行超时的问题 (推荐

PHP curl_multi_exec 解析一个cURL批处理句柄

curl_init()和curl_multi_init()多线程的速度比较 推荐