這一期給大家帶來的乾貨是curl
什麼是curl?
curl 是一個利用URL語法規定來傳輸文件和數據的工具,支持很多協議,如HTTP、FTP、TELNET等。PHP也支持 curl 庫。本文將介紹 curl 的一些高級特性,本期文章會教大家如何使用它。
為什麼要用 curl?
是的,我們可以通過其他辦法獲取網頁內容。大多數時候,我因為想偷懶,都直接用簡單的PHP函數:
代碼演示
$content = file_get_contents("http://www.xxx.com"); //直接獲取網頁上的內容
$lines = file("http://www.xxx.com");
readfile(http://www.xxx.com);
不過,這種做法缺乏靈活性和有效的錯誤處理。而且,你也不能用它完成一些高難度任務——比如處理coockies、驗證、表單提交、文件上傳等等,那麼這個時候curl就派上用處。
因為curl本身是一種功能強大的庫,支持很多不同的協議能提供 URL 請求相關的各種細節信息。
下面直接教大家如何使用curl
在學習更為複雜的功能之前,先來看一下請求的基本步驟:
// 1. 初始化
$ch = curl_init();
// 2. 設置選項,包括URL
curl_setopt($ch, CURLOPT_URL, "http://www.baidu.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
// 3. 執行並獲取HTML文檔內容
$output = curl_exec($ch);
// 4. 釋放curl句柄
curl_close($ch);
第二步(也就是 curl_setopt() 有一長串cURL參數可供設置,它們能指定URL請求的各個細節。要一次性全部介紹完比較難,今天我們只試一下那些更常用也更有用的選項。
$output = curl_exec($ch);
if ($output === FALSE) {//檢測是否錯誤
echo "curl Error: " . curl_error($ch);
}
curl_exec($ch);
$info = curl_getinfo($ch);//直接返回結果給info
返回的數組中包括了以下信息:
curl_setopt設置的參數
"URL" //資源網絡地址
"CONTENT_TYPE" //內容編碼
"HTTP_CODE" //HTTP狀態碼
"HEADER_SIZE" //header的大小
"REQUEST_SIZE" //請求的大小
"FILETIME" //文件創建時間
"SSL_VERIFY_RESULT" //SSL驗證結果
"REDIRECT_COUNT" //跳轉技術
"TOTAL_TIME" //總耗時
"NAMELOOKUP_TIME" //DNS查詢耗時
"CONNECT_TIME" //等待連接耗時
"PRETRANSFER_TIME" //傳輸前準備耗時
"SIZE_UPLOAD" //上傳數據的大小
"SIZE_DOWNLOAD" //下載數據的大小
"SPEED_DOWNLOAD" //下載速度
"SPEED_UPLOAD" //上傳速度
"DOWNLOAD_CONTENT_LENGTH" /下載內容的長度
"UPLOAD_CONTENT_LENGTH" //上傳內容的長度
"STARTTRANSFER_TIME" //開始傳輸的時間
"REDIRECT_TIME" //重定向耗時
我們利用 CURLOPT_HTTPHEADER 選項來設定我們發送出的HTTP請求頭信息(http headers),包括user agent信息和默認語言。然後我們來看看這些特定網站是否會把我們重定向到不同的URL。
以下為引用的內容:
// 測試用的URL
$urls = array(
"http://www.aaa.com",
"http://www.bbb.com",
"http://www.ccc.com"
);
$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: $url\\n";
foreach ($browsers as $test_name => $browser) {
$ch = curl_init();
// 設置 url
curl_setopt($ch, CURLOPT_URL, $url);
// 設置瀏覽器的特定header
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"User-Agent: {$browser['user_agent']}",
"Accept-Language: {$browser['language']}"
));
// 頁面內容我們並不需要
curl_setopt($ch, CURLOPT_NOBODY, 1);
// 只需返回HTTP header
curl_setopt($ch, CURLOPT_HEADER, 1);
// 返回結果,而不是輸出它
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
// 有重定向的HTTP頭信息嗎?
if (preg_match("!Location: (.*)!", $output, $matches)) {
echo "$test_name: redirects to $matches[1]\\n";
} else {
echo "$test_name: no redirection\\n";
}
}
echo "\\n\\n";
}
用POST方法發送數據
當發起GET請求時,數據可以通過查詢字串(query string)傳遞給一個URL:
http://www.google.com/search?q=demo
這種情況下你可能並不需要cURL來模擬。把這個URL丟給“file_get_contents()”就能得到相同結果。
不過有一些HTML表單是用POST方法提交的。這種表單提交時,數據是通過 HTTP請求體(request body) 發送,而不是查詢字串。例如,當使用CodeIgniter論壇的表單,無論你輸入什麼關鍵字,總是被POST到如下頁面:
http://cc.com/a/b/
你可以用PHP腳本來模擬這種URL請求。首先,新建一個可以接受並顯示POST數據的文件,我們給它命名為post_output.php:
print_r($_POST);
接下來,寫一段PHP腳本來執行cURL請求:
以下為引用的內容:
$url = "http://localhost/post_demo.php";
$post_data = array (
"foo" => "bar",
"query" => "demo",
"action" => "Submit"
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 我們在POST數據哦!
curl_setopt($ch, CURLOPT_POST, 1);
// 把post的變量加上
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
這段腳本發送一個POST請求給 post_demo.php ,這個頁面 $_POST 變量並返回輸出。
文件上傳
上傳文件和前面的POST十分相似。因為所有的文件上傳表單都是通過POST方法提交的。
首先新建一個接收文件的頁面,命名為 upload_info.php:
print_r($_FILES);
以下為引用的內容:
$url = "http://localhost/upload_info.php";
$post_data = array (
"foo" => "bar",
// 要上傳的本地文件地址
"upload" => "@C:/wamp/www/a.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變量一樣傳過去,不過記得在前面加上@符號。執行這段腳本應該會得到如下輸出:
cURL批處理(multi cURL)
cURL還有一個高級特性——批處理句柄(handle)。這一特性允許你同時或異步地打開多個URL連接。
下面是來自來自php.net的示例代碼:
以下為引用的內容:
// 創建兩個cURL資源
$ch1 = curl_init();
$ch2 = curl_init();
// 指定URL和適當的參數
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);
// 創建cURL批處理句柄
$mh = curl_multi_init();
// 加上前面兩個資源句柄
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);
// 預定義一個狀態變量
$active = null;
// 執行批處理
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);
}
}
// 關閉各個句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
這裡要做的就是打開多個cURL句柄並指派給一個批處理句柄。然後你就只需在一個while循環裡等它執行完畢。
這個示例中有兩個主要循環。第一個 do-while 循環重複調用 curl_multi_exec() 。這個函數是無隔斷(non-blocking)的,但會盡可能少地執行。它返回一個狀態值,只要這個值等於常量 CURLM_CALL_MULTI_PERFORM ,就代表還有一些刻不容緩的工作要做(例如,把對應URL的http頭信息發送出去)。也就是說,我們需要不斷調用該函數,直到返回值發生改變。
而接下來的 while 循環,只在 $active 變量為 true 時繼續。這一變量之前作為第二個參數傳給了 curl_multi_exec() ,代表只要批處理句柄中是否還有活動連接。接著,我們調用 curl_multi_select() ,在活動連接(例如接受服務器響應)出現之前,它都是被“屏蔽”的。這個函數成功執行後,我們又會進入另一個 do-while 循環,繼續下一條URL。
HTTP 認證
如果某個URL請求需要基於 HTTP 的身份驗證,你可以使用下面的代碼:
複製內容到剪貼板代碼:
以下為引用的內容:
$url = "http://www.somesite.com/members/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 發送用戶名和密碼
curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");
// 你可以允許其重定向
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
// 下面的選項讓 cURL 在重定向後
// 也能發送用戶名和密碼
curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
$output = curl_exec($ch);
curl_close($ch);
FTP 上傳
PHP 自帶有 FTP 類庫, 但你也能用 cURL:
以下為引用的內容:
// 開一個文件指針
$file = fopen("/path/to/file", "r");
// url裡包含了大部分所需信息
$url = "ftp://username:[email protected]:21/path/to/new/file";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 上傳相關的選項
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);
今天我們一起學習了cURL庫的強大功能和靈活的擴展性,希望大家看完之後有大家也能夠快速上手
閱讀更多 OR編程 的文章