1. 程式人生 > >PHP使用CURL抓取網頁

PHP使用CURL抓取網頁

CURL是一個非常強大的開源庫,支援很多協議,包括HTTP、FTP、TELNET等,我們使用它來發送HTTP請求。它給我 們帶來的好處是可以通過靈活的選項設定不同的HTTP協議引數,並且支援HTTPS。CURL可以根據URL字首是“HTTP” 還是“HTTPS”自動選擇是否加密傳送內容。

使用CURL的PHP擴充套件完成一個HTTP請求的傳送一般有以下幾個步驟:

    初始化連線控制代碼;
    設定CURL選項;
    執行並獲取結果;
    釋放CURL連線控制代碼。

一、使用curl模擬GET請求

$curl=curl_init();  //初始化curl控制代碼
$url="http://www.conglinfeng.com/together/detail.php?show_ID=5";  //要請求的url地址
curl_setopt($curl, CURLOPT_URL,$url);  //設定curl的引數,即要請求的url是$url
curl_exec($curl);  //執行操作
curl_close($curl);  //關閉控制代碼

 執行curl_exec()時,成功時會輸出網頁程式碼,並且返回值為 TRUE,在失敗時返回 FALSE。 然而,如果 CURLOPT_RETURNTRANSFER選
項被設定,函式執行成功時會返回執行的結果,失敗時返回 FALSE 。

二、使用curl模擬post請求

 

$curl=curl_init();
$url="./register.php";
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_POST, true);  //設定請求為post
$post_data=array('username'=>"嘿嘿",'password'=>'111111','confirm'=>'111111',
	'email'=>"[email protected]");   //要傳送的資料組裝成一個數組
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);  //post的資料內容
curl_exec($curl); 
curl_close($curl);

 

 register.php:

var_dump($_POST);

 三、處理響應的資料

curl_exec()執行時會直接把響應資料輸出,如果不需要直接輸出,可以加:curl_setopt($curl, CURLOPT_RETURNTRANSFER, true) ;

 

$url="./register.php";
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_POST, true);  //設定請求為post
$post_data=array('username'=>"嘿嘿",'password'=>'111111','confirm'=>'111111',
	'email'=>"[email protected]");
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);  //post的資料內容
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true) ; //請求執行時,不將響應資料直接輸出,而是以返回值的形式輸出響應資料
$res=curl_exec($curl); 
echo $res;  //少了這句就輸不出來了
curl_close($curl);

 

 四、模擬post 檔案上傳

$url="./register.php";
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_POST, true);  //設定請求為post
$post_data=array('logo'=>'@D:\wamp\wamp\www\czbk\php&mysql\1.png'); // logo是$_FILES的name,後面的是圖片路徑,加@表示這是一個檔案而不是字串
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);  //post的資料內容
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true) ; //請求執行時,不將響應資料直接輸出,而是以返回值的形式輸出響應資料
$res=curl_exec($curl); 
echo $res; 
curl_close($curl);

 五、輸出響應頭

curl預設是不輸出響應頭的,如果要輸出,則要加:curl_setopt($curl, CURLOPT_HEADER, true); 

 

$url="http://www.conglinfeng.com/together/member/index.php";
curl_setopt($curl, CURLOPT_URL,$url);
curl_setopt($curl, CURLOPT_HEADER, true);  //將響應頭輸出,預設是不輸出的
//  HTTP/1.1 200 OK Date: Thu, 15 Sep 2016 14:49:28 GMT Server: Apache/2.4.4 (Win32) PHP/5.4.16 X-Powered-By: PHP/5.4.16 Content-Length: 692 Content-Type: text/html
curl_exec($curl); 
curl_close($curl);

 

 六、例項:CURL模擬登陸

可以簡單和有效地抓取網頁並採集內容,設定cookie完成模擬登入網頁,curl提供了豐富的函式,開發者可以從PHP手冊中獲取更多關於cURL資訊。本文以模擬登入開源中國(oschina)為例,和大家分享cURL的使用。

PHP的curl()在抓取網頁的效率方面是比較高的,而且支援多執行緒,而file_get_contents()效率就要稍低些,當然,使用curl時需要開啟下curl擴充套件。

先來看登入部分的程式碼:

