PHP 可以使用 file_get_content() 函數抓取網頁內容,但卻無法進行更複雜的處理,譬如檔案的上傳或下載、 Cookie 操作等等。而 PHP 的 cURL 提供了這些功能。
一、cURL簡介
cURL 是 PHP 的一個擴充庫。它可以與各種類型的伺服器、使用各種類型的協議進行串連和通訊。
它目前支援 http、https、ftp、gopher、telnet、dict、file 和 ldap 協議,同時也支援 HTTPS 認證、HTTP POST、 FTP 上傳、代理、cookies 和 使用者名稱 + 密碼的認證等。
二、cURL函數庫
常用函數
| 函數 |
描述 |
| curl_init() |
初始化 cURL 會話 |
| curl_setopt() |
設定 cURL 選項 |
curl_exec()
|
執行 cURL 會話 |
| curl_getinfo() |
擷取當前會話資訊 |
| curl_errno() |
返回最後一次的錯誤碼 |
| curl_error() |
返回當前會話最後一次的錯誤字串 |
| curl_close() |
關閉 cURL 會話 |
其他函數
| 函數 |
描述 |
| curl_copy_handle() |
複製一個 cURL 控制代碼和它的所有選項。 |
| curl_escape() |
返迴轉義字串,對給定的字串進行URL編碼。 |
| curl_file_create() |
建立一個 CURLFile 對象。 |
| curl_multi_add_handle() |
向 cURL 批處理會話中添加單獨的curl控制代碼。 |
| curl_multi_close() |
關閉一組 cURL 控制代碼。 |
| curl_multi_exec() |
運行當前 cURL 控制代碼的子串連。 |
| curl_multi_getcontent() |
如果設定了 CURLOPT_RETURNTRANSFER ,則返回擷取的輸出的文字資料流。 |
| curl_multi_info_read() |
擷取當前解析的 cURL 的相關傳輸資訊。 |
| curl_multi_init() |
返回一個新 cURL 批處理控制代碼。 |
| curl_multi_remove_handle() |
移除 cURL 批處理控制代碼資源中的某個控制代碼資源。 |
| curl_multi_select() |
等待所有 cURL 批處理中的活動串連。 |
| curl_multi_setopt() |
設定一個批處理 cURL 傳輸選項。 |
| curl_multi_strerror() |
返回描述錯誤碼的字串文本。 |
| curl_pause() |
暫停及恢複串連。 |
| curl_reset() |
重設 libcurl 的交談控制代碼的所有選項。 |
| curl_setopt_array() |
為 cURL 傳輸會話大量設定選項。 |
| curl_share_close() |
關閉 cURL 共用控制代碼。 |
| curl_share_init() |
初始化 cURL 共用控制代碼。 |
| curl_share_setopt() |
設定一個共用控制代碼的 cURL 傳輸選項。 |
| curl_strerror() |
返回錯誤碼的字串描述。 |
| curl_unescape() |
解碼 URL 編碼後的字串。 |
| curl_version() |
擷取 cURL 版本資訊。 |
三、實現流程
1. 初始化 cURL 會話
2. 設定 cURL 選項
3. 執行 cURL 會話
4. 擷取 cURL 資訊和(或)錯誤資訊(這一步可以沒有)
5. 關閉 cURL 控制代碼
這裡面最複雜的是第 2 步,cURL 的設定選項有很多,下面會結合執行個體來瞭解。
四、執行個體1:GET請求
GET 請求的流程就是 cURL 的一般流程。
在本機伺服器 localserver.com 根目錄準備一個測試指令碼 index.php,內容如下:
<?php $url = 'http://www.baidu.com'; // 初始化,獲得一個cURL控制代碼 $ch = curl_init(); // 設定選項 curl_setopt($ch, CURLOPT_URL, $url); // 請求URL curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //返回資料流,而不直接輸出 curl_setopt($ch, CURLOPT_HEADER, 0); // 無需響應的header頭 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); //連線逾時,秒為單位 // 執行並擷取返回內容 $output = curl_exec($ch); if($output === false){ $output = 'cURL error: ' . curl_error($ch); } // 釋放 cURL 控制代碼資源 curl_close($ch); print_r($output);?>
瀏覽器訪問本機伺服器首頁 localserver.com/index.php,顯示百度首頁。
五、執行個體2. POST請求
POST 請求需要設定兩個選項:
curl_setopt($ch, CURLOPT_POST, 1); // 表明POST請求curl_setopt($ch, CURLOPT_POSTFIELDS, $postData)); // POST提交資料
先在遠程伺服器 remoteserver.com 根目錄準備一個用於接收的指令碼 index.php,內容如下:
<?php $input = file_get_contents('php://input'); echo $input;?>
然後在本機伺服器 localserver.com 根目錄來寫用於 POST 請求的指令碼 index.php,內容如下:
<?php $url = 'http://remoteserver.com/index.php'; $data = array( 'fname'=> 'Daniel', 'lname' => 'Stenberg' ); // 初始化 $ch = curl_init(); // 設定選項 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ch, CURLOPT_POST, 1); // POST請求 curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); //POST資料。用http_build_query()轉換為“&”拼接的字串 // 執行並擷取返回內容 $output = curl_exec($ch); if($output === false){ $output = 'cURL error: ' . curl_error($ch); } // 釋放 cURL 控制代碼資源 curl_close($ch); print_r($output);?>
瀏覽器訪問 localserver.com/index.php,顯示如下:
fname=Daniel&lname=Stenberg
六、執行個體3. 上傳檔案
cURL 上傳檔案的思路是:在檔案路徑的前面添加"@"符號,並裝在請求欄位裡實現上傳。後台可以通過 $_FILES 擷取上傳檔案資訊。但 PHP5.6 以後,廢除了"@"符號,可以使用 CURLFile 類實現上傳。
先在遠程伺服器 remoteserver.com 根目錄準備一個用於接收的指令碼 index.php,內容如下:
<?php $action = $_POST['action']; if($action == 'uploadImage'){ $name = $_FILES['file']['name']; $tmpname = $_FILES['file']['tmp_name']; // 儲存到當前指令碼所在目錄 move_uploaded_file($tmpname, dirname(__FILE__).'/'.$name); $error = $_FILES['file']['error']; switch ($error) { case 0: echo '上傳成功'; break; case 1: echo '檔案大小超出 php.ini 限制'; break; case 2: echo '檔案大小超出 表單 MAX_FILE_SIZE 限制'; break; case 3: echo '檔案部分被上傳'; break; case 4: echo '沒有檔案被上傳'; break; case 6: echo '找不到臨時檔案夾'; break; case 7: echo '檔案寫入失敗'; break; default: $output = '未知錯誤'; } }?>
然後在本機伺服器 localserver.com 根目錄準備一個圖片檔案 test.jpg 和 cURL 上傳指令碼 index.php, 指令碼內容如下:
<?php $url = 'http://remoteserver.com/index.php'; $file = realpath(getcwd() . '/test.jpg'); $data = array( 'action' => 'uploadImage', 'file' => '@' . $file ); if(version_compare(PHP_VERSION, '5.6.0') > 0){ $data['file'] = new CURLFile($file); } // 初始化 $ch = curl_init(); // 設定選項 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // 執行並擷取返回內容 $output = curl_exec($ch); if($output === false){ $output = 'cURL error: ' . curl_error($ch); } // 釋放 cURL 控制代碼資源 curl_close($ch); print_r($output);?>
瀏覽器訪問 localserver.com/index.php,顯示如下:
上傳成功
查看遠程伺服器根目錄,發現多了一張剛才上傳的圖片。
七、執行個體4. 下載檔案
cURL 下載檔案的一個思路是:設定 cURL 選項 CURLOPT_FILE 為一個檔案指標,以此將請求的資源檔關聯到一個檔案流裡,這個檔案流一般是 fopen()函數的傳回值。使用檔案流將遠程檔案寫到本地,可以避免寫(下載)大檔案時可能的記憶體出錯。
在本機伺服器 localserver.com 根目錄來寫測試指令碼 index.php,內容如下:
<?php $url = 'http://remoteserver.com/test.jpg'; $file = './test.jpg'; $fp = fopen($file, 'w'); // 初始化 $ch = curl_init(); // 設定選項 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ch, CURLOPT_FILE, $fp); // 用於傳輸的檔案流,預設是STDOUT // 執行並擷取返回內容 $output = curl_exec($ch); if($output === false){ $output = 'cURL error: ' . curl_error($ch); } // 擷取已下載大小 $size_download = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD); // 釋放資源 fclose($fp); curl_close($ch); if ($size_download && $size_download == filesize($file)) { echo "下載成功"; } else { echo "下載失敗或不完整"; } ?>
瀏覽器訪問 localserver.com/index.php,顯示如下:
下載成功
查看本機伺服器根目錄,發現下載到了遠端圖片。
八、執行個體5. 批處理
cURL 有一個批處理控制代碼,通過開啟多個 cURL 控制代碼,並將這些控制代碼綁定到一個批處理控制代碼,然後在迴圈中依次處理每個 cURL 串連,可以實現非同步批處理,類似“多線程”。
在本機伺服器 localserver.com 根目錄來寫測試指令碼 index.php,內容如下:
<?php $urls = array( 'http://www.baidu.com', 'http://www.qidian.com' ); $count = count($urls); $ch = array(); // 建立批處理cURL控制代碼 $mh = curl_multi_init(); // 初始化每個cURL,並設定選項,綁定給批處理控制代碼 for ($i = 0; $i < $count; $i++) { $ch[$i] = curl_init(); curl_setopt($ch[$i], CURLOPT_URL, $urls[$i]); curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch[$i], CURLOPT_HEADER, 0); curl_setopt($ch[$i], CURLOPT_CONNECTTIMEOUT, 30); curl_multi_add_handle($mh, $ch[$i]); } // 執行批處理 $running = null; do { usleep(10000); // 延遲0.01秒,單位為百萬分之一秒 curl_multi_exec($mh, $running); // 非同步實現批處理,類似“多線程” } while($running > 0); // 擷取每個cURL的響應 $res = array(); for ($i = 0; $i < $count; $i++) { $res[$i] = curl_multi_getcontent($ch[$i]); } // 關閉全部控制代碼 for ($i = 0; $i < $count; $i++) { curl_multi_remove_handle($mh, $ch[$i]); } curl_multi_close($mh); print_r($res);?>
瀏覽器訪問 localserver.com/index.php,顯示“串連”的百度首頁和起點網首頁。