PHP獲取頁面中圖片並下載保存

啦啦啦,本週工作日的最後一天,我來冒個泡

預備
要了解CURL的使用:參考一:我自己總結的curl的使用
參考二:CURL手冊
參考三:匹配查找

一、背景,原因
今天其實沒有特別的事情,突然想起來前端時間朋友的網店,因爲供應商不給提供圖片數據包,只能一張一張的保存,然後上傳,我就覺着我試試獲取網站的圖片吧(支持獲取https協議網站);

二、漫漫請求路
這個實現就是獲取網站的信息,那麼首先映入腦海的萬能的CURL方法,不瞭解的可以點擊去學習一下。這裏很重要,只有抓取到頁面數據你才能獲取到想要的東西並保存下來;

首先,獲取頁面數據:
這個封裝的方法也是支持抓取http、https兩種協議的網站頁面信息

public function is_request($url, $ssl = true, $type = "GET", $data = null)
    {
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, $url);
        $user_agent = isset($_SERVER["HTTP_USERAGENT"]) ?
            $_SERVER['HTTP_USERAGENT'] :
            'Mozilla / 5.0 (Windows NT 6.1; WOW64) AppleWebKit / 537.36 (KHTML, like Gecko) Chrome / 50.0.2661.102 Safari / 537.36';
            curl_setopt($curl, CURLOPT_USERAGENT, $user_agent);//請求代理信息
            curl_setopt($curl, CURLOPT_AUTOREFERER, true);//referer頭 請求來源
            curl_setopt($curl, CURLOPT_TIMEOUT, 30);//請求超時
            curl_setopt($curl, CURLOPT_HEADER, false);//是否處理響應頭
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);//curl_exec()是否返回響應
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);

        if ($ssl) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//禁用後curl將終止從服務端進行驗證
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);//檢查服務器ssl證書中是否存在一個公用名(common name)

        }
        if ($type == 'POST') {
            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }

        //發出請求
        $response = curl_exec($curl);
        if ($response === false) {
            return false;
        }
        return $response;
    }

其次,就是通過正則匹配,獲取頁面中所有的滿足你條件的字符串組:

		$img_pattern = "|<img[^>]+src=['\" ]?([^ '\"?]+)['\" >]|U";
        preg_match_all($img_pattern, $content, $img_out, PREG_SET_ORDER);

最後嘍,你都獲取到了頁面中所有的img標籤,那就可以循環保存在自己指定的文件中嘍:

/**
     * 保存單個圖片的方法
     *
     * @param String $capture_url   用於抓取圖片的網頁地址
     * @param String $img_url       需要保存的圖片的url
     *
     */
    public function save_one_img($capture_url,$img_url)
    {
        //圖片路徑地址
//        if ( strpos($img_url, 'http://')!==false )
//        {
//            // $img_url = $img_url;
//        }else
//        {
//            $domain_url = substr($capture_url, 0,strpos($capture_url, '/',8)+1);
//            $img_url=$domain_url.$img_url;
//        }
        $pathinfo = pathinfo($img_url);    //獲取圖片路徑信息
        $pic_name=$pathinfo['basename'];   //獲取圖片的名字
        if (file_exists($this->save_path.$pic_name))  //如果圖片存在,證明已經被抓取過,退出函數
        {
            echo $img_url . '<span style="color:red;margin-left:80px">該圖片已經抓取過!</span><br/>';
            return;
        }
        //將圖片內容讀入一個字符串
//        dd($img_url);
//        info($img_url);
        $img_data = @file_get_contents($img_url);   //屏蔽掉因爲圖片地址無法讀取導致的warning錯誤

        if ( strlen($img_data) > $this->img_size )   //下載size比限制大的圖片
        {
            $img_size = file_put_contents($this->save_path . $pic_name, $img_data);
            if ($img_size)
            {
                echo $img_url . '<span style="color:green;margin-left:80px">圖片保存成功!</span><br/>';
            } else
            {
                echo $img_url . '<span style="color:red;margin-left:80px">圖片保存失敗!</span><br/>';
            }
        } else
        {
            echo $img_url . '<span style="color:red;margin-left:80px">圖片讀取失敗!</span><br/>';
        }
    }

當然,爲了避免重複下載,我判斷了是否已經下載過,下面的完整的項目代碼:BaseService主要是爲了引入is_request()

三、重中之重,完整的代碼

class GetImgController extends Controller
{
    public $save_path;                  //抓取圖片的保存地址

    //抓取圖片的大小限制(單位:字節) 只抓比size比這個限制大的圖片
    public $img_size=0;

    //定義一個靜態數組,用於記錄曾經抓取過的的超鏈接地址,避免重複抓取
    public static $a_url_arr=array();
    protected $baseService ;

    /**
     * @param BaseService $baseService
     */
    public function __construct(BaseService $baseService)
    {
        $this->save_path=public_path().'/uploadfile/curl_img/chumaoqi/lunbo/';
        $this->img_size=0;
        $this->baseService = $baseService;
    }