//模擬登入 
function login_post($url, $cookie, $post) { 
    $curl = curl_init();//初始化curl模組 
    curl_setopt($curl, CURLOPT_URL, $url);//登入提交的地址 
    curl_setopt($curl, CURLOPT_HEADER, 0);//是否顯示頭資訊 
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 0);//是否自動顯示返回的資訊 
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie); //設定Cookie資訊儲存在指定的檔案中 
    curl_setopt($curl, CURLOPT_POST, 1);//post方式提交 
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));//要提交的資訊 
    curl_exec($curl);//執行cURL 
    curl_close($curl);//關閉cURL資源,並且釋放系統資源 
} 

 函式login_post()首先初始化curl_init(),然後使用curl_setopt()設定相關選項資訊,包括要提交的url地址,儲存的cookie檔案,post的資料(使用者名稱和密碼等資訊),是否返回資訊等等,然後curl_exec執行curl,最後curl_close()釋放資源。注意PHP自帶的http_build_query()可以將陣列轉換成相連線的字串。

接下來如果登入成功後,我們要獲取登入成功後的頁面資訊。

//登入成功後獲取資料 
function get_content($url, $cookie) { 
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url); 
    curl_setopt($ch, CURLOPT_HEADER, 0); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie); //讀取cookie 
    $rs = curl_exec($ch); //執行cURL抓取頁面內容 
    curl_close($ch); 
    return $rs; 
} 

 函式get_content()中也是先初始化curl,然後設定相關選項,執行curl,釋放資源。其中我們設定CURLOPT_RETURNTRANSFER為1即自動返回資訊,而CURLOPT_COOKIEFILE可以讀取到登入時儲存的cookie資訊,最後將頁面內容返回。

我們的最終目的是要獲取到模擬登入後的資訊,也就是隻有正常登入成功後才能獲取的有用資訊。接下來我們以登入開源中國的移動版為例,看看如何抓取到登入成功後的資訊。

//設定post的資料 
$post = array ( 
    'email' => 'oschina賬戶', 
    'pwd' => 'oschina密碼', 
    'goto_page' => '/my', 
    'error_page' => '/login', 
    'save_login' => '1', 
    'submit' => '現在登入' 
); 
 
//登入地址 
$url = "http://m.oschina.net/action/user/login"; 
//設定cookie儲存路徑 
$cookie = dirname(__FILE__) . '/cookie_oschina.txt'; 
//登入後要獲取資訊的地址 
$url2 = "http://m.oschina.net/my"; 
//模擬登入 
login_post($url, $cookie, $post); 
//獲取登入頁的資訊 
$content = get_content($url2, $cookie); 
//刪除cookie檔案 
@ unlink($cookie); 
//匹配頁面資訊 
$preg = "/<td class='portrait'>(.*)<\/td>/i"; 
preg_match_all($preg, $content, $arr); 
$str = $arr[1][0]; 
//輸出內容 
echo $str; 

 七、封裝CURL

為了便於日後呼叫,我們可以把這些操作封裝起來。

1.模擬get或post請求

/**
 * curl()   curl模擬請求---一個引數是get請求,兩個引數是post請求
 *
 * 上傳檔案$post=array('logo'=>'@D:\wamp\wamp\www\czbk\php&mysql\1.png'); 
 * logo是$_FILES的name,後面的是圖片路徑,加@表示這是一個檔案而不是字串
 *
 * @param string $url   模擬請求的url
 * @param array $post   post請求時要提交的資料
 * @param boolean $header  是否要將響應頭輸出
 * @return string $str   返回響應結果
 */
function curl($url,$post=array(),$header=false){
  if(!$url)   return;
 
  //設定資源控制代碼
  $curl=curl_init();
  curl_setopt($curl, CURLOPT_URL,$url);
 
  //如果傳$post,則說明是post請求
  if($post && is_array($post) && count($post)>0){
       curl_setopt($curl, CURLOPT_POST, 1);
       curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
       curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //不驗證證書
       curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); //不驗證證書
  }
 
  //請求執行時,不將響應資料直接輸出,而是以返回值的形式輸出響應資料
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
 
  //決定要不要將響應頭輸出
  curl_setopt($curl, CURLOPT_HEADER,$header); 
  $str=curl_exec($curl); 
  
  //IGNORE 忽略轉換時的錯誤,如果沒有ignore引數,所有該字元後面的字串都無法被儲存。
  $str = iconv("UTF-8","GBK//IGNORE",$str);
  curl_close($curl);
 
  return $str;
}

 2.模擬登陸

