C++中http client庫自己就少,好用的就更少了,在瞭解微軟開源的CPP REST SDK庫以前,我知道的C++ http client庫有libcurl(這個是C語言的),Qt的QNetworkAccessManager,還有VC++ http client,Qt的QNetworkAccessManager庫我在開發CZPlayer的時候用來下載過音樂、專輯圖片和歌詞,不得不說Qt提供的API仍是比較好用的,若是不涉及界面開發,難道咱們在linux上就只能用libcurl,在windows上就用VC++的http client?答案是否認的,在絕望之際CPP REST SDK出如今個人眼前,CPP REST SDK是微軟開源的基於PPL的異步http client,網絡層使用的是Boost.Asio,跨平臺,而且支持json解析,在使用CPP REST SDK以前要確保你已經安裝了boost和openssl,下面是微軟官方提供的例子。html
#include <cpprest/http_client.h> #include <cpprest/filestream.h> using namespace utility; // Common utilities like string conversions using namespace web; // Common features like URIs. using namespace web::http; // Common HTTP functionality using namespace web::http::client; // HTTP client features using namespace concurrency::streams; // Asynchronous streams int main(int argc, char* argv[]) { auto fileStream = std::make_shared<ostream>(); // Open stream to output file. pplx::task<void> requestTask = fstream::open_ostream(U("results.html")).then([=](ostream outFile) { *fileStream = outFile; // Create http_client to send the request. http_client client(U("http://www.bing.com/")); // Build request URI and start the request. uri_builder builder(U("/search")); builder.append_query(U("q"), U("cpprestsdk github")); return client.request(methods::GET, builder.to_string()); }) // Handle response headers arriving. .then([=](http_response response) { printf("Received response status code:%u\n", response.status_code()); // Write response body into the file. return response.body().read_to_end(fileStream->streambuf()); }) // Close the file stream. .then([=](size_t) { return fileStream->close(); }); // Wait for all the outstanding I/O to complete and handle any exceptions try { requestTask.wait(); } catch (const std::exception &e) { printf("Error exception:%s\n", e.what()); } return 0; }
上面的例子主要內容是訪問一個網站並將該內容保存在results.html裏面,這裏用到了微軟自家的PPL並行計算庫(還有一個是英特爾的TBB),該例子使用lambda表達式做爲異步回調的handler,打開文件流、請求、迴應和關閉文件流都是異步的,爲了方便起見,咱們將該例子改成同步方式。linux
auto fileStream = std::make_shared<ostream>(); ostream outFile = fstream::open_ostream(U("results.html")).get(); *fileStream = outFile; // Create http_client to send the request. http_client client(U("http://www.bing.com/")); // Build request URI and start the request. uri_builder builder(U("/search")); builder.append_query(U("q"), U("cpprestsdk github")); http_response response = client.request(methods::GET, builder.to_string()).get(); // Write response body into the file. response.body().read_to_end(fileStream->streambuf()).get(); fileStream->close().get();
使用同步方式的代碼比較清晰,能夠看到每個函數調用後面都會調用get函數,由於CPP REST SDK是基於PPL的,因此在request、read_to_end、close等函數調用後都會返回一個Task對象,而Task裏面的get和wait函數是等待任務執行完成。ios
我以前在看別人博客的時候看到每一篇博客前都有一副圖,看起來比較好看,聽博主講他是使用bing的每日一圖,後來我也效仿,我在主題之家找到了本身比較喜歡的圖片,最開始本身寫博客的時候都是直接去主題之家一頁一頁的找看,而後再下載圖片,寫了幾篇博客以後感受每次都要去下載圖片感受有點low,因此我決定直接將主題之家的某類型的圖片都下載下來,這樣選圖片就方便多了,下面是我在主題之家抓圖的代碼。git
#include <iostream> #include <string> #include <vector> #include <fstream> #include <memory> #include <regex> #include <cpprest/http_client.h> #include <cpprest/filestream.h> #include <cpprest/containerstream.h> // 請求並解析url bool get_result(const std::string& url, const std::string& pattern, std::vector<std::string>& vec) { try { web::http::client::http_client client(web::uri(utility::conversions::to_string_t(url))); web::http::http_response response = client.request(web::http::methods::GET).get(); concurrency::streams::stringstreambuf buffer; response.body().read_to_end(buffer).get(); std::string& str = buffer.collection(); // 使用C++11提供的正則表達式庫 std::regex r(pattern); for (std::sregex_iterator iter(str.begin(), str.end(), r), end; iter != end; ++iter) { std::cout << iter->str() << std::endl; vec.emplace_back(iter->str()); } } catch (std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; return false; } return true; } // 獲取圖片 bool get_result(const std::string& url, std::string& picture) { try { web::http::client::http_client client(web::uri(utility::conversions::to_string_t(url))); web::http::http_response response = client.request(web::http::methods::GET).get(); concurrency::streams::stringstreambuf buffer; response.body().read_to_end(buffer).get(); picture = buffer.collection(); } catch (std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; return false; } return true; } // 保存圖片 bool write_to_file(const std::string& file_path, const std::string& data) { try { std::ofstream file; file.open(file_path, std::ios::out | std::ios::binary); if (!file.good()) { return false; } file.write(data.c_str(), data.size()); file.close(); } catch (std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; return false; } return true; } int main() { // [1] 請求每一頁,將子頁面的url保存在sub_url_vec裏面 std::vector<std::string> sub_url_vec; std::string pattern = "/desk/[0-9]+.htm"; for (int i = 1; i <= 32; ++i) { // 創意主題 std::string url = "http://www.51ztzj.com/dbizhi/category_27_" + std::to_string(i) + ".htm#content_anchor"; std::cout << "Start get " << i << " page, url: " << url << std::endl; // 請求並解析url if (!get_result(url, pattern, sub_url_vec)) { std::cout << "Get " << i << " page failed" << std::endl; } } // 最終的圖片url:http://img.51ztzj.com//upload/image/20130220/2013022014_670x419.jpg // [2] 將子頁面的圖片url解析出來放入picture_url_vec std::vector<std::string> picture_url_vec; pattern = "http://img.51ztzj.com//upload/image/.+/.+_670x419.jpg"; for (std::size_t i = 0; i < sub_url_vec.size(); ++i) { std::string url = "http://www.51ztzj.com" + sub_url_vec[i]; std::cout << "Start get " << i + 1 << " sub page, url: " << url << std::endl; // 請求並解析url if (!get_result(url, pattern, picture_url_vec)) { std::cout << "Get " << i + 1 << " sub page failed" << std::endl; } } // [3] 最後遍歷picture_url_vec,而後一個一個的下載圖片 for (std::size_t i = 0; i < picture_url_vec.size(); ++i) { std::cout << "Start download " << i + 1 << " picture, url: " << picture_url_vec[i] << std::endl; std::string picture; // 獲取圖片 if (!get_result(picture_url_vec[i], picture)) { std::cout << "Download " << i + 1 << " picture failed" << std::endl; } std::string file_path = "./download/" + std::to_string(i) + ".jpg"; // 保存圖片 if (!write_to_file(file_path, picture)) { std::cout << "Write to file failed: " << i + 1 << std::endl; } } return 0; }