PHP 的 cURL库快速入门文档
作者:Burak Guzel
原文鏈接:http://net.tutsplus.com/tutorials/php/techniques-and-resources-for-mastering-curl/
翻譯:笨活兒
cURL 是一個利用 URL 語法規(guī)定來傳輸文件和數(shù)據(jù)的工具,支持很多協(xié)議,如 HTTP、FTP、TELNET 等。最爽的是,PHP 也支持 cURL 庫。本文將介紹 cURL 的一些高級特性,以及在 PHP 中如何運用它。
?
為什么要用 cURL?
是的,我們可以通過其他辦法獲取網(wǎng)頁內(nèi)容。大多數(shù)時候,我因為想偷懶,都直接用簡單的PHP函數(shù):
| 12345 | $content = file_get_contents("http://www.nettuts.com");// or$lines = file("http://www.nettuts.com");// orreadfile(http://www.nettuts.com); |
不過,這種做法缺乏靈活性和有效的錯誤處理。而且,你也不能用它完成一些高難度任務(wù)——比如處理coockies、驗證、表單提交、文件上傳等等。
基本結(jié)構(gòu)
在學(xué)習(xí)更為復(fù)雜的功能之前,先來看一下在 PHP 中建立 cURL 請求的基本步驟:
| 12345678910111213 | // 1. 初始化$ch = curl_init();// 2. 設(shè)置選項,包括URLcurl_setopt($ch, CURLOPT_URL, "http://www.nettuts.com");curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_HEADER, 0);// 3. 執(zhí)行并獲取HTML文檔內(nèi)容$output = curl_exec($ch);// 4. 釋放curl句柄curl_close($ch); |
第二步(也就是 curl_setopt() )最為重要,一切玄妙均在此。有一長串 cURL 參數(shù)可供設(shè)置,它們能指定 URL 請求的各個細(xì)節(jié)。要一次性全部看完并理解可能比較困難,所以今天我們只試一下那些更常用也更有用的選項。
檢查錯誤
你可以加一段檢查錯誤的語句(雖然這并不是必需的):
| 123456 | // ...$output = curl_exec($ch);if ($output === FALSE) { echo "cURL Error: " . curl_error($ch);}// ... |
請注意,比較的時候我們用的是“=== FALSE”,而非“== FALSE”。因為我們得區(qū)分 空輸出 和 布爾值 FALSE,后者才是真正的錯誤。
獲取信息
這是另一個可選的設(shè)置項,能夠在 cURL 執(zhí)行后獲取這一請求的有關(guān)信息:
| 12345 | // ...curl_exec($ch);$info = curl_getinfo($ch);echo '獲取'. $info['url'] . '耗時'. $info['total_time'] . '秒';// ... |
返回的數(shù)組中包括了以下信息:
“url” //資源網(wǎng)絡(luò)地址
“content_type” //內(nèi)容編碼
“http_code” //HTTP狀態(tài)碼
“header_size” //header的大小
“request_size” //請求的大小
“filetime” //文件創(chuàng)建時間
“ssl_verify_result” //SSL驗證結(jié)果
“redirect_count” //跳轉(zhuǎn)技術(shù)
“total_time” //總耗時
“namelookup_time” //DNS查詢耗時
“connect_time” //等待連接耗時
“pretransfer_time” //傳輸前準(zhǔn)備耗時
“size_upload” //上傳數(shù)據(jù)的大小
“size_download” //下載數(shù)據(jù)的大小
“speed_download” //下載速度
“speed_upload” //上傳速度
“download_content_length”//下載內(nèi)容的長度
“upload_content_length” //上傳內(nèi)容的長度
“starttransfer_time” //開始傳輸?shù)臅r間
“redirect_time”//重定向耗時
基于瀏覽器的重定向
在第一個例子中,我們將提供一段用于偵測服務(wù)器是否有基于瀏覽器的重定向的代碼。例如,有些網(wǎng)站會根據(jù)是否是手機(jī)瀏覽器甚至用戶來自哪個國家來重定向網(wǎng)頁。
我們利用 CURLOPT_HTTPHEADER 選項來設(shè)定我們發(fā)送出的 HTTP 請求頭信息(http headers),包括 user agent 信息和默認(rèn)語言。然后我們來看看這些特定網(wǎng)站是否會把我們重定向到不同的URL。
| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 | // test URLs$urls = array("http://www.cnn.com","http://www.mozilla.com","http://www.facebook.com");// test browsers$browsers = array("standard" => array ("user_agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)","language" => "en-us,en;q=0.5"),"iphone" => array ("user_agent" => "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3","language" => "en"),"french" => array ("user_agent" => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)","language" => "fr,fr-FR;q=0.5"));foreach ($urls as $url) {echo "URL: $urln";foreach ($browsers as $test_name => $browser) {$ch = curl_init();// set urlcurl_setopt($ch, CURLOPT_URL, $url);// set browser specific headerscurl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: {$browser['user_agent']}","Accept-Language: {$browser['language']}"));// we don't want the page contentscurl_setopt($ch, CURLOPT_NOBODY, 1);// we need the HTTP Header returnedcurl_setopt($ch, CURLOPT_HEADER, 1);// return the results instead of outputting itcurl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$output = curl_exec($ch);curl_close($ch);// was there a redirection HTTP header?if (preg_match("!Location: (.*)!", $output, $matches)) {echo "$test_name: redirects to $matches[1]n";} else {echo "$test_name: no redirectionn";}}echo "nn";} |
首先,我們建立一組需要測試的URL,接著指定一組需要測試的瀏覽器信息。最后通過循環(huán)測試各種URL和瀏覽器匹配可能產(chǎn)生的情況。
因為我們指定了cURL選項,所以返回的輸出內(nèi)容則只包括HTTP頭信息(被存放于 $output 中)。利用一個簡單的正則,我們檢查這個頭信息中是否包含了“Location:”字樣。
運行這段代碼應(yīng)該會返回如下結(jié)果:
用POST方法發(fā)送數(shù)據(jù)
當(dāng)發(fā)起 GET 請求時,數(shù)據(jù)可以通過“查詢字串”(query string)傳遞給一個URL。例如,在google中搜索時,搜索關(guān)鍵即為 URL 的查詢字串的一部分:
http://www.google.com/search?q=nettuts這種情況下你可能并不需要 cURL 來模擬。把這個 URL 丟給“file_get_contents()”就能得到相同結(jié)果。
不過有一些 HTML 表單是用 POST 方法提交的。這種表單提交時,數(shù)據(jù)是通過 HTTP 請求體(request body) 發(fā)送,而不是查詢字串。例如,當(dāng)使用 CodeIgniter 論壇的表單,無論你輸入什么關(guān)鍵字,總是被 POST 到如下頁面:
http://codeigniter.com/forums/do_search/你可以用 PHP 腳本來模擬這種 URL 請求。首先,新建一個可以接受并顯示 POST 數(shù)據(jù)的文件,我們給它命名為post_output.php:
print_r($_POST);接下來,寫一段 PHP 腳本來執(zhí)行 cURL 請求:
| 12345678910111213 | $url = "http://localhost/post_output.php";$post_data = array ( "foo" => "bar", "query" => "Nettuts", "action" => "Submit");$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 我們在POST數(shù)據(jù)哦!curl_setopt($ch, CURLOPT_POST, 1);// 把post的變量加上curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo $output; |
執(zhí)行代碼后應(yīng)該會得到以下結(jié)果:
這段腳本發(fā)送一個 POST 請求給 post_output.php ,這個頁面 $_POST 變量并返回,我們利用 cURL 捕捉了這個輸出。
文件上傳
上傳文件和前面的 POST 十分相似。因為所有的文件上傳表單都是通過 POST 方法提交的。
首先新建一個接收文件的頁面,命名為 upload_output.php:
print_r($_FILES);以下是真正執(zhí)行文件上傳任務(wù)的腳本:
| 12345678910111213141516 | $url = "http://localhost/upload_output.php";$post_data = array ( "foo" => "bar", // 要上傳的本地文件地址 "upload" => "@C:/wamp/www/test.zip");$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);$output = curl_exec($ch);curl_close($ch);echo $output; |
如果你需要上傳一個文件,只需要把文件路徑像一個 post 變量一樣傳過去,不過記得在前面加上@符號。執(zhí)行這段腳本應(yīng)該會得到如下輸出:
cURL批處理(multi cURL)
cURL 還有一個高級特性——批處理句柄(handle)。這一特性允許你同時或異步地打開多個URL連接。
下面是來自來自 php.net 的示例代碼:
| 12345678910111213141516171819202122232425262728293031323334353637 | // 創(chuàng)建兩個cURL資源$ch1 = curl_init();$ch2 = curl_init();// 指定URL和適當(dāng)?shù)膮?shù)curl_setopt($ch1, CURLOPT_URL, "http://lxr.php.net/");curl_setopt($ch1, CURLOPT_HEADER, 0);curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/");curl_setopt($ch2, CURLOPT_HEADER, 0);// 創(chuàng)建cURL批處理句柄$mh = curl_multi_init();// 加上前面兩個資源句柄curl_multi_add_handle($mh,$ch1);curl_multi_add_handle($mh,$ch2);// 預(yù)定義一個狀態(tài)變量$active = null;// 執(zhí)行批處理do { $mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); }}//關(guān)閉各個句柄curl_multi_remove_handle($mh, $ch1);curl_multi_remove_handle($mh, $ch2);curl_multi_close($mh); |
這里要做的就是打開多個 cURL 句柄并指派給一個批處理句柄。然后你就只需在一個 while 循環(huán)里等它執(zhí)行完畢。
這個示例中有兩個主要循環(huán)。第一個 do-while 循環(huán)重復(fù)調(diào)用 curl_multi_exec() 。這個函數(shù)是無隔斷(non-blocking)的,但會盡可能少地執(zhí)行。它返回一個狀態(tài)值,只要這個值等于常量 CURLM_CALL_MULTI_PERFORM ,就代表還有一些刻不容緩的工作要做(例如,把對應(yīng) URL 的 http 頭信息發(fā)送出去)。也就是說,我們需要不斷調(diào)用該函數(shù),直到返回值發(fā)生改變。
而接下來的 while 循環(huán),只在 $active 變量為 true 時繼續(xù)。這一變量之前作為第二個參數(shù)傳給了 curl_multi_exec() ,代表只要批處理句柄中是否還有活動連接。接著,我們調(diào)用 curl_multi_select() ,在活動連接(例如接受服務(wù)器響應(yīng))出現(xiàn)之前,它都是被“屏蔽”的。這個函數(shù)成功執(zhí)行后,我們又會進(jìn)入另一個 do-while 循環(huán),繼續(xù)下一條URL。
還是來看一看怎么把這一功能用到實處吧:
WordPress 連接檢查器
想象一下你有一個文章數(shù)目龐大的博客,這些文章中包含了大量外部網(wǎng)站鏈接。一段時間之后,因為這樣那樣的原因,這些鏈接中相當(dāng)數(shù)量都失效了。要么是被和諧了,要么是整個站點都被功夫網(wǎng)了…
我們下面建立一個腳本,分析所有這些鏈接,找出打不開或者 404 的網(wǎng)站/網(wǎng)頁,并生成一個報告。
請注意,以下并不是一個真正可用的 WordPress 插件,僅僅是一段獨立功能的腳本而已,僅供演示,謝謝。
好,開始吧。首先,從數(shù)據(jù)庫中讀取所有這些鏈接:
| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 | // CONFIG$db_host = 'localhost';$db_user = 'root';$db_pass = '';$db_name = 'wordpress';$excluded_domains = array('localhost', 'www.mydomain.com');$max_connections = 10;// initialize some variables$url_list = array();$working_urls = array();$dead_urls = array();$not_found_urls = array();$active = null;// connect to MySQLif (!mysql_connect($db_host, $db_user, $db_pass)) {die('Could not connect: ' . mysql_error());}if (!mysql_select_db($db_name)) {die('Could not select db: ' . mysql_error());}// get all published posts that have links$q = "SELECT post_content FROM wp_postsWHERE post_content LIKE '%href=%'AND post_status = 'publish'AND post_type = 'post'";$r = mysql_query($q) or die(mysql_error());while ($d = mysql_fetch_assoc($r)) {// get all links via regexif (preg_match_all("!href="(.*?)"!", $d['post_content'], $matches)) {foreach ($matches[1] as $url) {// exclude some domains$tmp = parse_url($url);if (in_array($tmp['host'], $excluded_domains)) {continue;}// store the url$url_list []= $url;}}}// remove duplicates$url_list = array_values(array_unique($url_list));if (!$url_list) {die('No URL to check');} |
我們首先配置好數(shù)據(jù)庫,一系列要排除的域名($excluded_domains),以及最大并發(fā)連接數(shù)($max_connections)。然后,連接數(shù)據(jù)庫,獲取文章和包含的鏈接,把它們收集到一個數(shù)組中($url_list)。
下面的代碼有點復(fù)雜了,因此我將一小步一小步地詳細(xì)解釋:
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 | // 1. 批處理器$mh = curl_multi_init();// 2. 加入需要批處理的 URLfor ($i = 0; $i < $max_connections; $i++) {add_url_to_multi_handle($mh, $url_list);}// 3. 初始化處理do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);// 4. 主循環(huán)while ($active && $mrc == CURLM_OK) {// 5. 有活動鏈接if (curl_multi_select($mh) != -1) {// 6. 干活do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);// 7. 是否有信息?if ($mhinfo = curl_multi_info_read($mh)) {// 意味著該鏈接正常結(jié)束// 8. 從 curl 句柄獲取信息$chinfo = curl_getinfo($mhinfo['handle']);// 9. 死鏈?if (!$chinfo['http_code']) {$dead_urls []= $chinfo['url'];// 10. 404?} else if ($chinfo['http_code'] == 404) {$not_found_urls []= $chinfo['url'];// 11. 還能用} else {$working_urls []= $chinfo['url'];}// 12. 移除句柄curl_multi_remove_handle($mh, $mhinfo['handle']);curl_close($mhinfo['handle']);// 13. 加入新 URL,干活if (add_url_to_multi_handle($mh, $url_list)) {do {$mrc = curl_multi_exec($mh, $active);} while ($mrc == CURLM_CALL_MULTI_PERFORM);}}}}// 14. 結(jié)束curl_multi_close($mh);echo "==Dead URLs==n";echo implode("n",$dead_urls) . "nn";echo "==404 URLs==n";echo implode("n",$not_found_urls) . "nn";echo "==Working URLs==n";echo implode("n",$working_urls);// 15. 向批處理器添加 URLfunction add_url_to_multi_handle($mh, $url_list) {static $index = 0;// 如果還有 URL 沒用if ($url_list[$index]) {// 新建 curl 句柄$ch = curl_init();// 配置 curlcurl_setopt($ch, CURLOPT_URL, $url_list[$index]);// 不想輸出返回內(nèi)容curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 跟蹤重定向curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);// 不需要返回內(nèi)容,節(jié)省帶寬和時間curl_setopt($ch, CURLOPT_NOBODY, 1);// 加入到批處理器中curl_multi_add_handle($mh, $ch);// 撥一下計數(shù)器,下次調(diào)用該函數(shù)就能添加下一個 url 了$index++;return true;} else {// 沒有新的URL需要處理了return false;}} |
下面解釋一下以上代碼。列表的序號對應(yīng)著代碼注釋中的順序數(shù)字。
我把這個腳本在我的博客上跑了一遍(測試需要,有一些錯誤鏈接是故意加上的),結(jié)果如下:
另一些有用的cURL 選項
HTTP 認(rèn)證
如果某個URL請求需要基于 HTTP 的身份驗證,你可以使用下面的代碼:
| 1234567891011121314 | $url = "http://www.somesite.com/members/";$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 發(fā)送用戶名和密碼curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");// 你可以允許其重定向curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);// 下面的選項讓 cURL 在重定向后// 也能發(fā)送用戶名和密碼curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);$output = curl_exec($ch);curl_close($ch); |
FTP 上傳
PHP 自帶有 FTP 類庫, 但你也能用 cURL:
| 1234567891011121314151617 | // 開一個文件指針$file = fopen("/path/to/file", "r");// url里包含了大部分所需信息$url = "ftp://username:password@mydomain.com:21/path/to/new/file";$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 上傳相關(guān)的選項curl_setopt($ch, CURLOPT_UPLOAD, 1);curl_setopt($ch, CURLOPT_INFILE, $fp);curl_setopt($ch, CURLOPT_INFILESIZE, filesize("/path/to/file"));// 是否開啟ASCII模式 (上傳文本文件時有用)curl_setopt($ch, CURLOPT_FTPASCII, 1);$output = curl_exec($ch);curl_close($ch); |
翻墻術(shù)
你可以用代理發(fā)起 cURL 請求:
| 12345678910 | $ch = curl_init();curl_setopt($ch, CURLOPT_URL,'http://www.example.com');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// 指定代理地址curl_setopt($ch, CURLOPT_PROXY, '11.11.11.11:8080');// 如果需要的話,提供用戶名和密碼curl_setopt($ch, CURLOPT_PROXYUSERPWD,'user:pass');$output = curl_exec($ch);curl_close ($ch); |
回調(diào)函數(shù)
可以在一個 URL 請求過程中,讓 cURL 調(diào)用某指定的回調(diào)函數(shù)。例如,在內(nèi)容或者響應(yīng)下載的過程中立刻開始利用數(shù)據(jù),而不用等到完全下載完。
| 12345678910 | $ch = curl_init();curl_setopt($ch, CURLOPT_URL,'http://net.tutsplus.com');curl_setopt($ch, CURLOPT_WRITEFUNCTION,"progress_function");curl_exec($ch);curl_close ($ch);function progress_function($ch,$str) { echo $str; return strlen($str);} |
這個回調(diào)函數(shù)必須返回字串的長度,不然此功能將無法正常使用。
在 URL 響應(yīng)接收的過程中,只要收到一個數(shù)據(jù)包,這個函數(shù)就會被調(diào)用。
小結(jié)
今天我們一起學(xué)習(xí)了 cURL 庫的強(qiáng)大功能和靈活的擴(kuò)展性。希望你喜歡。下一次要發(fā)起 URL 請求時,考慮下 cURL 吧!
轉(zhuǎn)載于:https://www.cnblogs.com/17too/archive/2010/12/22/1913341.html
總結(jié)
以上是生活随笔為你收集整理的PHP 的 cURL库快速入门文档的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sharepoint学习笔记--Farm
- 下一篇: 嵌入式SQL程序的VC+SQL serv