/**
 * curl_login()   curl模擬登陸
 *
 * @param string $logUrl   登陸地址url
 * @param string $desUrl   要訪問頁面的url
 * @param array $post    要提交的資料
 * @param string $cookie=''  儲存cookie的檔案路徑
 * @return string $str   返回響應結果
 */
function curl_login($logUrl,$desUrl,$post,$cookie=''){
 /********模擬登陸**********/
    //初始化curl模組 
    $curl = curl_init();
    //登入提交的地址 
    curl_setopt($curl, CURLOPT_URL,$logUrl);
    //是否顯示頭資訊
    curl_setopt($curl, CURLOPT_HEADER, 0); 
    //是否自動顯示返回的資訊 
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 0);
    //設定Cookie資訊儲存在指定的檔案中 
    if(!$cookie)  $cookie=dirname(__FILE__) . '/cookie.txt';
    if(!file_exists($cookie))}{
      	$fp=fopen($cookie, 'w'); fclose($fp);
    }
    curl_setopt($curl, CURLOPT_COOKIEJAR, $cookie); 
    //post方式提交 
    curl_setopt($curl, CURLOPT_POST, 1);
    //提交資訊,http_build_query()可以將陣列轉換成相連線的字串。
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post)); 
    //執行cURL並關閉cURL資源,並且釋放系統資源 
    curl_exec($curl);
    curl_close($curl);
 /********登陸後獲取資料**********/
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $desUrl); 
    curl_setopt($ch, CURLOPT_HEADER, 0); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    //讀取cookie 
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie); 
    $rs = curl_exec($ch); //執行cURL抓取頁面內容 
    curl_close($ch); 
    return $rs; 
}

 也可以把curl的操作封裝成一個類 Curl.class.php

<?php
 
/**
 *  CURL操作類,可用於模擬請求
 *  @author leshen <[email protected]>
 *  @version 1.1
 *  usage:
 *	1.設定選項
 *  $options=array(
 *    'isReturn'=>true,   //將響應結果返回。如果不想獲取原始碼而是想渲染頁面,請設定為false
 *	  'isHeader'=>true,   //將響應頭返回
 *   );
 *  $curl=new Curl($options);
 *  2.模擬一般的get請求
 *  $curl=new Curl();
 *  $url="https://www.baidu.com";
 *  var_dump($curl->curl_get($url));
 *  3.模擬需要盜鏈的get請求(查詢四六級為例)
 *  $curl=new Curl();
 *  $url='http://www.chsi.com.cn/cet/query';
 *  $referer="http://www.chsi.com.cn/cet";
 *  $data=array('xm'=>'鍾林生','zkzh'=>'360021161218718');
 *  $respn=$curl->curl_get_chain($url,$data,$referer);
 *  var_dump($respn);
 *  4.模擬post請求(查詢四六級為例)
 *	 $data=array(
 *	 'name'=>'王勇平',
 *	 'province'=>'江西',
 *	 'school'=>'江西師範大學',
 *	 'type'=>'1'
 *	);
 *	$url='http://cet.zy62.com/query/2';
 *	$curl=new Curl();
 *	var_dump($curl->curl_post($url,$data));
 */
 
class Curl{
 
	/**
     * curl資源控制代碼
     * @var resource
     */
    private $curl;
 
    /*curl選項*/
    private $isReturn;   //是否將響應結果返回
    private $isHeader;   //是否將響應頭返回
    private $timeout;    //超時時間
    private $userAgent;  //客戶端代理
    private $verifyPeer; //是否終止cURL從服務端進行驗證
    private $verifyHost; //檢查伺服器SSL證書中是否存在一個公用名
 
