php curl

PHP cURL實現模擬登陸與採集使用方法詳解教程Featured

做者:  Zjmainstay

本文將經過案例,整合瀏覽器工具與PHP程序,教你如何讓數據 唾手可得 。javascript

對於作過數據採集的人來講,cURL必定不會陌生。雖然在PHP中有file_get_contents函數能夠獲取遠程連接的數據,可是它的可控制性太差了,對於各類複雜狀況的採集情景,file_get_contents顯得有點無能爲力。所以,本文將爲你介紹採集神器cURL的使用。php

 

 

 

工具

 

火狐瀏覽器(FireFox) + Firebug

「工欲善其事,必先利其器。」 在分析案例以前,先讓咱們學習一下如何利用神器Firebug獲取咱們必要的信息。 
使用F12打開Firebug,咱們能夠獲得如圖(一)界面: 
Firebughtml

  1. 箭頭圖標是「元素選擇」工具,單擊一次會高亮圖標,同時,鼠標在頁面內的移動會同時在HTML菜單中選定相應的內容,此時單擊內容則表示選定了該元素,圖標高亮取消。如圖(二)所示: 
    Firebug查看元素java

  2. 控制檯 
    JS裏面的console.log系列函數的打印就是在這裏輸出。ios

  3. HTML 
    HTML內容,注意這裏看到的不必定是採集要解析的內容,採集時候對內容的分析,一概以查看源碼(Ctrl+U)爲準,這裏只是能快速定位元素的結構,而後再選擇一個比較特殊的參照,在源碼中定位相應的位置。 
    好比,你在HTML裏面看到一個標籤是<div id="demo" class="demo">Demo</div>,可是你查看源碼時候看到的內容多是<div class="demo" id="demo">Demo</div>,若是你對採集內容按照前者去作正則匹配,那麼你會得不到結果。
  4. CSS 
    這裏是CSS文件內容
  5. 腳本 
    這裏是Javascript文件內容
  6. DOM 
    Dom節點內容
  7. 網絡 
    每個請求連接的數據,這裏是咱們採集要關注和分析的地方,它可以顯示每個請求的參數、請求頭、Cookie數據等。在頁面提交會刷新的狀況下,須要使用保持,使得頁面請求內容在刷新後仍然留着控制檯中,如圖(三)所示: 
    Firebug網絡保持
    另外,火狐還有一款 Tamper data 擴展也能獲得請求數據,必要時能夠安裝使用。
  8. Cookies 
    Cookie數據

在圖(一)中還看到下面有不少可選的小菜單項,其中保持是咱們要關注的,當選擇它的時候,即便提交表單刷新了頁面,下面內容區域的數據仍是會保留,這個對於分析提交數據特別關鍵。git

 

總結

咱們在分析採集請求的時候,主要關心「網絡」菜單裏的請求數據,必要時候使用「保持」以查看刷新頁面的請求數據,請求前可使用「清除」先清除下面的內容。github

 

案例解析

 

1、簡單的採集

這裏所指的簡單採集,是指一個單一頁面GET請求的採集,它簡單得即便經過file_get_contents函數也能輕鬆得到頁面返回結果。web


  • 代碼片斷之file_get_contents
 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/simple.html';
  3. $content = file_get_contents($url);
  4. echo $content;

  • 代碼片斷之cURL
 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/simple.html';
  3. $ch = curl_init($url);
  4. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  5. $content = curl_exec($ch); //執行並存儲結果
  6. curl_close($ch);
  7. echo $content;

 

2、須要參數的採集

這種狀況,頁面請求須要傳入一些參數,能夠是GET請求,也能夠是POST請求。這種狀況的採集,使用file_get_contents外帶一些參數仍是能夠實現的,可是這裏咱們將再也不展現。ajax

  • 代碼片斷之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 參數就是咱們要傳入的動態參數,而其餘參數則能夠不變,所以獲得咱們下面的採集代碼。
 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. $url = 'http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&ch=&tn=baidu&bar=&wd=' . urlencode($keyword);
  4. $ch = curl_init($url);
  5. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  6. $content = curl_exec($ch); //執行並存儲結果
  7. curl_close($ch);
  8. echo $content;

