對於作過數據採集的人來講,cURL必定不會陌生。雖然在PHP中有file_get_contents函數能夠獲取遠程連接的數據,可是它的可控制性太差了,對於各類複雜狀況的採集情景,file_get_contents顯得有點無能爲力。所以,本文將爲你介紹採集神器cURL的使用。php
先給你們補充一下file_get_contents函數能夠獲取遠程連接數據的方法。html
<?php $url = "http://git.oschina.net/yunluo/API/raw/master/notice.txt"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $notice = curl_exec($ch); echo $notice; ?>
這段代碼會直接使用curl顯示文件內容,可是問題來了,由於curl是php的擴展,有的主機爲了安全會金庸curl的,寧外php本地調試的時候也是關閉curl的,因此會發生報錯,因此這段代碼是不可取的,因此雲落對他從新改寫了ios
<?php if (function_exists('curl_init')) { $url = "http://git.oschina.net/yunluo/API/raw/master/notice.txt"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $dxycontent = curl_exec($ch); echo $dxycontent; } else { echo '汗!貌似您的服務器還沒有開啓curl擴展,沒法收到來自雲落的通知,請聯繫您的主機商開啓,本地調試請無視'; } ?>
修改後的版本是對curl擴展作一個判斷,看看服務器到底有木有打開curl擴展,若是打開了,就直接顯示文件,若是沒打開就顯示一段提示文字。
雖然修復了問題,可是又有一個問題來了,我只是顯示一段文字而已,我也不是是用什麼作什麼大事的,因此我爲何要寫那麼多的代碼呢??
通過一些瞎掰的檢測,發現file_get_contents獲取遠程文件內容的速度不比curl慢,在一些文件較少的狀況下可能還比curl擴展要快得多,因此我又重寫了代碼git
<?php echo file_get_contents( "http://git.oschina.net/yunluo/API/raw/master/notice.txt" ); ?>
工具
火狐瀏覽器(FireFox) + Firebug
「工欲善其事,必先利其器。」 在分析案例以前,先讓咱們學習一下如何利用神器Firebug獲取咱們必要的信息。
使用F12打開Firebug,咱們能夠獲得如圖(一)界面:web
一、箭頭圖標是「元素選擇」工具,單擊一次會高亮圖標,同時,鼠標在頁面內的移動會同時在HTML菜單中選定相應的內容,此時單擊內容則表示選定了該元素,圖標高亮取消。如圖(二)所示:
Firebug查看元素後端
二、控制檯
JS裏面的console.log系列函數的打印就是在這裏輸出。
三、HTML
HTML內容,注意這裏看到的不必定是採集要解析的內容,採集時候對內容的分析,一概以查看源碼(Ctrl+U)爲準,這裏只是能快速定位元素的結構,而後再選擇一個比較特殊的參照,在源碼中定位相應的位置。
好比,你在HTML裏面看到一個標籤是<div id="demo" class="demo">Demo</div>,可是你查看源碼時候看到的內容多是<div class="demo" id="demo">Demo</div>,若是你對採集內容按照前者去作正則匹配,那麼你會得不到結果。
四、CSS
這裏是CSS文件內容
五、腳本
這裏是Javascript文件內容
六、DOM
Dom節點內容
七、網絡
每個請求連接的數據,這裏是咱們採集要關注和分析的地方,它可以顯示每個請求的參數、請求頭、Cookie數據等。在頁面提交會刷新的狀況下,須要使用保持,使得頁面請求內容在刷新後仍然留着控制檯中,如圖(三)所示:數組
另外,火狐還有一款 Tamper data 擴展也能獲得請求數據,必要時能夠安裝使用。
八、Cookies
Cookie數據瀏覽器
在圖(一)中還看到下面有不少可選的小菜單項,其中保持是咱們要關注的,當選擇它的時候,即便提交表單刷新了頁面,下面內容區域的數據仍是會保留,這個對於分析提交數據特別關鍵。安全
總結
咱們在分析採集請求的時候,主要關心「網絡」菜單裏的請求數據,必要時候使用「保持」以查看刷新頁面的請求數據,請求前可使用「清除」先清除下面的內容。服務器
案例解析
1、簡單的採集
這裏所指的簡單採集,是指一個單一頁面GET請求的採集,它簡單得即便經過file_get_contents函數也能輕鬆得到頁面返回結果。
代碼片斷之file_get_contents
<?php $url = 'http://demo.zjmainstay.cn/php/curl/simple.html'; $content = file_get_contents($url); echo $content;
代碼片斷之cURL
<?php $url = 'http://demo.zjmainstay.cn/php/curl/simple.html'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); echo $content;
2、須要參數的採集
這種狀況,頁面請求須要傳入一些參數,能夠是GET請求,也能夠是POST請求。這種狀況的採集,使用file_get_contents外帶一些參數仍是能夠實現的,可是這裏咱們將再也不展現。
代碼片斷之cURL GET
這種請求,咱們能夠選擇搜索引擎做爲演示,好比我百度搜索一個詞語「PHP cURL」,在輸入回車後,咱們會獲得一個相似http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=PHP%20cURL的連接,注意這裏的連接可能不一樣瀏覽器、不一樣入口方式訪問獲得不同結果,所以沒必要介意連接是否同樣。經過輸入多個關鍵詞並觀察連接,咱們能夠肯定 wd 參數就是咱們要傳入的動態參數,而其餘參數則能夠不變,所以獲得咱們下面的採集代碼。
<?php $keyword = 'PHP cURL'; $url = 'http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=' . urlencode($keyword); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); echo $content;
有些時候,一些參數並非必須的,這時候咱們能夠刪掉它,好比上面的連接能夠只保留http://www.baidu.com/s?ie=utf-8&wd=PHP%20cURL,ie=utf-8 這個參數可能影響結果的編碼,因此暫且留着它。就這樣簡單的代碼,咱們就能夠採集到百度搜索的結果了。
代碼片斷之cURL POST
對於POST類型的請求,咱們平時並很多見,好比有些搜索就是使用POST方式提交,這時候咱們就須要使用POST類型來提交參數了。這個在PHP cURL裏面有相應的參數:CURLOPT_POST 和 CURLOPT_POSTFIELDS , CURLOPT_POST 的設置能夠指定當前提交是否爲POST方式,CURLOPT_POSTFIELDS則用於設定提交的參數,能夠是參數串,也能夠是參數數組,好比:
curl_setopt($ch, CURLOPT_POSTFIELDS, 'ie=utf-8&wd=PHP%20cURL'); 或 curl_setopt($ch, CURLOPT_POSTFIELDS, array( 'ie' => 'utf-8', 'wd' => 'PHP%20cURL', ));
下面是我作的一個POST模擬搜索PHP POST 搜索,後端是使用了前面的百度關鍵詞搜索,基本原理就是,客戶端提交一個關鍵詞到我服務器,我服務器使用該關鍵詞請求百度的搜索,而後獲得結果,返回到客戶端。
如圖(四)是利用Firebug對請求數據的分析,獲得咱們須要提交的請求連接和請求參數:
而後下面是咱們的代碼:
<?php $keyword = 'PHP cURL'; //參數方法一 // $post = 'wd=' . urlencode($keyword); //參數方法二 $post = array( 'wd' => urlencode($keyword), ); $url = 'http://demo.zjmainstay.cn/php/curl/search.php'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據 curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); var_dump($content);
3、須要Referer的採集
對於一些程序,它可能判斷來源網址,若是發現referer不是本身的網站,則拒絕訪問,這時候,咱們就須要添加CURLOPT_REFERER參數,模擬來路,使得程序可以正常採集。
<?php $keyword = 'PHP cURL'; //參數方法一 // $post = 'wd=' . urlencode($keyword); //參數方法二 $post = array( 'wd' => urlencode($keyword), ); $url = 'http://demo.zjmainstay.cn/php/curl/search_refer.php'; $refer = 'http://demo.zjmainstay.cn/'; //來路地址 $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 curl_setopt($ch, CURLOPT_REFERER, $refer); //來路模擬 curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據 curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); var_dump($content);
search_refer.php的源碼以下,作了簡單的Referer判斷攔截:
<?php if(empty($_POST['wd'])) { exit('Deny empty params.'); } //Referer判斷 if(stripos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) { exit('Deny'); } $keyword = addslashes(trim(strip_tags($_POST['wd']))); $url = 'http://www.baidu.com/s?ie=utf-8&wd=' . urlencode($keyword); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); echo $content;
4、須要cookie支持的採集
對於模擬登陸的應用,單單提交參數和模擬來路並不能解決問題,這時候咱們就須要保存或者提交相應的Cookie參數,這個在PHP cURL裏面也提供了相應的參數:
CURLOPT_COOKIE: 直接使用字符串方式提交cookie參數
CURLOPT_COOKIEFILE: 使用文件方式提交cookie參數
CURLOPT_COOKIEJAR: 保存提交後反饋的cookie數據
下面是PHP100的模擬登陸示例:
<?php header("content-Type: text/html; charset=UTF-8"); $cookie_file = tempnam('./temp', 'cookie'); $login_url="http://bbs.php100.com/login.php"; $post_fields="cktime=36000&step=2&pwuser=username&pwpwd=password"; //提交登陸表單請求 $ch=curl_init($login_url); curl_setopt($ch,CURLOPT_HEADER,0); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch,CURLOPT_POST,1); curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields); curl_setopt($ch,CURLOPT_COOKIEJAR,$cookie_file); //存儲提交後獲得的cookie數據 curl_exec($ch); curl_close($ch); //登陸成功後,獲取bbs首頁數據 $url="http://bbs.php100.com/index.php"; $ch=curl_init($url); curl_setopt($ch,CURLOPT_HEADER,0); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch,CURLOPT_COOKIEFILE,$cookie_file); //使用提交後獲得的cookie數據作參數 $contents=curl_exec($ch); curl_close($ch); //轉碼顯示 echo iconv('gbk', 'UTF-8', $contents);
5、壓縮網頁採集(gzip)
有些沒有接觸過壓縮頁面的朋友估計會在這裏被坑死,由於他們會發現採集回來的內容是亂碼,而且不管使用iconv仍是強大的mb_convert_encoding都沒法還原數據,而後又沒有概念,各類抓狂卻找不到方法,哈哈,我曾經也是這樣~
如圖(五)是亂碼錶現形式:
還好最後功夫不負有心人,仍是找到了,它就是CURLOPT_ENCODING參數。
好比,採集搜狐的新聞時候就遇到gzip壓縮問題,下面是示例:
<?php $url = 'http://news.sohu.com/'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip壓縮 $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); echo $content;
手冊說明:支持的編碼有"identity","deflate"和"gzip"。若是爲空字符串"",請求頭會發送全部支持的編碼類型。
後面一句代表,使用curl_setopt($ch, CURLOPT_ENCODING, "");也是能夠的,可是不能不加這個參數。
6、SSL連接的採集
有些請求連接是https類型的,這時候使用cURL採集可能會失敗,這時候,咱們可使用 var_dump(curl_error($ch));的方法打印錯誤提示,而後根據錯誤提示查找相應的解決方案。好比SSL錯誤常見提示:SSL certificate problem: unable to get local issuer certificate,這時候,咱們就須要利用參數:CURLOPT_SSL_VERIFYPEER 和 CURLOPT_SSL_VERIFYHOST 來禁用SSL證書的驗證,我嘗試過只使用CURLOPT_SSL_VERIFYPEER參數禁用失敗,因此你們最好同時使用兩個參數。
下面是代碼示例:
<?php $searchStr = 'RC376981638HK'; $post = 'accion=LocalizaUno&numero='.$searchStr.'&ecorreo=&numeros='; $url = 'https://aplicacionesweb.correos.es/localizadorenvios/track.asp'; $ch = curl_init($url); //初始化curl curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出 curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據 curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接參數串 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 報錯時使用 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 報錯時使用 $contents = curl_exec($ch); //執行並存儲結果 // var_dump(curl_error($ch)); //獲取失敗是使用(採集錯誤提示) curl_close($ch); echo $contents;
7、代理採集
你們都知道,國內存在萬惡的牆,因此,假如咱們須要獲取某些被牆數據時,就須要用到國外代理服務器;又或者咱們須要採集大量數據時,須要不斷切換IP,也會用到代理。
使用代理在PHP cURL裏面有幾個相對應的參數:CURLOPT_PROXY、CURLOPT_PROXYPORT 和 CURLOPT_PROXYUSERPWD,還有另外幾個,這裏不列舉。
CURLOPT_PROXY 指定代理IP參數
CURLOPT_PROXYPORT 指定代理端口參數
CURLOPT_PROXYUSERPWD 指定須要驗證的代理的帳號密碼,"[username]:[password]"格式的字符串
關於代理帳號獲取,你們本身發揮,我這裏提供網上搜索到的一個列表:cURL 高匿代理
下面是代理採集示例:
<?php $url = 'http://demo.zjmainstay.cn/php/curl/dump_ip.php?t=' . time(); echo "本地IP:" . file_get_contents($url) . "\n僞造IP:"; $ip = '183.224.1.116'; $port = '80'; //僞造請求頭參數,若是是高匿代理這裏不須要提供 $header = array( 'X-FORWARDED-FOR: ' . $ip, 'CLIENT-IP: ' . $ip, ); $ch = curl_init($url); //初始化curl curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_PROXY, $ip); curl_setopt($ch, CURLOPT_PROXYPORT, $port); $content = curl_exec($ch); //執行並存儲結果 curl_close($ch); echo $content;
8、 多線程採集
對於大量採集工做,爲了提升採集效率,使用PHP cURL提供的多線程採集是必不可少的。手冊上提供的多線程採集例子好像都不太好用,我剛開始也從裏面測試了幾個例子,可是發現都是執行卡死,根本沒法執行完成,前幾天忽然又測試了一下,而後發現curl_multi_info_read函數下面的Example #1是能夠執行的,它的內容在$res上,可是沒有打印出來,並且雅虎的請求比較慢,會卡住,前面兩個連接都能正常返回。
不過,還好當時的例子很差用,而後我通過搜索找到了一個很厲害的項目,CurlMulti ,它對PHP cURL Multi 進行了一個良性擴展的封裝,可以很好地提供採集支持。
關於CurlMulti的使用我就很少介紹,官網上面提供了demo,使用過程有技術難題能夠直接加入Q羣討論,做者@Ares 和其餘的採集大牛都會提供技術解答幫助。
下面是PHP cURL Multi的一個簡單示例:
<?php $urls = array( "http://demo.zjmainstay.cn/php/curl/curl_multi_1.php", "http://demo.zjmainstay.cn/php/curl/curl_multi_2.php", ); $mh = curl_multi_init(); foreach ($urls as $i => $url) { $conn[$i] = curl_init($url); curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1); //不直接輸出結果 curl_multi_add_handle($mh, $conn[$i]); } $active = null; $res = array(); do { $status = curl_multi_exec($mh, $active); $info = curl_multi_info_read($mh); if (false !== $info) { //採集信息處理 $res[] = array( 'content' => curl_multi_getcontent($info['handle']), 'info' => $info, ); curl_close($info['handle']); } } while ($status === CURLM_CALL_MULTI_PERFORM || $active); curl_multi_close($mh); var_dump($res);
9、302跳轉(301跳轉)
對於一些應用,好比模擬登陸,若是趕上302跳轉,會致使cookie丟失而使得模擬登陸失敗,請求現象如圖(六)所示:
這個時候,可使用:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
關於CURLOPT_FOLLOWLOCATION,手冊說明是:
啓用時會將服務器服務器返回的"Location: "放在header中遞歸的返回給服務器,使用CURLOPT_MAXREDIRS能夠限定遞歸返回的數量。
我我的理解,通俗點講就是後面的跳轉會繼續跟蹤訪問,並且cookie在header裏面被保留了下來。
10、模擬上傳文件
在PHP手冊的curl_setopt函數中,關於CURLOPT_POSTFIELDS有以下描述:
所有數據使用HTTP協議中的"POST"操做來發送。要發送文件,在文件名前面加上@前綴並使用完整路徑。這個參數能夠經過urlencoded後的字符串相似'para1=val1¶2=val2&...'或使用一個以字段名爲鍵值,字段數據爲值的數組。若是value是一個數組,Content-Type頭將會被設置成multipart/form-data。
對於上傳文件,這句話包含兩個信息:
1. 要上傳文件,post的數據參數必須使用數組,使得Content-Type頭將會被設置成multipart/form-data。
2. 要上傳文件,在文件名前面加上@前綴並使用完整路徑。
所以,模擬文件上傳能夠按照以下實現:
//上傳D盤下的test.jpg文件,文件必須存在,不然curl處理失敗且沒有任何提示 $data = array('name' => 'Foo', 'file' => '@d:/test.jpg'); $ch = curl_init('http://localhost/upload.php'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_exec($ch);
本地測試的時候,在upload.php文件中打印出\\(_POST和\$_FILES便可驗證是否上傳成功,以下: ``` <?php print_r(\)_POST);
print_r($_FILES);
輸出結果相似:
Array ( [name] => Foo ) Array ( [file] => Array ( [name] => test.jpg [type] => application/octet-stream [tmp_name] => D:\xampp\tmp\php2EA0.tmp [error] => 0 [size] => 139999 ) )
關於CURLOPT_POSTFIELDS的賦值,另外補充一句描述:
傳遞一個數組到CURLOPT_POSTFIELDS,cURL會把數據編碼成 multipart/form-data,而然傳遞一個URL-encoded字符串時,數據會被編碼成 application/x-www-form-urlencoded。
即:
curl_setopt(\(ch, CURLOPT_POSTFIELDS, 'param1=val1¶m2=val2&...'); 和 curl_setopt(\)ch, CURLOPT_POSTFIELDS, array('param1' => 'val1', 'param2' => 'val2', ...));
這樣一個功能強大的採集神器cURL的使用方法爲你們介紹到這,但願對你們的學習有所幫助。