直接使用普通的 dev-cpp 就可以编译,编译时请加上 -lwinhttp 选项以链接库
测试环境:Windows11,Embarcadero Dev-C++
session 是一个封装好的类,需要用一个域名字符串(wchar_t[])和端口号(int,默认为443)来初始化。一些函数可以参考官方文档,这里不解释。
1. get 函数
向服务器读取信息
参数为域名后面的部分 + 最大长度(可选)。
比如我要爬取 blog.youkuaiyun.com/cbs_cbs,就可以使用 get("cbs_cbs")。
注:getex 可以发送带有 header 的 get 请求
2. post 函数
给服务器发送信息
参数为域名后面的部分 + 请求头 + 请求信息 + 最大长度(可选)。
3. 一些变量
int lasterror, lastlen;
wchar_t* lastheader;
bool getheader;
lasterror 储存了上次请求的错误代码(代码含义可以在microsoft官网查询);
lastlen 储存了上次请求返回的html长度;
lastheader 储存了上次请求的响应标头。
getheader 用于设置是否储存响应标头。(略微影响速度)
4. 提示
一些大网站,需要将 maxlen 参数调大,否则会内存溢出。不过,记得用 delete 释放内存,防止内存泄漏。
我加入了错误处理函数(session::Error),默认输出英文错误信息(中文容易出现乱码),用于方便调试,可删除。
每一个函数,有 string 版本和 wchar_t 版本,不建议混用
你可以自行修改 main 函数。
5.更新记录
- v3.2:更新了错误信息
#include<bits/stdc++.h>
#include<windows.h>
#include<winhttp.h>
using namespace std;
// -lwinhttp 链接库!
// -lwinhttp 链接库!
// -lwinhttp 链接库!
class session {
HINTERNET hSession, hConnect; // 整个会话的句柄;该域名连接的句柄
void Error(string fname, int warn=0) {
if(warn) cerr << "[session] : [非致命错误] 调用 " << fname << " 失败,详情:\n";
else cerr << "[session] : 调用 " << fname << " 失败,详情:\n";
int code = GetLastError();
TCHAR szErrorMsg[512];
DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), szErrorMsg, 512, NULL);
if (dwRet > 0) cerr << "[session] : 代码" << code << ", " << szErrorMsg << endl;
else cerr << "[session] : 代码" << code << endl;
}
char* http_base(wchar_t* type/*GET 或 POST*/, wchar_t* url, wchar_t* header, const char* data, int maxlen, bool outputheader=1) {
char* S=new char[maxlen], *T=S;
HINTERNET hRequest = WinHttpOpenRequest(hConnect, type, url, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
DWORD len = (data==nullptr ? 0 : strlen(data));
if(header != nullptr) { // 如果不为空就添加
if(!WinHttpAddRequestHeaders(hRequest, header, -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
printf("%S\n",header);
Error("WinHttpAddRequestHeaders", 1);
}
}
if(!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)data, len, len, 0)) {
Error("WinHttpSendRequest");
return lasterror=GetLastError(), nullptr;
}
if(!WinHttpReceiveResponse(hRequest, NULL)) {
Error("WinHttpReceiveResponse");
return lasterror=GetLastError(), nullptr;
}
DWORD dwSize = 0;
lastlen=0;
do {
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
Error("WinHttpQueryDataAvailable");
return lasterror=GetLastError(), nullptr;
}
DWORD dwDownloaded = 0;
if (!WinHttpReadData(hRequest, (LPVOID)T, dwSize, &dwDownloaded)) {
Error("WinHttpReadData");
return lasterror=GetLastError(), nullptr;
}
T += dwDownloaded;
lastlen += dwDownloaded;
} while (dwSize > 0);
if(getheader) {
if(!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX)) Error("WinHttpQueryHeaders", 1);
else {
LPWSTR lpOutBuffer = new wchar_t[dwSize / sizeof(wchar_t)];
if(!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, lpOutBuffer, &dwSize, WINHTTP_NO_HEADER_INDEX)) Error("WinHttpQueryHeaders", 1);
else lastheader = lpOutBuffer;
}
}
WinHttpCloseHandle(hRequest);
*T = '\0';
return S;
}
wchar_t* towstr(string str) {
int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* wct = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wct, len);
return wct;
}
public:
int lasterror, lastlen;
wchar_t* lastheader;
bool getheader;
// 443 https; 80 http
// 443 https; 80 http
// 443 https; 80 http
session(wchar_t* url, int port=443) {
lasterror=0;
hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(!hSession) Error("WinHttpOpen"), cerr<<"session 启动失败。如果你继续调用,则行为是未定义的。\n";
hConnect = WinHttpConnect(hSession,url,port,0);
if(!hConnect) Error("WinHttpConnect"), cerr<<"session 启动失败。如果你继续调用,则行为是未定义的。\n";
}
~session() {
WinHttpCloseHandle(hSession), WinHttpCloseHandle(hConnect);
}
char* get(wchar_t* url, int maxlen=100000) {
return http_base(L"GET", url, NULL, NULL, maxlen);
}
char* get(string url, int maxlen=100000) {
return get(towstr(url),maxlen);
}
char* getex(wchar_t* url, wchar_t* header, int maxlen=100000) {
return http_base(L"GET", url, header, NULL, maxlen);
}
char* getex(string url, string header, int maxlen=100000) {
return getex(towstr(url),towstr(header),maxlen);
}
char* post(wchar_t* url, wchar_t* header, char* data, int maxlen=100000) {
return http_base(L"POST", url, header, data, maxlen);
}
char* post(string url, string header, string data, int maxlen=100000) {
return post(towstr(url), towstr(header), const_cast<char*>(data.c_str()), maxlen);
}
};
session example_1(L"api.loj.ac");
session example_2(L"blog.youkuaiyun.com");
int main() {
SetConsoleOutputCP(CP_UTF8); // 设置 UTF-8,可选
char* s = example_1.post(L"/api/problem/getProblem", L"Content-Type: application/json", "{\"displayId\":1,\"localizedContentsOfLocale\":\"zh_CN\",\"tagsOfLocale\":\"zh_CN\",\"samples\":true,\"judgeInfo\":true}", 1000000);
printf("%s\n\n",s);
char* s2 = example_2.get(L"/cbs_cbs", 10000000);
printf("%s\n\n",s2);
return 0;
}
本文介绍了一个基于C++的简单爬虫模板,利用winhttp库实现网页内容抓取功能。该模板支持GET和POST请求,并提供了错误处理机制。
606

被折叠的 条评论
为什么被折叠?