有些時候,一些參數並非必須的,這時候咱們能夠刪掉它,好比上面的連接能夠只保留http://www.baidu.com/s?ie=utf-8&wd=PHP%20cURL,ie=utf-8 這個參數可能影響結果的編碼,因此暫且留着它。就這樣簡單的代碼,咱們就能夠採集到百度搜索的結果了。json

  • 代碼片斷之cURL POST 
    對於POST類型的請求,咱們平時並很多見,好比有些搜索就是使用POST方式提交,這時候咱們就須要使用POST類型來提交參數了。這個在PHP cURL裏面有相應的參數:CURLOPT_POST 和 CURLOPT_POSTFIELDS , CURLOPT_POST 的設置能夠指定當前提交是否爲POST方式,CURLOPT_POSTFIELDS則用於設定提交的參數,能夠是參數串,也能夠是參數數組,好比:
 
  1. curl_setopt($ch, CURLOPT_POSTFIELDS, 'ie=utf-8&wd=PHP%20cURL');
  2. curl_setopt($ch, CURLOPT_POSTFIELDS, array(
  3. 'ie' => 'utf-8',
  4. 'wd' => 'PHP%20cURL',
  5. ));

下面是我作的一個POST模擬搜索PHP POST 搜索,後端是使用了前面的百度關鍵詞搜索,基本原理就是,客戶端提交一個關鍵詞到我服務器,我服務器使用該關鍵詞請求百度的搜索,而後獲得結果,返回到客戶端。 
如圖(四)是利用Firebug對請求數據的分析,獲得咱們須要提交的請求連接和請求參數: 
cURL POST 參數分析

而後下面是咱們的代碼:

 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. //參數方法一
  4. // $post = 'wd=' . urlencode($keyword);
  5. //參數方法二
  6. $post = array(
  7. 'wd' => urlencode($keyword),
  8. );
  9. $url = 'http://demo.zjmainstay.cn/php/curl/search.php';
  10. $ch = curl_init($url);
  11. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  12. curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據
  13. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接
  14. $content = curl_exec($ch); //執行並存儲結果
  15. curl_close($ch);
  16. var_dump($content);

 

3、須要Referer的採集

對於一些程序,它可能判斷來源網址,若是發現referer不是本身的網站,則拒絕訪問,這時候,咱們就須要添加CURLOPT_REFERER參數,模擬來路,使得程序可以正常採集。

 
  1. <?php
  2. $keyword = 'PHP cURL';
  3. //參數方法一
  4. // $post = 'wd=' . urlencode($keyword);
  5. //參數方法二
  6. $post = array(
  7. 'wd' => urlencode($keyword),
  8. );
  9. $url = 'http://demo.zjmainstay.cn/php/curl/search_refer.php';
  10. $refer = 'http://demo.zjmainstay.cn/'; //來路地址
  11. $ch = curl_init($url);
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  13. curl_setopt($ch, CURLOPT_REFERER, $refer); //來路模擬
  14. curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據
  15. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接
  16. $content = curl_exec($ch); //執行並存儲結果
  17. curl_close($ch);
  18. var_dump($content);

search_refer.php的源碼以下,作了簡單的Referer判斷攔截:

 
  1. <?php
  2. if(empty($_POST['wd'])) {
  3. exit('Deny empty params.');
  4. }
  5. //Referer判斷
  6. if(stripos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
  7. exit('Deny');
  8. }
  9. $keyword = addslashes(trim(strip_tags($_POST['wd'])));
  10. $url = 'http://www.baidu.com/s?ie=utf-8&wd=' . urlencode($keyword);
  11. $ch = curl_init($url);
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  13. $content = curl_exec($ch); //執行並存儲結果
  14. curl_close($ch);
  15. echo $content;

 

4、須要cookie支持的採集

對於模擬登陸的應用,單單提交參數和模擬來路並不能解決問題,這時候咱們就須要保存或者提交相應的Cookie參數,這個在PHP cURL裏面也提供了相應的參數: 
CURLOPT_COOKIE: 直接使用字符串方式提交cookie參數 
CURLOPT_COOKIEFILE: 使用文件方式提交cookie參數 
CURLOPT_COOKIEJAR: 保存提交後反饋的cookie數據

 

(一)模擬登陸示例

