用PHP CURL多线程保存远程文件/图片

以前就写过很多版本,这个版本算是比较满意的吧,主要特点:
- 利用CURL的多线程特点,多并发请求
- 过滤当前域下的文件
- 隐藏的文件类型会通过
HTTP Headers里面的Content-Type自动获取 - fwrite方式写入磁盘
核心函数
/**
* 文件多线程下载
* @param array 下载文件的数组列表
* @param string 文件保存的目录
* @param int 执行超时时间
*/
function downloadFile($array, $path, $timeout){
$data = array();
$mh = curl_multi_init();//创建多个curl语柄
foreach($array as $k=>$url){
$conn[$k] = curl_init($url);
curl_setopt($conn[$k], CURLOPT_TIMEOUT, $timeout);//设置超时时间
curl_setopt($conn[$k], CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36');
curl_setopt($conn[$k], CURLOPT_MAXREDIRS, 7);//HTTp定向级别
curl_setopt($conn[$k], CURLOPT_HEADER, 0);//这里不要header,加块效率
curl_setopt($conn[$k], CURLOPT_FOLLOWLOCATION, 1); // 302 redirect
curl_setopt($conn[$k], CURLOPT_RETURNTRANSFER, 1);
curl_setopt($conn[$k], CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($conn[$k], CURLOPT_SSL_VERIFYHOST, 0);
curl_multi_add_handle ($mh, $conn[$k]);
}
// 执行批处理句柄
$active = null;
do{
$mrc = curl_multi_exec($mh,$active);//当无数据,active=true
}while($mrc == CURLM_CALL_MULTI_PERFORM);//当正在接受数据时
while($active && $mrc == CURLM_OK){//当无数据时或请求暂停时,active=true
do{
$mrc = curl_multi_exec($mh, $active);
}while($mrc == CURLM_CALL_MULTI_PERFORM);
}
foreach ($array as $k => $url) {
curl_error($conn[$k]);
$res[$k] = curl_multi_getcontent($conn[$k]);//获得返回信息
$header[$k] = curl_getinfo($conn[$k]);//返回头信息
$ext = pathinfo($url, PATHINFO_EXTENSION);
if(!$ext)
{
$contentType = $header[$k]['content_type'];
$pattern = '/[a-zA-Z]+\/([a-zA-Z]+)/';
preg_match($pattern, $contentType, $matches);
$ext = $matches[1];
}
if($ext){
//Save File to locally
$path_to_file = $path . md5($url) . ".{$ext}";
// Option #1:
//file_put_contents($path_to_file, $res[$k]);
// Opticon #2:
$saver = fopen($path_to_file, "w+");
fwrite($saver, $res[$k]);
fclose($saver);
$data['files'][] = realpath($path_to_file);
}
curl_close($conn[$k]);//关闭语柄
curl_multi_remove_handle($mh, $conn[$k]);//释放资源
}
curl_multi_close($mh);
//return $res;
return $data;
} 以为图片下载为例
/**
* 下载远程图片到本地
*
* @param string $txt 用户输入的文字,可能包含有图片的url
* @return string
*/
function getRemoteFile($txt)
{
//1:提取所有html文本中的img标签,将数据集存放于$matches中
$meta = '<meta charset="utf-8">';
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHTML($meta . $txt);
libxml_use_internal_errors(false);
$xpath = new DOMXPath($doc);
$nodelist = $xpath->query("//img"); // find your image
foreach ($nodelist as $key => $img) {
$matches[] = $img->getAttribute('src');
}
$matches = array_unique($matches); //去重
//2:过滤本地图片,只需要远程文件,将数据集存放于$images中
foreach ($matches as $url) {
//规范化URL,使用"//"的引用方式在前面加上http
if(substr($url, 0, 2 ) == '//') $url = "http:" . $url;
//如果URL不是HTTP绝对地址或者是当前域的HTTP则排除
if(substr($url, 0, 4 ) != 'http' || strpos($url, $_SERVER["HTTP_HOST"])) continue;
//if($ext = getRemoteFileExt($url)){
$files[] = $url;
//}
}
//设置图片存放目录
$path = "./images/";
var_dump(downloadFile($files, $path, 10));
}使用方法
$html = '<div class="entry-content">
<p>这次Bill Hartzer客户被陷害的过程大致是这样:</p>
<img src="https://www.seozac.com/wp-content/uploads/2018/04/negative-canonical.png">
<ul>
<li>客户网站是A,有不错排名。</li>
<li>某黑帽手里有垃圾网站B,通常是被惩罚的,被黑的,或者有大量垃圾内容,或者有大量垃圾外链。</li>
<li>把A网站页面head部分完整抄到B网站页面上,然后B网站页面上加上(或修改)canonical标签指向A网站页面。</li>
<li>Google看到B网站页面有canonical标签指向A,把B和A网站合并处理,B网站被惩罚的信号被传递到A网站。</li>
<li>A网站排名下降。</li>
</ul>
<img src="https://s7d5.scene7.com/is/image/Specialized/146773?$hd$" alt="S-WORKS">
<p>这个方法害人之处在于,很难被检测到。通常,负面SEO还是会留下蛛丝马迹的:</p>
<ul>
<li>制造垃圾链接,在外链数据里能看到</li>
<li>黑进别人网站加垃圾内容、加黑链,在页面或源代码里能看到</li>
<li>即使做了cloaking,正常页面看不到垃圾内容,也能在搜索引擎快照看到</li>
<li>攻击别人网站,这个当然很快就知道了</li>
<li>抄袭内容、制造镜像网站,在搜索引擎搜索页面上的文字就能翻出来</li>
<li>被刷跳出率等用户体验数据,流量统计后台有显示</li>
</ul>
<a href="http://www.pdf995.com/samples/pdf.pdf">This is a pdf file</a>
<p>总之,发现排名和流量骤降,如果是被人负面SEO了,仔细检查,一般会发现什么地方被做了手脚。但Bill Hartzer描述的这个方法很可能不会留下任何蛛丝马迹:</p>
<ul>
<li>没有涉及链接</li>
<li>没有被攻击、被黑,被陷害网站代码没问题</li>
<li>没有抄袭内容,垃圾网站B只是抄head部分,页面可见内容可以是空的,或者是与A网站完全无关的内容</li>
</ul>
<p><img class="aligncenter size-full wp-image-3725" src="https://trek.scene7.com/is/image/TrekBicycleProducts/EmondaSLR9DH2Etap_24145_A_Primary?wid=1360&hei=1020&fmt=pjpeg&qlt=40,1&iccEmbed=0&cache=on,on" alt="利用canonical标签陷害竞争对手" width="271" height="189"></p>
</div>';
getRemoteFile($html);