    /**
     * 構造方法,用於例項化一個curl物件
     */
    public function __construct($options=array()){
        /*初始化資源控制代碼*/
        $this->curl=curl_init();
        /*初始化curl選項*/
        $this->isReturn=isset($options['isReturn'])?$options['isReturn']:ture;
        $this->isHeader=isset($options['isHeader'])?$options['isHeader']:false;
        $this->timeout=isset($options['timeout'])?$options['timeout']:30;
        $this->userAgent=isset($options['userAgent'])?$options['timeout']:
           isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']: 
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0 FirePHP/0.7.4';
        $this->verifyPeer=isset($options['verifyPeer'])?$options['verifyPeer']:false;
        $this->verifyHost=isset($options['verifyHost'])?$options['verifyHost']:2;
    }   
 
	/**
     * 設定curl選項
     * @param  string   $url  	請求的url
     * @param  boolean  $ssl 	是否以https協議傳輸
     * @return void 
     */	
    private function setOption($url,$ssl){
		/*設定curl選項*/
		curl_setopt($this->curl, CURLOPT_URL, $url);//URL
		curl_setopt($this->curl, CURLOPT_USERAGENT, $this->userAgent);//userAgent,請求代理資訊
		curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout);//設定超時時間
 
		/*SSL相關*/
		if ($ssl) {
			curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,$this->verifyPeer);//禁用後cURL將終止從服務端進行驗證
			curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,$this->verifyHost);//檢查伺服器SSL證書中是否存在一個公用名(common name)。
		}
 
	    /*響應結果*/
		curl_setopt($this->curl, CURLOPT_HEADER, $this->isHeader);//是否返回響應頭
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, $this->isReturn);//curl_exec()是否返回響應結果
    }
 
	/**
     * 執行curl請求
     * @return string  返回響應內容
     */	
    private function exec(){
		/*發出請求*/
		$response = curl_exec($this->curl);
		if (false === $response) {
			echo '<br>', curl_error($this->curl), '<br>';
			return false;
		}
		curl_close($this->curl);
		return $response;
    }
 
	/**
     * curl模擬post請求,返回響應的內容
     * @param  string   $url  		請求的url
     * @param  array    $data  		傳送的資料,陣列
     * @param  boolean  $ssl 		是否以https協議傳輸,預設為true
     * @return string   $response  	返回響應的內容
     */	
	public function curl_post($url, $data, $ssl=true) {
		/*設定選項*/
		$this->setOption($url,$ssl);
 
		/*處理post相關選項*/
		curl_setopt($this->curl, CURLOPT_POST, true);// 是否為POST請求
		curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data);// 設定post的內容
 
		/*執行curl請求*/
		if($this->isReturn){
			return $this->exec();
		}
		$this->exec();
	}
 
	/**
     * curl模擬一般的get請求,返回響應的內容
     * @param  string   $url  		請求的url,可以帶查詢引數
     * @param  boolean  $ssl 		是否以https協議傳輸,預設為true
     * @return string   $response  	返回響應的內容
     */	
	public function curl_get($url,$ssl=true) {
		/*設定選項*/
		$this->setOption($url,$ssl);
 
		/*執行curl請求*/
		if($this->isReturn){
			return $this->exec();
		}
		$this->exec();
	}	
 
	/**
     * curl模擬需要盜鏈的get請求,返回響應的內容
     * @param  string   $url  		請求的url
     * @param  array    $data  		查詢引數,陣列形式,方法內會自動轉換為字串形式
     * @param  string   $referer 	盜鏈的url
     * @param  boolean  $ssl 		是否以https協議傳輸,預設為true
     * @return string   $response  	返回響應的內容
     */	
	public function curl_get_chain($url,$data,$referer,$ssl=true){
		/*設定選項*/
		$this->setOption($url,$ssl);
		$param='';
		foreach ($data as $k => $v) {
			$param.= urlencode($k).'='.urlencode($v).'&';
		}
		/*設定查詢引數*/
    	curl_setopt($this->curl, CURLOPT_POST, 0);
  	    curl_setopt($this->curl, CURLOPT_POSTFIELDS, $param);
 
		/*設定referer盜鏈*/
		curl_setopt($this->curl, CURLOPT_REFERER, $referer);
 
  	    /*執行curl請求*/
		if($this->isReturn){
			return $this->exec();
		}
		$this->exec();
	}
}