下面是PHP100的模擬登陸示例:

 
  1. <?php
  2. header("content-Type: text/html; charset=UTF-8");
  3. $cookie_file = tempnam('./temp', 'cookie');
  4. $login_url="http://bbs.php100.com/login.php";
  5. $post_fields="cktime=36000&step=2&pwuser=username&pwpwd=password";
  6. //提交登陸表單請求
  7. $ch=curl_init($login_url);
  8. curl_setopt($ch,CURLOPT_HEADER,0);
  9. curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
  10. curl_setopt($ch,CURLOPT_POST,1);
  11. curl_setopt($ch,CURLOPT_POSTFIELDS,$post_fields);
  12. curl_setopt($ch,CURLOPT_COOKIEJAR,$cookie_file); //存儲提交後獲得的cookie數據
  13. curl_exec($ch);
  14. curl_close($ch);
  15. //登陸成功後,獲取bbs首頁數據
  16. $url="http://bbs.php100.com/index.php";
  17. $ch=curl_init($url);
  18. curl_setopt($ch,CURLOPT_HEADER,0);
  19. curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
  20. curl_setopt($ch,CURLOPT_COOKIEFILE,$cookie_file); //使用提交後獲得的cookie數據作參數
  21. $contents=curl_exec($ch);
  22. curl_close($ch);
  23. //轉碼顯示
  24. echo iconv('gbk', 'UTF-8', $contents);
 

(二)自動模擬登陸實現

最近研究出來一個自動模擬登陸的類,請查看《PHP基於cURL實現自動模擬登陸》瞭解。(補充於2016.7.31)

 

5、壓縮網頁採集(gzip)

有些沒有接觸過壓縮頁面的朋友估計會在這裏被坑死,由於他們會發現採集回來的內容是亂碼,而且不管使用iconv仍是強大的mb_convert_encoding都沒法還原數據,而後又沒有概念,各類抓狂卻找不到方法,哈哈,我曾經也是這樣~ 
如圖(五)是亂碼錶現形式: 
cURL 亂碼 解決
還好最後功夫不負有心人,仍是找到了,它就是CURLOPT_ENCODING參數。 
好比,採集搜狐的新聞時候就遇到gzip壓縮問題,下面是示例:

 
  1. <?php
  2. $url = 'http://news.sohu.com/';
  3. $ch = curl_init($url);
  4. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  5. curl_setopt($ch, CURLOPT_ENCODING, "gzip"); //指定gzip壓縮
  6. $content = curl_exec($ch); //執行並存儲結果
  7. curl_close($ch);
  8. 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參數禁用失敗,因此你們最好同時使用兩個參數。 
下面是代碼示例:

 
  1. <?php
  2. $searchStr = 'RC376981638HK';
  3. $post = 'accion=LocalizaUno&numero='.$searchStr.'&ecorreo=&numeros=';
  4. $url = 'https://aplicacionesweb.correos.es/localizadorenvios/track.asp';
  5. $ch = curl_init($url); //初始化curl
  6. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  7. curl_setopt($ch, CURLOPT_POST, 1); //發送POST類型數據
  8. curl_setopt($ch, CURLOPT_POSTFIELDS, $post); //POST數據,$post能夠是數組,也能夠是拼接參數串
  9. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //SSL 報錯時使用
  10. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //SSL 報錯時使用
  11. $contents = curl_exec($ch); //執行並存儲結果
  12. // var_dump(curl_error($ch)); //獲取失敗是使用(採集錯誤提示)
  13. curl_close($ch);
  14. echo $contents;

今天(2016.8.20)遇到一個錯誤error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112),測試追加參數

 
  1. //值有0-6,請參考手冊,值1不行試試其餘值
  2. curl_setopt($ch, CURLOPT_SSLVERSION, 1);

可解決問題。

 

7、代理採集

你們都知道,國內存在萬惡的牆,因此,假如咱們須要獲取某些被牆數據時,就須要用到國外代理服務器;又或者咱們須要採集大量數據時,須要不斷切換IP,也會用到代理。 
使用代理在PHP cURL裏面有幾個相對應的參數:CURLOPT_PROXY、CURLOPT_PROXYPORT 和 CURLOPT_PROXYUSERPWD,還有另外幾個,這裏不列舉。 
CURLOPT_PROXY 指定代理IP參數 
CURLOPT_PROXYPORT 指定代理端口參數 
CURLOPT_PROXYUSERPWD 指定須要驗證的代理的帳號密碼,"[username]:[password]"格式的字符串