    /**
     * 遞歸下載抓取首頁及其子頁面圖片的方法  ( recursive 遞歸)
     *
     * @param Request $request
     *
     */
    public function recursive_download_images(Request $request)
    {
        $capture_url = $request->input('url');
        if (!in_array($capture_url,self::$a_url_arr))   //沒抓取過
        {
            self::$a_url_arr[]=$capture_url;   //計入靜態數組
        } else   //抓取過,直接退出函數
        {
            return;
        }

        $this->download_current_page_images($capture_url);  //下載當前頁面的所有圖片

        //用@屏蔽掉因爲抓取地址無法讀取導致的warning錯誤
        $content=@file_get_contents($capture_url);

        //匹配a標籤href屬性中?之前部分的正則
        $a_pattern = "|<a[^>]+href=['\" ]?([^ '\"?]+)['\" >]|U";
        preg_match_all($a_pattern, $content, $a_out, PREG_SET_ORDER);

        $tmp_arr=array();  //定義一個數組,用於存放當前循環下抓取圖片的超鏈接地址
        foreach ($a_out as $k => $v)
        {
            /**
             * 去除超鏈接中的 空'','#','/'和重複值
             * 1: 超鏈接地址的值 不能等於當前抓取頁面的url, 否則會陷入死循環
             * 2: 超鏈接爲''或'#','/'也是本頁面,這樣也會陷入死循環,
             * 3: 有時一個超連接地址在一個網頁中會重複出現多次,如果不去除,會對一個子頁面進行重複下載)
             */
            if ( $v[1] && !in_array($v[1],self::$a_url_arr) &&!in_array($v[1],array('#','/',$capture_url) ) )
            {
                $tmp_arr[]=$v[1];
            }
        }

        foreach ($tmp_arr as $k => $v)
        {
            //超鏈接路徑地址
            if ( strpos($v, 'http://')!==false ) //如果url包含http://,可以直接訪問
            {
                $a_url = $v;
            }else   //否則證明是相對地址, 需要重新拼湊超鏈接的訪問地址
            {
                $domain_url = substr($capture_url, 0,strpos($capture_url, '/',8)+1);
                $a_url=$domain_url.$v;
            }

            $this->recursive_download_images($a_url);

        }

    }


    /**
     * 下載當前網頁下的所有圖片
     *
     * @param   String  $capture_url  用於抓取圖片的網頁地址
     * @return  Array   當前網頁上所有圖片img標籤url地址的一個數組
     */
    public function download_current_page_images($capture_url)
    {
//        $capture_url = $request->input('url');

        $content = $this->baseService->is_request($capture_url);

        //匹配img標籤src屬性中?之前部分的正則
        $img_pattern = "|<img[^>]+src=['\" ]?([^ '\"?]+)['\" >]|U";
        preg_match_all($img_pattern, $content, $img_out, PREG_SET_ORDER);

        $photo_num = count($img_out);
        //匹配到的圖片數量
        echo '<h1>'.$capture_url . "共找到 " . $photo_num . " 張圖片</h1>";
        foreach ($img_out as $k => $v)
        {
            $this->save_one_img($capture_url,$v[1]);
        }
    }


    /**
     * 保存單個圖片的方法
     *
     * @param String $capture_url   用於抓取圖片的網頁地址
     * @param String $img_url       需要保存的圖片的url
     *
     */
    public function save_one_img($capture_url,$img_url)
    {
        //圖片路徑地址
//        if ( strpos($img_url, 'http://')!==false )
//        {
//            // $img_url = $img_url;
//        }else
//        {
//            $domain_url = substr($capture_url, 0,strpos($capture_url, '/',8)+1);
//            $img_url=$domain_url.$img_url;
//        }
        $pathinfo = pathinfo($img_url);    //獲取圖片路徑信息
        $pic_name=$pathinfo['basename'];   //獲取圖片的名字
        if (file_exists($this->save_path.$pic_name))  //如果圖片存在,證明已經被抓取過,退出函數
        {
            echo $img_url . '<span style="color:red;margin-left:80px">該圖片已經抓取過!</span><br/>';
            return;
        }
        //將圖片內容讀入一個字符串
//        dd($img_url);
//        info($img_url);
        $img_data = @file_get_contents($img_url);   //屏蔽掉因爲圖片地址無法讀取導致的warning錯誤

        if ( strlen($img_data) > $this->img_size )   //下載size比限制大的圖片
        {
            $img_size = file_put_contents($this->save_path . $pic_name, $img_data);
            if ($img_size)
            {
                echo $img_url . '<span style="color:green;margin-left:80px">圖片保存成功!</span><br/>';
            } else
            {
                echo $img_url . '<span style="color:red;margin-left:80px">圖片保存失敗!</span><br/>';
            }
        } else
        {
            echo $img_url . '<span style="color:red;margin-left:80px">圖片讀取失敗!</span><br/>';
        }
    }
}

其中recursive_download_images()是遞歸下載指定頁面以及子頁面下的圖片,download_current_page_images()只是下載指定地址頁面的圖片,你可以稍作調整,從而滿足你的需求。

四、寫在最後
網絡有很多這種curl請求獲取頁面數據的,大家都可以參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章