<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[沧海一粟]]></title> 
<link>http://www.dzhope.com/index.php</link> 
<description><![CDATA[Web系统架构与服务器运维,php开发]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[沧海一粟]]></copyright>
<item>
<link>http://www.dzhope.com/post//</link>
<title><![CDATA[PHP curl实现多进程并发高效率采集爬虫]]></title> 
<author>jed &lt;jed521@163.com&gt;</author>
<category><![CDATA[服务器技术]]></category>
<pubDate>Tue, 13 Dec 2016 07:44:58 +0000</pubDate> 
<guid>http://www.dzhope.com/post//</guid> 
<description>
<![CDATA[ 
	PHP curl实现多进程并发抓取数据我们经常的用到了，今天我们来看一篇关于PHP curl实现多进程并发高效率采集爬虫的例子，具体的细节如下。<br/><br/><br/><br/>主要封装函数<br/><br/>multi_process();<br/>根据参数，创建指点数目的子进程。<br/>亮点功能1：子进程各种异常退出，比如segment fault, Allowed memory size exhausted等，中断一个子进程后，父进程会重新fork一个新进程顶上去，保持子进程数量。如果子进程里完成任务（比如判断tid达到10000），可以在子进程里exit(9)，父进程会收到这个退出状态(9)，然后等待所有子进程退出，最后退出自身进程。<br/>亮点功能2：与curl封装函数一起实现了一统计功能，在程序关闭后会显示出一些主要的统计信息(图2的底部)。<br/>mp_counter();<br/>在父进程以及所有子进程之间通信，负责协调分配各子进程的任务，使用了锁机制。可以设置’init’参数重置计数，可以设置每次更新计数的值。<br/>curl_get();<br/>对curl相关函数的封装，加入了大量的错误机制，支持POST,GET,Cookie,Proxy,下载。<br/>mp_msg();<br/>实现规范之一就是，每条任务处理完，只输出一行信息。<br/>亮点功能：这个函数会判断终端的高度和宽度，实现每一屏内容会显示一条统计信息(图1的紫色行)，便于观察程序的执行情况，控制每一行输出的长度，保持一条信息不会超过一行。<br/>rand_exit();<br/>众所周知，PHP存在内在泄露的问题，所以每一个子进程里执行一定次数的任务后就退出，由multi_process()负责自动建立新的子进程(如图1中的绿色行)。<br/><br/>curl.lib.php<br/><div class="code"><br/>&lt;?php<br/><br/>// 命令行颜色输出<br/>$colors&#91;&#039;red&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;31m&quot;;<br/>$colors&#91;&#039;green&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;32m&quot;;<br/>$colors&#91;&#039;yellow&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= &quot;&#92;33&#91;33m&quot;;<br/>$colors&#91;&#039;end&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;0m&quot;;<br/>$colors&#91;&#039;reverse&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;7m&quot;;<br/>$colors&#91;&#039;purple&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= &quot;&#92;33&#91;35m&quot;;<br/><br/>/*<br/> 默认参数设置<br/>*/<br/>$curl_default_config&#91;&#039;ua&#039;&#93; = &#039;Mozilla/5.0 (compatible; Baiduspider/2.0; http://www.baidu.com/search/spider.html)&#039;;<br/>$curl_default_config&#91;&#039;referer&#039;&#93; = &#039;&#039;;<br/>$curl_default_config&#91;&#039;retry&#039;&#93; = 5;<br/>$curl_default_config&#91;&#039;conntimeout&#039;&#93; = 30;<br/>$curl_default_config&#91;&#039;fetchtimeout&#039;&#93; = 30;<br/>$curl_default_config&#91;&#039;downtimeout&#039;&#93; = 60;<br/><br/>/*<br/> 针对指定域名设置referer(通常是用于下载图片)，优先于$curl_default_config<br/> 默认使用空referer，一般不会有问题<br/> eg: $referer_config = array(<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;img_domain&#039;=&gt;&#039;web_domain&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;e.hiphotos.baidu.com&#039;=&gt;&#039;http://hi.baidu.com/&#039;);<br/>*/<br/>$referer_config = array(&#039;img1.51cto.com&#039;=&gt;&#039;blog.51cto.com&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#039;360doc.com&#039;=&gt;&#039;www.360doc.com&#039;);<br/><br/>/*<br/>针对指定域名设置User-agent，优先于$curl_default_config<br/> 默认使用百度蜘蛛的UA，拒绝百度UA的网站极少<br/> eg: $useragent_config = array(<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;web_domain&#039;=&gt;&#039;user agent&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;www.xxx.com&#039;=&gt;&#039;Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)&#039;);<br/>*/<br/>$useragent_config = array(&#039;hiphotos.baidu.com&#039;=&gt;&#039;Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)&#039;;<br/><br/>/*<br/> * 如果机器有多个IP地址，可以改变默认的出口IP，每次调用会在数组中随机选择一个。考虑到可能会有需要排除的IP，所以这里不自动配置为所有的IP。<br/> * eg: $curl_ip_config = array(&#039;11.11.11.11&#039;, &#039;22.22.22.22&#039;);<br/> */<br/>$local_ip_config = array();<br/><br/>// cookie和临时文件目录<br/>if((@file_exists(&#039;/dev/shm/&#039;) &amp;&amp; @is_writable(&#039;/dev/shm/&#039;)))&#123;<br/> $cookie_dir = $tmpfile_dir = &#039;/dev/shm/&#039;;<br/>&#125;else&#123;<br/> $cookie_dir = $tmpfile_dir = &#039;/tmp/&#039;;<br/>&#125;<br/><br/>// 清除过期的cookie文件和下载临时文件<br/>if(php_sapi_name() == &#039;cli&#039;)&#123;<br/> clear_curl_file();<br/>&#125;<br/><br/>/**<br/> * GET方式抓取网页<br/> *<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;网页URL地址<br/> * @param string $encode 返回的页面编码，默认为GBK，设置为空值则不转换<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;网页HTML内容<br/> */<br/>function curl_get($url, $encode=&#039;gbk&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return curl_func($url, &#039;GET&#039;, null, null, null, $encode);<br/>&#125;<br/><br/>/**<br/> * POST方式请求网页<br/> *<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求的URL地址<br/> * @param array&nbsp;&nbsp;$data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发送的POST数据<br/> * @param string $encode&nbsp;&nbsp;&nbsp;&nbsp;返回的页面编码，默认为GBK，设置为空值则不转换<br/> * @return bool<br/> */<br/>function curl_post($url, $data, $encode=&#039;gbk&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return curl_func($url, &#039;POST&#039;, $data, null, null, $encode);<br/>&#125;<br/><br/>/**<br/> * 获取页面的HEADER信息<br/> *<br/> * HTTP状态码并不是以“名称:值”的形式返回，这里以http_code作为它的名称，其他的值都有固定的名称，并且转成小写<br/> *<br/> * @param string $url URL地址<br/> * @return array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;返回HEADER数组<br/> */<br/>function curl_header($url, $follow=true)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$header_text = curl_func($url, &#039;HEADER&#039;);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!$header_text)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 获取HTTP头失败<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$header_array =explode(&quot;&#92;r&#92;n&#92;r&#92;n&quot;, trim($header_text));<br/> if($follow)&#123;<br/>&nbsp;&nbsp;$last_header = array_pop($header_array);<br/> &#125;else&#123;<br/>&nbsp;&nbsp;$last_header = array_shift($header_array);<br/> &#125;<br/>&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;$lines = explode(&quot;&#92;n&quot;, trim($last_header));<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 处理状态码<br/>&nbsp;&nbsp;&nbsp;&nbsp;$status_line = trim(array_shift($lines));<br/>&nbsp;&nbsp;&nbsp;&nbsp;preg_match(&quot;/(&#92;d&#92;d&#92;d)/&quot;, $status_line, $preg);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($preg&#91;1&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$header&#91;&#039;http_code&#039;&#93; = $preg&#91;1&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$header&#91;&#039;http_code&#039;&#93; = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach ($lines as $line) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list($key, $val) = explode(&#039;:&#039;, $line, 2);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$key = str_replace(&#039;-&#039;, &#039;_&#039;, strtolower(trim($key)));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$header&#91;$key&#93; = trim($val);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $header;<br/>&#125;<br/><br/>/**<br/> * 下载文件<br/> *<br/> * @param $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;文件地址<br/> * @param $path&nbsp;&nbsp;&nbsp;&nbsp; 保存到的本地路径<br/> * @return bool&nbsp;&nbsp;&nbsp;&nbsp; 下载是否成功<br/> */<br/>function curl_down($url, $path, $data=null, $proxy=null)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(empty($data))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$method = &#039;GET&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$method = &#039;POST&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;return curl_func($url, $method, $data, $path, $proxy);<br/>&#125;<br/><br/>/**<br/> * 使用代理发起GET请求<br/> *<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求的URL地址<br/> * @param string $proxy&nbsp;&nbsp;&nbsp;&nbsp; 代理地址<br/> * @param string $encode&nbsp;&nbsp;&nbsp;&nbsp;返回编码<br/> *<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网页内容<br/> */<br/>function curl_get_by_proxy($url, $proxy, $encode=&#039;gbk&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return curl_func($url, &#039;GET&#039;, null, null, $proxy, $encode);<br/>&#125;<br/><br/><br/>/**<br/> * 使用代理发起POST请求<br/> *<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求的URL地址<br/> * @param string $proxy&nbsp;&nbsp;&nbsp;&nbsp; 代理地址<br/> * @param string $encode&nbsp;&nbsp;&nbsp;&nbsp;返回编码<br/> *<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网页内容<br/> */<br/>function curl_post_by_proxy($url, $data, $proxy, $encode=&#039;gbk&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return curl_func($url, &#039;POST&#039;, $data, null, $proxy, $encode);<br/>&#125;<br/><br/>/**<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求的URL地址<br/> * @param string $encode&nbsp;&nbsp;&nbsp;&nbsp;返回编码<br/> *<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网页内容<br/> */<br/><br/>function img_down($url, $path_pre)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$img_tmp = &#039;/tmp/curl_imgtmp_pid_&#039;.getmypid();<br/>&nbsp;&nbsp;&nbsp;&nbsp;$res = curl_down($url, $img_tmp);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(empty($res))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $res;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$ext = get_img_ext($img_tmp);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(empty($ext))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$path = &quot;&#123;$path_pre&#125;.&#123;$ext&#125;&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;@mkdir(dirname($path), 0777, TRUE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 转移临时的文件路径<br/>&nbsp;&nbsp;&nbsp;&nbsp;rename($img_tmp, $path);<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $path;<br/>&#125;<br/><br/>function get_img_ext($path)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$types = array(<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 =&gt; &#039;gif&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 =&gt; &#039;jpg&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3 =&gt; &#039;png&#039;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6 =&gt; &#039;bmp&#039;<br/>&nbsp;&nbsp;&nbsp;&nbsp;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$info = @getimagesize($path);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(isset($types&#91;$info&#91;2&#93;&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$ext = $info&#91;&#039;type&#039;&#93; = $types&#91;$info&#91;2&#93;&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$ext == &#039;jpeg&#039; &amp;&amp; $ext = &#039;jpg&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125; else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$ext = FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $ext;<br/>&#125;<br/><br/>/**<br/> * 获取文件类型<br/> *<br/> * @param string $filepath 文件路径<br/> * @return array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 返回数组，格式为array($type, $ext)<br/> */<br/>function get_file_type($filepath)&#123;<br/><br/>&#125;<br/><br/>/**<br/> * 返回文件的大小，用于下载文件后判断与本地文件大小是否相同<br/> * curl_getinfo()方式获得的size_download并不一定是文件的真实大小<br/> *<br/> * @param&nbsp;&nbsp;string $url&nbsp;&nbsp;&nbsp;&nbsp;URL地址<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网络文件的大小<br/> */<br/>function get_file_size($url)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$header = curl_header($url);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($header&#91;&#039;content_length&#039;&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $header&#91;&#039;content_length&#039;&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>/**<br/> * 获取状态码<br/> *<br/> * @param&nbsp;&nbsp;string $url URL地址<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;状态码<br/> */<br/>function get_http_code($url, $follow=true)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$header = curl_header($url, $follow);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($header&#91;&#039;http_code&#039;&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $header&#91;&#039;http_code&#039;&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>/**<br/> * 获取URL文件后缀<br/> *<br/> * @param&nbsp;&nbsp;string $url URL地址<br/> * @return array&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;文件类型的后缀<br/> */<br/>function curl_get_ext($url)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$header = curl_header($url);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($header&#91;&#039;content_type&#039;&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@list($type, $ext) = @explode(&#039;/&#039;, $header&#91;&#039;content_type&#039;&#93;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($type) &amp;&amp; !empty($ext))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return array($type, $ext);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return array(&#039;&#039;, &#039;&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return array(&#039;&#039;, &#039;&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>/**<br/> * 封装curl操作<br/> *<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;请求的URL地址<br/> * @param string $method&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请求的方法(POST, GET, HEADER, DOWN)<br/> * @param mix&nbsp;&nbsp;&nbsp;&nbsp;$arg&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;POST方式为POST数据，DOWN方式时为下载保存的路径<br/> * @param string $return_encode&nbsp;&nbsp;网页返回的编码<br/> * @param string $proxy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;代理<br/> * @return mix&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 返回内容。4xx序列错误和空白页面会返回NULL，curl抓取错误返回False。结果正常则返回页面内容。<br/> */<br/>// 待改进，下载到临时文件，下载成功后再转移（已经有文件则覆盖），下载失败则删除。<br/>// 待改进，参数形式改成curl_func($url, $method, $data=null, savepath=null, $proxy=null, $return_encode=&#039;gbk&#039;)<br/>function curl_func($url, $method, $data=null, $savepath=null, $proxy=null, $return_encode=null)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;global $colors, $cookie_dir, $tmpfile_dir, $referer_config, $useragent_config, $local_ip_config, $curl_config;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 控制台输出颜色<br/>&nbsp;&nbsp;&nbsp;&nbsp;extract($colors);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 去除URL中的/../<br/>&nbsp;&nbsp;&nbsp;&nbsp;$url = get_absolute_path($url);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 去除实体转码<br/>&nbsp;&nbsp;&nbsp;&nbsp;$url = htmlspecialchars_decode($url);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 统计数据<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(function_exists(&#039;mp_counter&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;down_total&#039;);&nbsp;&nbsp; // 下载次数计数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;elseif($method == &#039;HEADER&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;header_total&#039;); // 抓取HTTP头次数计数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;fetch_total&#039;);&nbsp;&nbsp;// 抓取网页次数计数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;for($i = 0; $i &lt; curl_config_get(&#039;retry&#039;); $i )&#123;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 初始化<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$ch = curl_init();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_URL, $url);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置超时<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, curl_config_get(&#039;conntimeout&#039;));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 连接超时<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_TIMEOUT, curl_config_get(&#039;fetchtimeout&#039;));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 抓取网页(包括HEADER)超时<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_TIMEOUT, curl_config_get(&#039;downtimeout&#039;));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 下载文件超时<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 接收网页内容到变量<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 忽略SSL验证<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置referer, 在文件里配置的优先级最高<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach($referer_config as $domain=&gt;$ref)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(stripos($url, $domain) !== FALSE)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$referer = $ref;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;// 检查是否有通过curl_set_referer()设置referer<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($referer) &amp;&amp; !empty($curl_config&#91;getmypid()&#93;&#91;&#039;referer&#039;&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$referer = $curl_config&#91;getmypid()&#93;&#91;&#039;referer&#039;&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;if(!empty($referer))&#123;<br/>&nbsp;&nbsp; curl_setopt($ch, CURLOPT_REFERER, $referer);<br/>&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置HTTP请求标识，在文件里配置的优先级最高<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach($useragent_config as $domain=&gt;$ua)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(stripos($url, $domain) !== FALSE)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$useragent = $ua;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;// 检查是否有通过curl_set_ua()设置useragent<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($useragent))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$useragent = curl_config_get(&#039;ua&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_USERAGENT, $useragent);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 出口IP<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($local_ip_config))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_INTERFACE, $local_ip_config&#91;array_rand($local_ip_config)&#93;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置代理<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($proxy))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_PROXY, $proxy);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置允许接收gzip压缩数据，以及解压，抓取HEADER时不使用(获取不到正确的文件大小，影响判断下载成功)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($method != &#039;HEADER&#039;) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_HTTPHEADER, array(&#039;Accept-Encoding: gzip, deflate&#039;));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_ENCODING, &quot;&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 遇到301和302转向自动跳转继续抓取，如果用于WEB程序并且设置了open_basedir，这个选项无效<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 最大转向次数，避免进入到死循环<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_MAXREDIRS, 5);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 启用cookie<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$cookie_path = $cookie_dir . &#039;curl_cookie_pid_&#039; . get_ppid();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_path);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_path);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置post参数内容<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($method == &#039;POST&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_HEADER, 0);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_POSTFIELDS, $data);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 设置用于下载的参数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$tmpfile = $tmpfile_dir . &#039;/curl_tmpfile_pid_&#039;.getmypid();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file_exists($tmpfile) &amp;&amp; unlink($tmpfile);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$fp = fopen($tmpfile, &#039;w&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_FILE, $fp);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 仅获取header<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($method == &#039;HEADER&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_NOBODY, TRUE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_setopt($ch, CURLOPT_HEADER, TRUE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 抓取结果<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$curl_res = curl_exec($ch);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// curl info<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$info = curl_getinfo($ch);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 调试curl时间，记录连接时间，等待时间，传输时间，总时间。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 测试方法，任何输出前设置sleep，输出中间设置sleep<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach($info as $key=&gt;$val)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;$key:$val&#92;n&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(9);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 错误信息<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = curl_error($ch);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_no = curl_errno($ch);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 关闭CURL句柄<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_close($ch);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果CURL有错误信息则判断为抓取失败，重试<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($error_no) &#124;&#124; !empty($error_msg))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = &quot;&#123;$error_msg&#125;($error_no)&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_msg($error_msg, $method, $url, &#039;yellow&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 统计流量<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(function_exists(&#039;mp_counter&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($info&#91;&#039;size_download&#039;&#93;) &amp;&amp; $info&#91;&#039;size_download&#039;&#93; &gt; 0)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;download_total&#039;, $info&#91;&#039;size_download&#039;&#93;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 对结果进行处理<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($method == &#039;HEADER&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 返回header信息<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $curl_res;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 最终的状态码<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$status_code = $info&#91;&#039;http_code&#039;&#93;;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(in_array($status_code, array_merge(range(400, 417), array(500, 444))))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 非服务器故障性的错误，直接退出，返回NULL<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = $status_code;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$method = &quot;&#123;$method&#125;&#124;DOWN&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_msg($error_msg, $method, $url, &#039;red&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;if($status_code != 200)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 防止网站502等临时错误，排除了上面的情况后，非200就重试。这一条规则需要后续根据情况来改进。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// curl执行过程中会自动跳转，这里不会出现301和302，除非跳转次数超过CURLOPT_MAXREDIRS的值<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = $status_code;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_msg($error_msg, $method, $url, &#039;yellow&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 抓取页面<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($curl_res))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 空白页面<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = &quot;blank page&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 返回NULL值，调用处注意判断<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 默认将页面以GBK编码返回<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 分析页面编码<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;preg_match_all(&quot;/&lt;meta.*?charset=(&#92;&quot;&#124;&#039;?)(.*?)(;&#124;&#92;&quot;&#124;&#039;&#124;&#92;s)/is&quot;, $curl_res, $matches);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 转码条件：1)匹配到编码, 2)返回编码不为空, 3）匹配到的编码和返回编码不相同<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($matches&#91;2&#93;&#91;0&#93;) &amp;&amp; !empty($return_encode)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;&amp; str_replace(&#039;-&#039;, &#039;&#039;, strtolower($matches&#91;2&#93;&#91;0&#93;))<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;!= str_replace(&#039;-&#039;, &#039;&#039;, strtolower($return_encode)))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$curl_res = @iconv($matches&#91;2&#93;&#91;0&#93;, &quot;&#123;$return_encode&#125;//IGNORE&quot;, $curl_res);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 替换网页标明的编码<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$curl_res = str_ireplace($matches&#91;2&#93;&#91;0&#93;, $return_encode, $curl_res);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// iconv如果失败则返回空白页<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($curl_res))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return NULL;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 将相对路径转换为绝对路径<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$curl_res = relative_to_absolute($curl_res, $url);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return $curl_res;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 下载文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(@filesize($tmpfile) == 0)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$error_msg = &#039;Emtpy Content&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 统计下载文件量<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(function_exists(&#039;mp_counter&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;download_size&#039;, filesize($tmpfile));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 创建目录<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@mkdir(dirname($savepath), 0777, TRUE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 转移临时的文件路径<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rename($tmpfile, $savepath);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TRUE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 如果是下载或者抓取header，并且错误代码为6(域名无法解析)，则不输出错误。失效的图片引用太多了。<br/>&nbsp;&nbsp;&nbsp;&nbsp;// 域名不合法的时候也无法输出错误了，需要改进，在前面判断URL的合法性<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!(($method == &#039;HEADER&#039; &#124;&#124; !empty($savepath)) &amp;&amp; !empty($error_no) &amp;&amp; $error_no == 6))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$method = &quot;&#123;$method&#125;&#124;DOWN&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_msg($error_msg, $method, $url, &#039;red&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 统计数据<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(function_exists(&#039;mp_counter&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($savepath))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;down_failed&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;elseif($method == &#039;HEADER&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;header_failed&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;fetch_failed&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<br/>&#125;<br/><br/>/**<br/> * 输出错误信息<br/> *<br/> * @param string $msg&nbsp;&nbsp;&nbsp;&nbsp; 错误信息<br/> * @param string $method&nbsp;&nbsp;请求方式<br/> * @param string $url&nbsp;&nbsp;&nbsp;&nbsp; URL地址<br/> * @param string $color&nbsp;&nbsp; 颜色<br/> */<br/>function curl_msg($msg, $method, $url, $color)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;global $colors;<br/>&nbsp;&nbsp;&nbsp;&nbsp;extract($colors);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 多并发下建议关闭黄色错误输出<br/>&nbsp;&nbsp;&nbsp;&nbsp;//$available_msg&#91;&#93; = &#039;yellow&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$available_msg&#91;&#93; = &#039;red&#039;;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if(php_sapi_name() != &#039;cli&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!in_array($color, $available_msg))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;&#123;$reverse&#125;&quot;.$colors&#91;$color&#93;.&quot;(&#123;$method&#125;)&#91;cURL ERROR: &#123;$msg&#125;&#93; &#123;$url&#125;&#123;$end&#125;&#92;n&quot;;<br/>&#125;<br/><br/>/**<br/> * 将URL地址转换为绝对路径<br/> * URL地址有可能会遇到包含&#039;/../&#039;构成的相对路径，curl不会自动转换<br/> * echo get_absolute_path(&quot;http://www.a.com/a/../b/../c/../././index.php&quot;);<br/> * 结果为：http://www.a.com/index.php<br/> *<br/> * @param&nbsp;&nbsp;string $path 需要处理的URL<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 返回URL的绝对路径<br/> */<br/>function get_absolute_path($path) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$parts = array_filter(explode(&#039;/&#039;, $path), &#039;strlen&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$absolutes = array();<br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach ($parts as $part) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (&#039;.&#039; == $part) continue;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (&#039;..&#039; == $part) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array_pop($absolutes);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125; else &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$absolutes&#91;&#93; = $part;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return str_replace(&#039;:/&#039;, &#039;://&#039;, implode(&#039;/&#039;, $absolutes));<br/>&#125;<br/><br/>/**<br/> * 使用图片URL的md5值作为路径，并且分级目录<br/> * 深度为e时，伪静态规则为rewrite ^/(.)(.)(.)(.*)$ /$1/$2/$3/$4 break;<br/> * 平均1篇文章1张图片，三千万文章，三千万图片，3级目录最终4096子目录，平均每目录7324个图片<br/> *<br/> * @param string $str 原图片地址<br/> * @param int $deep&nbsp;&nbsp; 目录深度<br/> * @return string&nbsp;&nbsp;&nbsp;&nbsp; 返回分级目录<br/> */<br/>function md5_path($str, $deep = 3)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$md5 = substr(md5($str), 0, 16);<br/>&nbsp;&nbsp;&nbsp;&nbsp;preg_match_all(&#039;/./&#039;, $md5, $preg);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$res = &#039;&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;for($i = 0; $i &lt; count($preg&#91;0&#93;); $i )&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$res .= $preg&#91;0&#93;&#91;$i&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($i &lt; $deep)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$res .= &#039;/&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $res;<br/>&#125;<br/><br/>function relative_to_absolute($content, $url) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&quot;/src&#92;s*=&#92;s*&#92;&quot;&#92;s*/&quot;, &#039;src=&quot;&#039;, $content);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&quot;/href&#92;s*=&#92;s*&#92;&quot;&#92;s*/&quot;, &#039;href=&quot;&#039;, $content);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;preg_match(&quot;/(http&#124;https&#124;ftp):&#92;/&#92;/&#91;^&#92;/&#93;*/&quot;, $url, $preg_base);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($preg_base&#91;0&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// $preg_base&#91;0&#93;内容如http://www.yundaiwei.com<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 这里处理掉以/开头的链接，也就是相对于网站根目录的路径<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&#039;/href=&#92;s*&quot;&#92;//i&#039;, &#039;href=&quot;&#039;.$preg_base&#91;0&#93;.&#039;/&#039;, $content);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&#039;/src=&#92;s*&quot;&#92;//ims&#039;, &#039;src=&quot;&#039;.$preg_base&#91;0&#93;.&#039;/&#039;, $content);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;preg_match(&quot;/(http&#124;https&#124;ftp):&#92;/&#92;/.*&#92;//&quot;, $url, $preg_full);<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!empty($preg_full&#91;0&#93;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 这里处理掉相对于目录的路径，如src=&quot;../../images/jobs/lippman.gif&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 排除掉file://开头的本地文件链接，排除掉data:image方式的BASE64图片<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&#039;/href=&#92;s*&quot;&#92;s*(?!http&#124;file:&#92;/&#92;/&#124;data:image&#124;javascript)/i&#039;, &#039;href=&quot;&#039;.$preg_full&#91;0&#93;, $content);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$content = preg_replace(&#039;/src=&#92;s*&quot;&#92;s*(?!http&#124;file:&#92;/&#92;/&#124;data:image&#124;javascript)/i&#039;, &#039;src=&quot;&#039;.$preg_full&#91;0&#93;, $content);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;return $content;<br/>&#125;<br/><br/>/**<br/> * 清除过期的cookie文件和下载临时文件<br/> */<br/>function clear_curl_file()&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;global $cookie_dir;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;$cookie_files = glob(&quot;&#123;$cookie_dir&#125;curl_*_pid_*&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$tmp_files = glob(&quot;/tmp/curl_*_pid_*&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;$files = array_merge($cookie_files, $tmp_files);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;foreach($files as $file)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;preg_match(&quot;/pid_(&#92;d*)/&quot;, $file, $preg);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$pid = $preg&#91;1&#93;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$exe_path = &quot;/proc/&#123;$pid&#125;/exe&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果文件不存在则说明进程不存在，判断是否为PHP进程，排除php-fpm进程<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!file_exists($exe_path)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#124;&#124; stripos(readlink($exe_path), &#039;php&#039;) === FALSE<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#124;&#124; stripos(readlink($exe_path), &#039;php-fpm&#039;) === TRUE)&#123;<br/>&nbsp;&nbsp; $sem = @sem_get(@ftok($file, &#039;a&#039;));<br/>&nbsp;&nbsp; if($sem)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;@sem_remove($sem);<br/>&nbsp;&nbsp; &#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unlink($file);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>/**<br/> * 如果是在子进程中，获取父进程PID，否则获取自身PID<br/> * @return int<br/> */<br/>if(!function_exists(&#039;get_ppid&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;function get_ppid()&#123;<br/>&nbsp;&nbsp;<br/>&nbsp;&nbsp;if(php_sapi_name() != &#039;cli&#039;)&#123;<br/>&nbsp;&nbsp; // 如果是web方式调用，返回PHP执行进程PID，如APACHE或者PHP-FPM<br/>&nbsp;&nbsp; getmypid();<br/>&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp; // 命令行执行进入到这里<br/>&nbsp;&nbsp; // 这里需要识别出是在子进程中调用还是在父进程中调用，不同的形式，保存的变量内容的文件位置需要保持一致<br/>&nbsp;&nbsp; $ppid = posix_getppid();<br/>&nbsp;&nbsp; // 理论上，这种判断方式可能会出坑。但在实际使用中，除了fork出的子进程外，不太可能让PHP进程的父进程的程序名中出现php字样。<br/>&nbsp;&nbsp; if(strpos(readlink(&quot;/proc/&#123;$ppid&#125;/exe&quot;), &#039;php&#039;) === FALSE)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$pid = getmypid();<br/>&nbsp;&nbsp; &#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$pid = $ppid;<br/>&nbsp;&nbsp; &#125;<br/>&nbsp;&nbsp; return $pid;<br/>&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>// UTF-8转GBK<br/>if(!function_exists(&#039;u2g&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;function u2g($string)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return @iconv(&quot;UTF-8&quot;, &quot;GBK//IGNORE&quot;, $string);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/><br/>// GBK转UTF-8<br/>if(!function_exists(&#039;g2u&#039;)) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;function g2u($string)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return @iconv(&quot;GBK&quot;, &quot;UTF-8//IGNORE&quot;, $string);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/>&#125;<br/><br/>function curl_rand_ua_pc()&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$ua = &#039;Mozilla/5.0 (compatible; MSIE &#039;.rand(7, 9).<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#039;.0; Windows NT 6.1; WOW64; Trident/&#039;.rand(4, 5).&#039;.0)&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $ua;<br/>&#125;<br/><br/>function curl_rand_ua_mobile()&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$op = &#039;Mozilla/5.0 (Linux; U; Android &#039;.rand(4,5).&#039;.&#039;.rand(1,5).&#039;.&#039;.rand(1,5).&#039;; zh-cn; MI &#039;.rand(3, 5).&#039;);&#039;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$browser = &#039;AppleWebKit/&#039;.rand(500, 700).&#039;.&#039;.rand(1,100).&#039;.&#039;.rand(1,100)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.&#039; (KHTML, like Gecko) Version/&#039;.rand(5,10)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.&#039;.0 Mobile Safari/537.36 XiaoMi/MiuiBrowser/&#039;.rand(1,5).&#039;.&#039;.rand(1,5).&#039;.&#039;.rand(1,5);<br/>&nbsp;&nbsp;&nbsp;&nbsp;return $op.$browser;<br/>&#125;<br/><br/>function curl_config_get($key)&#123;<br/> global $curl_config, $curl_default_config;<br/><br/> if(!empty($curl_config&#91;getmypid()&#93;&#91;$key&#93;))&#123;<br/>&nbsp;&nbsp;return $curl_config&#91;getmypid()&#93;&#91;$key&#93;;<br/> &#125;elseif(!empty($curl_default_config&#91;$key&#93;))&#123;<br/>&nbsp;&nbsp;return $curl_default_config&#91;$key&#93;;<br/> &#125;else&#123;<br/>&nbsp;&nbsp;echo &#039;$curl_default_config&#039;.&quot;&#91;$key&#93; Not Found!&#92;n&quot;;<br/>&nbsp;&nbsp;exit(9);<br/> &#125;<br/>&#125;<br/><br/>function curl_config_set($key, $val)&#123;<br/> global $curl_config;<br/> $curl_config&#91;getmypid()&#93;&#91;$key&#93; = $val;<br/>&#125;<br/><br/>function curl_set_ua($ua)&#123;<br/> curl_config_set(&#039;ua&#039;, $ua);<br/>&#125;<br/><br/>function curl_set_referer($referer)&#123;<br/> curl_config_set(&#039;referer&#039;, $referer);<br/>&#125;<br/><br/>function curl_set_retry($retry)&#123;<br/> curl_config_set(&#039;retry&#039;, $retry);<br/>&#125;<br/><br/>function curl_set_conntimeout($conntimeout)&#123;<br/> curl_config_set(&#039;conntimeout&#039;, $conntimeout);<br/>&#125;<br/><br/>function curl_set_fetchtimeout($fetchtimeout)&#123;<br/> curl_config_set(&#039;fetchtimeout&#039;, $fetchtimeout);<br/>&#125;<br/><br/>function curl_set_downtimeout($downtimeout)&#123;<br/> curl_config_set(&#039;downtimeout&#039;, $downtimeout);<br/>&#125;<br/></div><br/><br/>process.lib.php<br/><div class="code"><br/>&lt;?php<br/>if(php_sapi_name() != &#039;cli&#039;)&#123;<br/> return;<br/>&#125;<br/><br/>declare(ticks = 1);<br/><br/>// 中断信号<br/>$signals = array(<br/>&nbsp;&nbsp;&nbsp;&nbsp;SIGINT&nbsp;&nbsp;=&gt; &quot;SIGINT&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;SIGHUP&nbsp;&nbsp;=&gt; &quot;SIGHUP&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;SIGQUIT =&gt; &quot;SIGQUIT&quot;<br/>);<br/><br/>// 命令行颜色输出<br/>$colors&#91;&#039;red&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;31m&quot;;<br/>$colors&#91;&#039;green&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;32m&quot;;<br/>$colors&#91;&#039;yellow&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= &quot;&#92;33&#91;33m&quot;;<br/>$colors&#91;&#039;end&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;0m&quot;;<br/>$colors&#91;&#039;reverse&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp; = &quot;&#92;33&#91;7m&quot;;<br/>$colors&#91;&#039;purple&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= &quot;&#92;33&#91;35m&quot;;<br/>$colors&#91;&#039;cyan&#039;&#93;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= &quot;&#92;33&#91;36m&quot;;<br/><br/>// 程序开始运行时间<br/>$start_time = time();<br/><br/>// 父进程PID<br/>$fpid = getmypid();<br/><br/>// 文件保存目录，/dev/shm/是内存空间映射到硬盘上，IO速度快。<br/>// 有些环境上可能会没有这个目录，比如OpenVZ的VPS，这个路径实际是在硬盘上<br/>if(file_exists(&#039;/dev/shm/&#039;) &amp;&amp; is_dir(&#039;/dev/shm/&#039;))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$process_file_dir = &#039;/dev/shm/&#039;;<br/>&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$process_file_dir = &#039;/tmp/&#039;;<br/>&#125;<br/><br/>// 清理过期资源(文件和SEM信号锁)，每次程序执行都需要调用，清除掉之前执行时的残留文件。<br/>clear_process_resource();<br/><br/>// 判断是否在子进程中<br/>function is_subprocess()&#123;<br/> global $fpid;<br/> if(getmypid() != $fpid)&#123;<br/>&nbsp;&nbsp;return true;<br/> &#125;else&#123;<br/>&nbsp;&nbsp;return false;<br/> &#125;<br/>&#125;<br/><br/>/**<br/> * 多进程计数<br/> *<br/> * 1,用于多进程运行时的任务分配与计数，比如要采集某DZ论坛的帖子，则可以将计数器用于/thread-tid-1-1.html中<br/> * 的tid，实现进程间的协调工作<br/> * 2,由于shm_*系列函数的操作不够灵活，所以这里主要用于/proc/和/dev/shm/这二个目录来实现数据的读写（内存操<br/> * 作，不受硬盘IO性能影响），用semaphore信号来实现锁定和互斥机制<br/> * 3,编译PHP时需要使用参数--enable-sysvmsg安装所需的模块<br/> *<br/> * @param&nbsp;&nbsp; string&nbsp;&nbsp;$countername&nbsp;&nbsp;&nbsp;&nbsp;计数器名称<br/> * @param&nbsp;&nbsp; mix&nbsp;&nbsp;&nbsp;&nbsp; $update&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计数器的更新值，如果是&#039;init&#039;，计数器则被初始化为0<br/> * @return int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;返回计数<br/> */<br/>function mp_counter($countername, $update=1)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;global $process_file_dir;<br/>&nbsp;&nbsp;&nbsp;&nbsp;$time = date(&#039;Y-m-d H:i:s&#039;);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 父进程PID或者自身PID<br/>&nbsp;&nbsp;&nbsp;&nbsp;$top_pid = get_ppid();<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 系统启动时间<br/>&nbsp;&nbsp;&nbsp;&nbsp;$sysuptime = get_sysuptime();<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 进程启动时间<br/>&nbsp;&nbsp;&nbsp;&nbsp;$ppuptime = get_ppuptime($top_pid);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 由父进程ID确定变量文件路径前缀<br/>&nbsp;&nbsp;&nbsp;&nbsp;$path_pre = &quot;&#123;$process_file_dir&#125;mp_counter_&#123;$countername&#125;_pid_&#123;$top_pid&#125;_&quot;;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 由于系统启动时间和当前父进程启动时间(jiffies格式)确定计数使用的文件<br/>&nbsp;&nbsp;&nbsp;&nbsp;$cur_path = &quot;&#123;$path_pre&#125;btime_&#123;$sysuptime&#125;_ptime_&#123;$ppuptime&#125;&quot;;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 更新计数，先锁定<br/>&nbsp;&nbsp;&nbsp;&nbsp;$lock = sem_lock();<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if(!file_exists($cur_path))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 调试代码。个别系统上启动时间会变化，造成文件路径跟随变化，最终导致计数归0。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// $log = &quot;&#91;&#123;$time&#125;&#93; - &#123;$countername&#125;($cur_path) - init&#92;n&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// file_put_contents(&#039;/tmp/process.log&#039;, $log, FILE_APPEND);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$counter = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 理论上在这里，文件是一定存在的<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$counter = file_get_contents($cur_path);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 更新记数, 继续研究下判断init不能用==<br/>&nbsp;&nbsp;&nbsp;&nbsp;if($update === &#039;init&#039;)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果接收到更新值为init,或者变量文件不存在，则将计数初始化为0。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$new_counter = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;else&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$new_counter = $counter $update;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 写入计数，解锁<br/>&nbsp;&nbsp;&nbsp;&nbsp;file_put_contents($cur_path, $new_counter);<br/>&nbsp;&nbsp;&nbsp;&nbsp;sem_unlock($lock);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;return $new_counter;<br/>&#125;<br/><br/>/**<br/> * 创建多进程<br/> *<br/> * 1,通过mp_counter()函数实现进程间的任务协调<br/> * 2,由于PHP进程可能会由于异常而退出(主要是segment fault)，并且由于处理内存泄露的问题需要子进程主动退出，本函数可以实现自动建立<br/> * 新的进程，使子进程数量始终保持在$num的数量<br/> * 3,编译PHP时需要使用参数--enable-pcntl安装所需的模块<br/> * 4,如果在子进程中调用了exit(9)，那么主进程和所有子进程都将退出<br/> *<br/> * @param int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $num&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;进程数量<br/> * @param bool&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$stat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 结束后是否输出统计信息<br/> */<br/>function multi_process($num, $stat=FALSE)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;global $colors, $signals;<br/>&nbsp;&nbsp;&nbsp;&nbsp;extract($colors);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;if(empty($num))&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$num = 1;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&#125;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 记录进程数量，统计用<br/> mp_counter(&#039;process_num&#039;, &#039;init&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;mp_counter(&#039;process_num&#039;, $num);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 子进程数量<br/>&nbsp;&nbsp;&nbsp;&nbsp;$child = 0;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;// 任务完成标识<br/>&nbsp;&nbsp;&nbsp;&nbsp;$task_finish = FALSE;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;while(TRUE) &#123;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 清空子进程退出状态<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unset($status);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 如果任务未完成，并且子进程数量没有达到最高，则创建<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($task_finish == FALSE &amp;&amp; $child &lt; $num) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$pid = pcntl_fork();<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($pid) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 有PID，这里是父进程<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$child ;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 注册父进程的信号处理函数<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if($stat)&#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach ($signals as $signal =&gt; $name) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!pcntl_signal($signal, &quot;signal_handler&quot;)) &#123;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;die(&quot;Install signal handler for &#123;$name&#125; failed&quot;);<br/></div>
]]>
</description>
</item><item>
<link>http://www.dzhope.com/post//#blogcomment</link>
<title><![CDATA[[评论] PHP curl实现多进程并发高效率采集爬虫]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.dzhope.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>