關於代理帳號獲取,你們本身發揮,我這裏提供網上搜索到的一個列表:cURL 高匿代理

下面是代理採集示例:

 
  1. <?php
  2. $url = 'http://demo.zjmainstay.cn/php/curl/dump_ip.php?t=' . time();
  3. echo "本地IP:" . file_get_contents($url) . "\n僞造IP:";
  4. $ip = '183.224.1.116';
  5. $port = '80';
  6. //僞造請求頭參數,若是是高匿代理這裏不須要提供
  7. $header = array(
  8. 'X-FORWARDED-FOR: ' . $ip,
  9. 'CLIENT-IP: ' . $ip,
  10. );
  11. $ch = curl_init($url); //初始化curl
  12. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  13. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  14. curl_setopt($ch, CURLOPT_PROXY, $ip);
  15. curl_setopt($ch, CURLOPT_PROXYPORT, $port);
  16. $content = curl_exec($ch); //執行並存儲結果
  17. curl_close($ch);
  18. echo $content;
 

8、 多線程採集

對於大量採集工做,爲了提升採集效率,使用PHP cURL提供的多線程採集是必不可少的。手冊上提供的多線程採集例子好像都不太好用,我剛開始也從裏面測試了幾個例子,可是發現都是執行卡死,根本沒法執行完成,前幾天忽然又測試了一下,而後發現curl_multi_info_read函數下面的Example #1是能夠執行的,它的內容在$res上,可是沒有打印出來,並且雅虎的請求比較慢,會卡住,前面兩個連接都能正常返回。 
不過,還好當時的例子很差用,而後我通過搜索找到了一個很厲害的項目,CurlMulti ,它對PHP cURL Multi 進行了一個良性擴展的封裝,可以很好地提供採集支持。 
關於CurlMulti的使用我就很少介紹,官網上面提供了demo,使用過程有技術難題能夠直接加入Q羣( cURL高級技術 215348766 )討論,做者@Ares 和其餘的採集大牛都會提供技術解答幫助。 
下面是PHP cURL Multi的一個簡單示例:

 
  1. <?php
  2. $urls = array(
  3. array(
  4. 'url' => 'http://demo.zjmainstay.cn/php/curl/curl_multi_1.php',
  5. 'id' => 1,
  6. ),
  7. array(
  8. 'url' => 'http://demo.zjmainstay.cn/php/curl/curl_multi_2.php',
  9. 'id' => 2,
  10. ),
  11. );
  12. $mh = curl_multi_init();
  13. $conn = array();
  14. foreach ($urls as $urlItem) {
  15. $ch = curl_init($urlItem['url']);
  16. $conn[(int)$ch] = $urlItem; //記錄資源與參數映射
  17. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //不直接輸出結果
  18. curl_multi_add_handle($mh, $ch);
  19. }
  20. $active = null;
  21. $res = array();
  22. do {
  23. $status = curl_multi_exec($mh, $active);
  24. $info = curl_multi_info_read($mh);
  25. if (false !== $info) {
  26. //採集信息處理
  27. $res[] = array(
  28. 'content' => curl_multi_getcontent($info['handle']),
  29. 'info' => $info,
  30. 'param' => $conn[(int)$info['handle']],
  31. );
  32. curl_close($info['handle']);
  33. }
  34. } while ($status === CURLM_CALL_MULTI_PERFORM || $active);
  35. curl_multi_close($mh);
  36. var_dump($res);

附:全站克隆本地化網頁爬蟲示例

 

9、302跳轉(301跳轉)

對於一些應用,好比模擬登陸,若是趕上302跳轉,會致使cookie丟失而使得模擬登陸失敗,請求現象如圖(六)所示:

curl 302跳轉

這個時候,可使用:

 
  1. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);

關於CURLOPT_FOLLOWLOCATION,手冊說明是:

啓用時會將服務器服務器返回的"Location: "放在header中遞歸的返回給服務器,使用CURLOPT_MAXREDIRS能夠限定遞歸返回的數量。

我我的理解,通俗點講就是後面的跳轉會繼續跟蹤訪問,並且cookie在header裏面被保留了下來。

 

10、模擬上傳文件

(一)基於本地文件上傳

