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

后端开发PHP

用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);

Post Comment