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(); } }