在PHP手冊的curl_setopt函數中,關於CURLOPT_POSTFIELDS有以下描述:

 
  1. 所有數據使用HTTP協議中的"POST"操做來發送。要發送文件,在文件名前面加上@前綴並使用完整路徑。這個參數能夠經過urlencoded後的字符串相似'para1=val1&para2=val2&...'或使用一個以字段名爲鍵值,字段數據爲值的數組。若是value是一個數組,Content-Type頭將會被設置成multipart/form-data

對於上傳文件,這句話包含兩個信息: 
1. 要上傳文件,post的數據參數必須使用數組,使得Content-Type頭將會被設置成multipart/form-data。 
2. 要上傳文件,在文件名前面加上@前綴並使用完整路徑。 
注意:PHP 5.5.0起,文件上傳建議使用CURLFile代替@

所以,模擬文件上傳能夠按照以下實現:

 
(1)單文件上傳
 
  1. //上傳D盤下的test.jpg文件,文件必須存在,不然curl處理失敗且沒有任何提示,Windows下中文文件名上傳失敗時,可以使用iconv('UTF-8', 'GBK//IGNORE', $filename)轉碼下文件名再上傳。
  2. $data = array('name' => 'Foo', 'file' => '@d:/test.jpg');
  3. 注: PHP 5.5.0起,文件上傳建議使用CURLFile代替@
  4. $ch = curl_init('http://localhost/upload.php');
  5. curl_setopt($ch, CURLOPT_POST, 1);
  6. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  7. curl_exec($ch);

本地測試的時候,在upload.php文件中打印出$_POST和$_FILES便可驗證是否上傳成功,以下:

 
  1. <?php
  2. print_r($_POST);
  3. print_r($_FILES);

輸出結果相似:

 
  1. Array ( [name] => Foo ) Array ( [file] => Array ( [name] => test.jpg [type] => application/octet-stream [tmp_name] => D:\xampp\tmp\php2EA0.tmp [error] => 0 [size] => 139999 ) )
 
(2)多文件上傳
 
  1. <?php
  2. // 注: PHP 5.5.0起,文件上傳建議使用CURLFile代替@
  3. // 多文件上傳
  4. $data = array(
  5. 'input_file[0]' => new CURLFile('d:/1.txt', 'text/plain', 'testfile.txt'),
  6. 'input_file[1]' => new CURLFile('d:/2.txt', 'text/plain'),
  7. 'input_file[2]' => new CURLFile('d:/3.txt', 'text/plain'),
  8. );
  9. $ch = curl_init('http://demo.zjmainstay.cn/php/curl/curlUploadHandler.php');
  10. curl_setopt($ch, CURLOPT_POST, 1);
  11. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  12. curl_exec($ch);

輸出結果相似:

 
  1. Array ( [upload_file] => Array ( [name] => Array ( [0] => 1.txt [1] => 2.txt [2] => 3.txt ) [type] => Array ( [0] => text/plain [1] => text/plain [2] => text/plain ) [tmp_name] => Array ( [0] => /tmp/phpkQ75hZ [1] => /tmp/phpuVB5La [2] => /tmp/phpu1z5fm ) [error] => Array ( [0] => 0 [1] => 0 [2] => 0 ) [size] => Array ( [0] => 2 [1] => 2 [2] => 2 ) ) )
 
(3)CURLOPT_POSTFIELDS使用字符串與數組的區別

關於CURLOPT_POSTFIELDS的賦值,另外補充一句描述:

 
  1. 傳遞一個數組到CURLOPT_POSTFIELDScURL會把數據編碼成 multipart/form-data,而然傳遞一個URL-encoded字符串時,數據會被編碼成 application/x-www-form-urlencoded

即:

 
  1. curl_setopt($ch, CURLOPT_POSTFIELDS, 'param1=val1&param2=val2&...');
  2. curl_setopt($ch, CURLOPT_POSTFIELDS, array('param1' => 'val1', 'param2' => 'val2', ...));

至關於html的form表單中:

 
  1. <form method="post" action="upload.php">

 
  1. <form method="post" action="upload.php" enctype="multipart/form-data">

的區別。

 

(二)基於採集文件內容上傳

對於數據採集回來的文件內容,有時候須要再次上傳到其餘的文件服務器上,此時,若是本地先存儲再利用上面的方式提交到文件服務器,顯然會多了一次IO寫入和讀取操做,對於這種狀況,咱們能夠利用構造模擬上傳請求頭和構造數據報文,直接利用採集回來的文件內容上傳到文件服務器:

 
  1. <?php
  2. /**
  3. * @author 若是的若是
  4. * PHP利用cURL直接以文件內容形式上傳文件
  5. * @param string $url 文件上傳處理連接
  6. * @param array $fileFields 文件上傳數據數組
  7. * @param array $postFields 非文件表單數據數組
  8. * @param array $curlOpt 擴展的CURLOPT_數據
  9. * @return string
  10. */
  11. function curlPostMemoryFile($url, $fileFields, $postFields = array(), $curlOpt = array()){
  12. //構造post數據
  13. $data = '';
  14. $delimiter = '-------------' . uniqid();
  15. // 表單數據
  16. foreach ($postFields as $name => $content) {
  17. $data .= "--" . $delimiter . "\r\n";
  18. $data .= 'Content-Disposition: form-data; name="' . $name . '"';
  19. $data .= "\r\n\r\n";
  20. $data .= $content;
  21. $data .= "\r\n";
  22. //$data .= "--" . $delimiter . "\r\n";
  23. }
  24. // 文件上傳數據
  25. foreach ($fileFields as $inputName => $file) {
  26. $data .= "--" . $delimiter . "\r\n";
  27. $data .= 'Content-Disposition: form-data; name="' . $inputName . '";' .
  28. ' filename="' . $file['filename'] . '"' . "\r\n";
  29. $data .= 'Content-Type: ' . $file['type'] . "\r\n";
  30. $data .= "\r\n";
  31. $data .= $file['content'] . "\r\n";
  32. }
  33. $data .= "--" . $delimiter . "--\r\n";
  34. //post請求提交文件上傳數據
  35. $handle = curl_init($url);
  36. curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1); //返回數據不直接輸出
  37. $header = array(
  38. 'Content-Type: multipart/form-data; boundary=' . $delimiter,
  39. 'Content-Length: ' . strlen($data)
  40. );
  41. if(isset($curlOpt[CURLOPT_HTTPHEADER])) {
  42. $header = array_merge($header, $curlOpt[CURLOPT_HTTPHEADER]);
  43. unset($curlOpt[CURLOPT_HTTPHEADER]);
  44. }
  45. curl_setopt($handle, CURLOPT_HTTPHEADER , $header);
  46. curl_setopt($handle, CURLOPT_POST, true);
  47. curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
  48. if(!empty($curlOpt)){
  49. foreach($curlOpt as $key => $val){
  50. curl_setopt($handle,$key,$val);
  51. }
  52. }
  53. return curl_exec($handle);
  54. }
  55. //演示
  56. $url = 'http://demo.zjmainstay.cn/php/curl/curlUploadHandler.php';
  57. $filename = 'test.txt';
  58. $fileContent = '測試直接文件內容形式上傳文件';
  59. $fileFields = array(
  60. 'upload_file' => array(
  61. 'filename' => $filename,
  62. #從正常上傳時的post數據中查看,對應$_FILES裏的type
  63. 'type' => 'text/plain',
  64. 'content' => $fileContent,
  65. ),
  66. );
  67. $postFields = array(
  68. 'name' => 'Zjmainstay',
  69. 'age' => '26',
  70. );
  71. //此處測試CURLOPT_HTTPHEADER的合併
  72. $header = array(
  73. 'Host: demo.zjmainstay.cn' ,
  74. );
  75. $curlOpt = array(
  76. CURLOPT_HTTPHEADER => $header,
  77. );
  78. $content = curlPostMemoryFile($url, $fileFields, $postFields, $curlOpt);
  79. var_dump($content);
 

11、發送與獲取json數據

發送json數據,在控制檯中的表現主要如圖(七)所示: 
ajax發送json的控制檯信息
第一條發送的是json格式的數據, 
第二條發送的是以\n分割的數據, 
第三條發送的是以&分割的數據。 
這個在ajax請求的時候,只須要添加contentType參數便可,如:

 
  1. var data = ["name:Zjmainstay", "website:http://www.zjmainstay.cn"];
  2. $.ajax({
  3. url: 'http://test.com/curl/testPostJsonData.php',
  4. type: 'post',
  5. data: data.join("\n"),
  6. contentType: 'text/plain',
  7. success: function(result) {
  8. console.log(result);
  9. },
  10. error: function(msg) {
  11. console.log(msg);
  12. }
  13. });

對於這類發送json數據的請求,複製cURL命令時,你會發現其中根本沒有發送數據,如:

 
  1. curl 'http://test.com/curl/testPostJsonData.php' -X POST -H 'Accept: */*' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Cache-Control: max-age=0' -H 'Connection: keep-alive' -H 'Content-Length: 48' -H 'Content-Type: text/plain' -H 'Cookie: __utma=99889051.942646074.1467634856.1467634856.1467636947.2' -H 'Host: test.com' -H 'Referer: http://test.com/curl/ajaxJsonData.html' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:47.0) Gecko/20100101 Firefox/47.0' -H 'X-Requested-With: XMLHttpRequest'

對於這種數據,咱們要用PHP cURL模擬發送的話,須要發送相應的header參數,示例:

 
  1. <?php
  2. #json數據
  3. $url = 'http://test.com/curl/testPostJsonData.php';
  4. $data = '{"a":"b"}';
  5. $length = strlen($data);
  6. $header = array(
  7. 'Content-Length: ' . $length, //不是必需的
  8. 'Content-Type: text/json',
  9. );
  10. $ch = curl_init($url); //初始化curl
  11. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  12. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  13. curl_setopt($ch, CURLOPT_POST, 1);
  14. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  15. $content = curl_exec($ch); //執行並存儲結果
  16. curl_close($ch);
  17. echo $content;
  18. #\n分割數據
  19. $data = [
  20. 'name:Zjmainstay',
  21. 'website:http://www.zjmainstay.cn',
  22. ];
  23. $data = implode("\n", $data);
  24. #&分割數據
  25. $data = 'name:Zjmainstay&website:http://www.zjmainstay.cn';

對於$.ajax咱們經常會使用這種寫法來指定返回json格式數據:

 
  1. $.ajax({
  2. url: url,
  3. dataType: 'json',
  4. ...
  5. });

咱們會發現,cURL請求頭Accept:部分會多出一些參數:

 
  1. -H 'Accept: application/json, text/javascript, */*; q=0.01'

所以,若是須要指定返回內容做爲json格式,咱們就須要指定application/json格式。

那麼,對於這種給服務器端發送json數據的程序,服務器端是怎麼獲得請求數據的呢? 
若是大家作過嘗試,必定會發現此時$_POST是空的,咱們要使用php://input進行數據獲取,示例:

 
  1. $postData = file_get_contents('php://input');
 

12、POST提交大數據(超過1024字節)異常解決方法

在使用cURL作POST的時候,當要POST的數據大於1024字節的時候,cURL並不會直接就發起POST請求, 而是會分爲倆步:

 
  1. 1. 發送一個請求, 包含一個Expect:100-continue, 詢問Server是否願意接受數據
  2. 2. 接收到Server返回的100-continue應答之後, 才把數據POSTServer

解決:

 
  1. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
 

十3、獲取請求頭部和丟棄BODY內容

有時候,被採集的站點把下一次跳轉的連接放入了響應頭裏,這時候,咱們須要經過設置CURLOPT_HEADER屬性爲true,讓響應結果攜帶響應頭便可。

 
  1. curl_setopt($ch, CURLOPT_HEADER, true);

若是咱們只須要獲取響應頭,或者有時候只是爲了測試某個連接是否可以正常請求,咱們能夠經過設置CURLOPT_NOBODYtrue,讓響應結果不返回BODY內容。

 
  1. curl_setopt($ch, CURLOPT_NOBODY, true);
 

總結

 

通用curl頁面採集函數

PHP通用cURL頁面採集函數

 

簡易解析cURL命令獲得PHP代碼程序

簡易解析cURL命令獲得PHP代碼程序

PHP cURL是一個很強大的採集工具,curl_setopt裏面還有不少參數,讀者能夠抽空總體看一遍,雖然平時未必用得上,可是至少作到內心有底,知道都有哪些參數,必要時還能找出來使用。 採集是一項大工程,使用過程當中遇到的問題還會很多,可是隻要學會分析和資料搜索,一切都會迎刃而解的,你們加油!哈哈~~

相關文章
相關標籤/搜索