IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++网络爬虫 -> 正文阅读

[C++知识库]C++网络爬虫

程序代码1

#include <iostream>
#include <iomanip>  //include setw() & setfill()
#include <windows.h>
#include <wininet.h>
//#include <string>
#include <regex>    //include keywords: regex & sregex_token_iterator 正则表达式
//#include <vector>
#include <sstream>  //stringstream is used in "string int2str(int i)"
#include <fstream>  //include ofstream等文件处理关键字和函数
#pragma comment(lib,"WinInet.lib") 
//wininet.lib include InternetCloseHandle() & InternetOpen() & InternetConnect() & HttpOpenRequest

/*  程序使用说明:
	1. 在下载结束前关闭程序,可能会导致最后一张图部分失真
	2. 关于 "HTTP/1.1" 字符串:
		HTTP 1.1支持持久连接(HTTP/1.1的默认模式使用带流水线的持久连接)
		在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟
		一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输
		但每个单独的网页文件的请求和应答仍然需要使用各自的连接
		HTTP 1.1还允许客户端不用等待上一次请求结果返回就能发出下一次请求
		但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果
		以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间
	3. 
*/

using namespace std;

string HttpRequest(string strUrl, string strMethod = "GET", string strPostData = "")
{
	/*	关于传递进来的形参说明:
		strUrl是完整的地址,如 http://198.168.0.1::80/api/device/notify
		strMethod是方法 “GET”或者“POST”
		strPostData是要post上去的数据,如果strMethod使用GET方法,则strPostData是NULL,即空字符串""
		返回值strResponse(string)是服务器返回的数据
	*/

	BOOL bRet;
	short sPort = 80;
	char* lpHostName, * lpUrl, * lpMethod, * lpPostData;
	int pos, nPostDataLen = strPostData.size();
	string tmpUrl, strResponse = "";

	//	解析URL
	/*	URL结构:协议://主机名或IP地址[:端口号] / 路径 / [;参数][?query]#fragment
	*	用到的字符串处理函数: 
	*	str.find(字符串s) 在字符串str中寻找字符串s, 若成功找到, 则返回s在str中的位置,若没找到,则返回str.npos
	*	str.erase(开始擦除的位置, 待擦除的长度) 在字符串str中擦除一定长度的字符
	*	substr(size_t Off, size_t Count) 生成(从Off位置开始截取, 长度为Count)的子字符串
	*/
	//消除strURL中的空格
	while ((pos = strUrl.find(" ")) != strUrl.npos) //pos是find()函数返回的空格符位置
		strUrl.erase(pos, 1); //删除strURL字符串中的空格符;

	//消除strURL中的协议名(http/https)
	if (strUrl.substr(0, 7) == "http://") //如果strURL的前7个字符是http协议的格式; 
		strUrl = strUrl.substr(7, strUrl.size() - 1); //删除网址中的协议名
	if (strUrl.substr(0, 8) == "https://") //如果strURL的前8个字符是https协议的格式
		strUrl = strUrl.substr(8, strUrl.size() - 1);
	if (strUrl.empty()) //如果strURL是空串,则直接返回空串""
		return strResponse; //此时strResponse = ""

	//分隔 主机名/IP地址 和 路径
	if ((pos = strUrl.find("/")) != strUrl.npos)
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1); //tmpUrl 临时存储 路径(Path)
		strUrl = strUrl.substr(0, pos); //strUrl 现在临时存储 主机名(hostname或IP地址)
	}
	else tmpUrl = "/"; //如果strUrl中没有路径,则设置默认路径为"/"

	//在C++11中str.data()与str.c_str()同义,返回一个指向字符数组的只读指针,该数组包含一个以空字符结尾的字符序列,需要现用现强制转换
	lpUrl = (char*)tmpUrl.data(); //路径 指针
	lpMethod = (char*)strMethod.data(); //方法 指针
	lpHostName = (char*)strUrl.data(); //主机名[:端口号] 指针
	lpPostData = (char*)strPostData.data(); //待上传数据 指针

	//分隔 主机名 和 端口号
	if ((pos = strUrl.find(":")) != strUrl.npos)
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1); //tmpUrl 临时存储 端口号
		strUrl = strUrl.substr(0, pos); //strUrl 临时存储 主机名
		sPort = (short)atoi(tmpUrl.c_str()); //端口号
	}

	HINTERNET hInternet, hConnect, hRequest;
	hInternet = hConnect = hRequest = NULL;

	hInternet = (HINSTANCE)InternetOpen("User-Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (!hInternet)
	{
		//cout << endl << strResponse << endl;
		return strResponse;
	}


	/*	InternetConnect()函数:打开给定站点的文件传输协议 (FTP) 或 HTTP 会话
	*	hInternet:上一次调用InternetOpen返回的句柄
	*	sPort:端口号,常见的有:INTERNET_DEFAULT_HTTP_PORT(80),INTERNET_DEFAULT_HTTPS_PORT(443)
	*	NULL:第4个参数表示登录的用户名。参数为NULL表示该函数使用适当的默认值,类似"匿名"
	*/
	hConnect = (HINSTANCE)InternetConnect(hInternet, lpHostName, sPort, NULL, "HTTP/1.1", INTERNET_SERVICE_HTTP, 0, 0);
	if (!hConnect) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		//在调用应用程序使用完HINTERNET句柄后,必须使用InternetCloseHandle函数将其关闭
		//cout << endl << strResponse << endl;
		return strResponse;
	}

	/*	HttpOpenRequest()函数:创建 HTTP 请求句柄
	*	hConnect:InternetConnect返回的 HTTP 会话句柄
	*	lpMethod作为形参传入时="GET",后lpMethod = (char*)strMethod.data()时内容还是"GET"
	*	INTERNET_FLAG_RELOAD:第7个参数互联网选项 强制从源服务器下载所请求的文件、对象或目录列表,而不是从缓存中下载
	*/
	hRequest = (HINSTANCE)HttpOpenRequest(hConnect, lpMethod, lpUrl, "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, 0);
	if (!hRequest) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		if (hConnect) InternetCloseHandle(hConnect);
		//cout << endl << strResponse << endl;
		return strResponse;
	}


	/*	HttpSendRequest()函数:将指定的请求发送到 HTTP 服务器(允许调用者发送超出通常传递给HttpSendRequestEx 的额外数据<--没用到这个)
	*	hRequest:HttpOpenRequest函数返回的句柄
	*	NULL, 0:指向附加标头的指针 和 附加标头的大小
	*	lpPostData指向空字符串,nPostDataLen应该等于0或者1
	*	bRet存储返回值,如果成功则TRUE,否则FALSE
	*/
	bRet = HttpSendRequest(hRequest, NULL, 0, lpPostData, nPostDataLen);

	while (true) 
	{
		char cReadBuffer[4096];
		unsigned long lNumberOfBytesRead;
		bRet = InternetReadFile(hRequest, cReadBuffer, sizeof(cReadBuffer) - 1, &lNumberOfBytesRead);
		if (!bRet || !lNumberOfBytesRead) break;
		cReadBuffer[lNumberOfBytesRead] = 0;
		strResponse = strResponse + cReadBuffer;
	}

	//不再需要使用这些句柄时,调用InternetCloseHandle()函数关闭这些HINTERNET句柄,而且关闭顺序和创建相反
	if (hRequest) InternetCloseHandle(hRequest);
	if (hConnect) InternetCloseHandle(hConnect);
	if (hInternet) InternetCloseHandle(hInternet);
	//cout << endl << strResponse << endl;
	return strResponse;
}

bool DownloadFile(string strUrl, string strFullFileName, unsigned int BUF_SIZE_KB = 1)
{
	size_t pos = 0;
	short sPort = 80;
	BOOL bRet;
	char* lpHostName, * lpUrl;
	string tmpUrl, strResponse = "";

	while ((pos = strUrl.find(" ")) != strUrl.npos) 
		strUrl.erase(pos, 1); //删除网址中的空格
	if (strUrl.substr(0, 7) == "http://") 
		strUrl = strUrl.substr(7, strUrl.size() - 1); //删除网址中的协议名
	if (strUrl.substr(0, 8) == "https://") 
		strUrl = strUrl.substr(8, strUrl.size() - 1);
	if (strUrl.empty()) return false;
	if ((pos = strUrl.find("/")) != strUrl.npos) 
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1);
		strUrl = strUrl.substr(0, pos);
	}
	else tmpUrl = "/";

	lpUrl = (char*)tmpUrl.data();
	lpHostName = (char*)strUrl.data();

	if ((pos = strUrl.find(":")) != strUrl.npos) 
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1);
		strUrl = strUrl.substr(0, pos);
		sPort = (short)atoi(tmpUrl.c_str());
	}

	HINTERNET hInternet, hConnect, hRequest;
	hInternet = hConnect = hRequest = NULL;
	hInternet = (HINSTANCE)InternetOpen("User-Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (!hInternet) return false;

	hConnect = (HINSTANCE)InternetConnect(hInternet, lpHostName, sPort, NULL, "HTTP/1.1", INTERNET_SERVICE_HTTP, 0, 0);
	if (!hConnect) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		return false;
	}
	char lpMethod[] = "GET";
	hRequest = (HINSTANCE)HttpOpenRequest(hConnect,lpMethod, lpUrl, "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, 0);
	if (!hRequest) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		if (hConnect) InternetCloseHandle(hConnect);
		return false;
	}

	bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);

	BUF_SIZE_KB = (BUF_SIZE_KB == 0) ? 1024 : BUF_SIZE_KB * 1024;
	char* buf = new char[BUF_SIZE_KB]; //char buf[BUF_SIZE_KB];
	DWORD buf_len, buf_read;
	buf_len = buf_read = BUF_SIZE_KB;

	FILE* fp = fopen(strFullFileName.c_str(), "wb");
	while (true) 
	{
		InternetReadFile(hRequest, buf, buf_len, &buf_read);
		if (buf_read == 0) break;
		fwrite(buf, 1, buf_read, fp);
	}
	delete[] buf;
	fclose(fp);

	if (hRequest) InternetCloseHandle(hRequest);
	if (hConnect) InternetCloseHandle(hConnect);
	if (hInternet) InternetCloseHandle(hInternet);

	return true;
}

int regexSplit(string& str, const string str_reg, vector<string>& vect, int pos = 0)
{
	if (pos != -1) pos = 0;  //pos=0 匹配到的位置,pos=-1匹配位置的前一字串
	regex myPattern(str_reg);
	sregex_token_iterator it(str.begin(), str.end(), myPattern, pos);//提取HTML注释
	//begin()函数返回一个迭代器,指向字符串的第一个元素.
	//end()函数返回一个迭代器,指向字符串的末尾(最后一个字符的下一个位置).
	sregex_token_iterator end;
	for (; it != end; ++it) vect.push_back(*it);//182~186删除注释并打印HTML标记
	//push_back()函数的用法:函数将一个新的元素(括号内元素)加到vector的最后面,位置为当前最后一个元素的下一个元素
	return vect.size();  //if 0 没有匹配到,else 匹配到的个数
}

string replaceAll(string& s, const string sub1, const string sub2)
{	//字符串中指定字符串sub2替换s所有的子串sub1
	size_t len, pos = 0;
	if (s.empty() || sub1.empty() || sub2.empty()) return s;
	if (s.find(sub1) == s.npos) return s; //sub1不是s的子串就退出
	len = sub1.size();
	while ((pos = s.find(sub1, pos)) != s.npos)
		s.replace(pos++, len, sub2);
	return s;
}

string int2str(int i)
{
	string s;
	stringstream ss;
	ss << setw(5) << setfill('0') << i;
	s = ss.str();
	ss.clear();
	return s;
}

int main()
{
	int i = 0;
	bool bRet;
	size_t pos;
	string Html, url, reg;
	vector <string> vect, vimg;
	//url = "https://esports.zol.com.cn/slide/688/6885385_1.html";
	url = "https://esports.zol.com.cn/slide/754/7542170_1.html#p=2";
	//url = "http://www.doc88.com/p-5337656471505.html"; //道客巴巴图片文件
	Html = HttpRequest(url);
	cout << "1-" << endl;

	while ((pos = Html.find("tutie")) != string::npos) {
		reg = "t_s1280x720(.*?).jpg"; //查找下载目标的网址装入vector
		regexSplit(Html, reg, vect);
		for (auto& v : vect) { //抓取在vector里的图片地址,逐个下载
			replaceAll(v, "\\/", "/");  //网站把图片地址每个/前都插入\字符,全部替换掉
			url = "http://article-fd.zol-img.com.cn/";
			bRet = DownloadFile(url + v, "C:\\Users\\18049\\Desktop\\pic\\pic" + int2str(++i) + ".jpg");
			//注意:D:\Pictures 文件目录是否存在,代码没作判断
			if (bRet) {
				cout << ".";
				if (i % 20 == 0) cout << endl;
				if (i % 100 == 0) {
					system("cls");
					cout << i + 1 << "-" << endl;
				}
				vimg.push_back(url + v); //记录下载成功的链接
				if (i >= 10000) return 0; //抓取满10000张退出,或者循环到网站没有下一图集标记为止
			}
		}
		reg = "<a href=\"/slide/(.*?)tutie"; //找出下一图集的地址
		vect.clear();
		regexSplit(Html, reg, vect);
		url = vect.at(0); //格式:<a href="/slide/688/6886276_1.html" class="tutie
		url = "https://esports.zol.com.cn" + url.substr(9, url.size() - 23);
		vect.clear();
		Html = HttpRequest(url);
	}
	
	/*
	* //本来这段说是存入文档但是由于我没找到那个文档,并且把这小段代码注释掉也没有影响到爬取过程
	ofstream out_file("DownList.txt"); //下载成功的链接保存到文件
	if (out_file) {
		for (auto v : vimg)
			out_file << v << endl; //输出到文件
		out_file.close();
	}*/
	return 0;
}

程序代码2 - 封装成类

初次运行爬取速度会很慢,第二次就好很多了

#include <iostream>
#include <iomanip>  //include setw() & setfill()
#include <windows.h>
#include <wininet.h>
//#include <string>
#include <regex>    //include keywords: regex & sregex_token_iterator 正则表达式
//#include <vector>
#include <sstream>  //stringstream is used in "string int2str(int i)"
#include <fstream>  //include ofstream等文件处理关键字和函数
#pragma comment(lib,"WinInet.lib") 
//wininet.lib include InternetCloseHandle() & InternetOpen() & InternetConnect() & HttpOpenRequest

/*  程序使用说明:
	1. 在下载结束前关闭程序,可能会导致最后一张图部分失真
	2. 关于 "HTTP/1.1" 字符串:
		HTTP 1.1支持持久连接(HTTP/1.1的默认模式使用带流水线的持久连接)
		在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟
		一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接
		HTTP 1.1还允许客户端不用等待上一次请求结果返回就能发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果
		以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。
	3. 
*/

using namespace std;

class PictureDownloader
{
public:
	PictureDownloader();
	string HttpRequest(string strUrl, string strMethod = "GET", string strPostData = "");
	bool DownloadFile(string strUrl, string strFullFileName, unsigned int BUF_SIZE_KB = 1);
	int regexSplit(string& str, const string str_reg, vector<string>& vect, int pos = 0);
	string replaceAll(string& s, const string sub1, const string sub2);
	string int2str(int i);
};

PictureDownloader::PictureDownloader()
{
	int i = 0;
	bool bRet;
	size_t pos;
	string Html, url, reg;
	vector <string> vect, vimg;
	//url = "https://esports.zol.com.cn/slide/688/6885385_1.html";
	url = "https://esports.zol.com.cn/slide/754/7542170_1.html#p=2";
	//url = "http://www.doc88.com/p-5337656471505.html"; //道客巴巴图片文件
	Html = HttpRequest(url);
	cout << "1-" << endl;

	while ((pos = Html.find("tutie")) != string::npos) 
	{
		reg = "t_s1280x720(.*?).jpg"; //查找下载目标的网址装入vector
		regexSplit(Html, reg, vect);
		for (auto& v : vect) 
		{ //抓取在vector里的图片地址,逐个下载
			replaceAll(v, "\\/", "/");  //网站把图片地址每个/前都插入\字符,全部替换掉
			url = "http://article-fd.zol-img.com.cn/";
			bRet = DownloadFile(url + v, "C:\\Users\\18049\\Desktop\\pic\\pic" + int2str(++i) + ".jpg");
										//注意:D:\Pictures 文件目录是否存在,代码中没作判断
			if (bRet) 
			{
				cout << ".";
				if (i % 20 == 0) cout << endl;
				if (i % 100 == 0) {
					system("cls");
					cout << i + 1 << "-" << endl;
				}
				vimg.push_back(url + v); //记录下载成功的链接
				if (i >= 10000) exit(0); //抓取满10000张退出,或者循环到网站没有下一图集标记为止
			}
		}
		reg = "<a href=\"/slide/(.*?)tutie"; //针对那个特定的网址才有效的正则表达式    目的:找出下一图集的地址
		vect.clear();
		regexSplit(Html, reg, vect);
		url = vect.at(0); //格式:<a href="/slide/688/6886276_1.html" class="tutie
		url = "https://esports.zol.com.cn" + url.substr(9, url.size() - 23);
		vect.clear();
		Html = HttpRequest(url);
	}

	/*
	* //本来这段说是存入文档但是由于我没找到那个文档,并且把这小段代码注释掉也没有影响到爬取过程
	ofstream out_file("DownList.txt"); //下载成功的链接保存到文件
	if (out_file) {
		for (auto v : vimg)
			out_file << v << endl; //输出到文件
		out_file.close();
	}*/
}

string PictureDownloader::HttpRequest(string strUrl, string strMethod, string strPostData)
{
	/*	关于传递进来的形参说明:
		strUrl是完整的地址,如 http://198.168.0.1::80/api/device/notify
		strMethod是方法 “GET”或者“POST”
		strPostData是要post上去的数据,如果strMethod使用GET方法,则strPostData是NULL,即空字符串""
		返回值strResponse(string)是服务器返回的数据
	*/

	BOOL bRet;
	short sPort = 80;
	char* lpHostName, * lpUrl, * lpMethod, * lpPostData;
	int pos, nPostDataLen = strPostData.size();
	string tmpUrl, strResponse = "";

	//	解析URL
	/*	URL结构:协议://主机名或IP地址[:端口号] / 路径 / [;参数][?query]#fragment
	*	用到的字符串处理函数: 
	*	str.find(字符串s) 在字符串str中寻找字符串s, 若成功找到, 则返回s在str中的位置,若没找到,则返回str.npos
	*	str.erase(开始擦除的位置, 待擦除的长度) 在字符串str中擦除一定长度的字符
	*	substr(size_t Off, size_t Count) 生成(从Off位置开始截取, 长度为Count)的子字符串
	*/
	//消除strURL中的空格
	while ((pos = strUrl.find(" ")) != strUrl.npos) //pos是find()函数返回的空格符位置
		strUrl.erase(pos, 1); //删除strURL字符串中的空格符;

	//消除strURL中的协议名(http/https)
	if (strUrl.substr(0, 7) == "http://") //如果strURL的前7个字符是http协议的格式; 
		strUrl = strUrl.substr(7, strUrl.size() - 1); //删除网址中的协议名
	if (strUrl.substr(0, 8) == "https://") //如果strURL的前8个字符是https协议的格式
		strUrl = strUrl.substr(8, strUrl.size() - 1);
	if (strUrl.empty()) //如果strURL是空串,则直接返回空串""
		return strResponse; //此时strResponse = ""

	//分隔 主机名/IP地址 和 路径
	if ((pos = strUrl.find("/")) != strUrl.npos)
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1); //tmpUrl 临时存储 路径(Path)
		strUrl = strUrl.substr(0, pos); //strUrl 现在临时存储 主机名(hostname或IP地址)
	}
	else tmpUrl = "/"; //如果strUrl中没有路径,则设置默认路径为"/"

	//在C++11中str.data()与str.c_str()同义,返回一个指向字符数组的const char*指针,该数组包含一个以空字符结尾的字符序列,需要现用现强制转换
	lpUrl = (char*)tmpUrl.data(); //路径 指针
	lpMethod = (char*)strMethod.data(); //方法 指针
	lpHostName = (char*)strUrl.data(); //主机名[:端口号] 指针
	lpPostData = (char*)strPostData.data(); //待上传数据 指针

	//分隔 主机名 和 端口号
	if ((pos = strUrl.find(":")) != strUrl.npos)
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1); //tmpUrl 临时存储 端口号
		strUrl = strUrl.substr(0, pos); //strUrl 临时存储 主机名
		sPort = (short)atoi(tmpUrl.c_str()); //端口号
	}

	HINTERNET hInternet, hConnect, hRequest;
	hInternet = hConnect = hRequest = NULL;

	hInternet = (HINSTANCE)InternetOpen("User-Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (!hInternet)
	{
		//cout << endl << strResponse << endl;
		return strResponse;
	}


	/*	InternetConnect()函数:打开给定站点的文件传输协议 (FTP) 或 HTTP 会话
	*	hInternet:上一次调用InternetOpen返回的句柄
	*	sPort:端口号,常见的有:INTERNET_DEFAULT_HTTP_PORT(80),INTERNET_DEFAULT_HTTPS_PORT(443)
	*	NULL:第4个参数表示登录的用户名。参数为NULL表示该函数使用适当的默认值,类似"匿名"
	*/
	hConnect = (HINSTANCE)InternetConnect(hInternet, lpHostName, sPort, NULL, "HTTP/1.1", INTERNET_SERVICE_HTTP, 0, 0);
	if (!hConnect) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		//在调用应用程序使用完HINTERNET句柄后,必须使用InternetCloseHandle函数将其关闭
		//cout << endl << strResponse << endl;
		return strResponse;
	}

	/*	HttpOpenRequest()函数:创建 HTTP 请求句柄
	*	hConnect:InternetConnect返回的 HTTP 会话句柄
	*	lpMethod作为形参传入时="GET",后lpMethod = (char*)strMethod.data()时内容还是"GET"
	*	INTERNET_FLAG_RELOAD:第7个参数互联网选项 强制从源服务器下载所请求的文件、对象或目录列表,而不是从缓存中下载
	*/
	hRequest = (HINSTANCE)HttpOpenRequest(hConnect, lpMethod, lpUrl, "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, 0);
	if (!hRequest) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		if (hConnect) InternetCloseHandle(hConnect);
		//cout << endl << strResponse << endl;
		return strResponse;
	}


	/*	HttpSendRequest()函数:将指定的请求发送到 HTTP 服务器(允许调用者发送超出通常传递给HttpSendRequestEx 的额外数据<--没用到这个)
	*	hRequest:HttpOpenRequest函数返回的句柄
	*	NULL, 0:指向附加标头的指针 和 附加标头的大小
	*	lpPostData指向空字符串,nPostDataLen应该等于0或者1
	*	bRet存储返回值,如果成功则TRUE,否则FALSE
	*/
	bRet = HttpSendRequest(hRequest, NULL, 0, lpPostData, nPostDataLen);

	while (true) 
	{
		char cReadBuffer[4096];
		unsigned long lNumberOfBytesRead;
		bRet = InternetReadFile(hRequest, cReadBuffer, sizeof(cReadBuffer) - 1, &lNumberOfBytesRead);
		if (!bRet || !lNumberOfBytesRead) break;
		cReadBuffer[lNumberOfBytesRead] = 0;
		strResponse = strResponse + cReadBuffer;
	}

	//不再需要使用这些句柄时,调用InternetCloseHandle()函数关闭这些HINTERNET句柄,而且关闭顺序和创建相反
	if (hRequest) InternetCloseHandle(hRequest);
	if (hConnect) InternetCloseHandle(hConnect);
	if (hInternet) InternetCloseHandle(hInternet);
	//cout << endl << strResponse << endl;
	return strResponse;
}

bool PictureDownloader::DownloadFile(string strUrl, string strFullFileName, unsigned int BUF_SIZE_KB)
{
	size_t pos = 0;
	short sPort = 80;
	BOOL bRet;
	char* lpHostName, * lpUrl;
	string tmpUrl, strResponse = "";

	while ((pos = strUrl.find(" ")) != strUrl.npos) 
		strUrl.erase(pos, 1); //删除网址中的空格
	if (strUrl.substr(0, 7) == "http://") 
		strUrl = strUrl.substr(7, strUrl.size() - 1); //删除网址中的协议名
	if (strUrl.substr(0, 8) == "https://") 
		strUrl = strUrl.substr(8, strUrl.size() - 1);
	if (strUrl.empty()) return false;
	if ((pos = strUrl.find("/")) != strUrl.npos) 
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1);
		strUrl = strUrl.substr(0, pos);
	}
	else tmpUrl = "/";

	lpUrl = (char*)tmpUrl.data();
	lpHostName = (char*)strUrl.data();

	if ((pos = strUrl.find(":")) != strUrl.npos) 
	{
		tmpUrl = strUrl.substr(pos + 1, strUrl.size() - 1);
		strUrl = strUrl.substr(0, pos);
		sPort = (short)atoi(tmpUrl.c_str());
	}

	HINTERNET hInternet, hConnect, hRequest;
	hInternet = hConnect = hRequest = NULL;
	hInternet = (HINSTANCE)InternetOpen("User-Agent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
	if (!hInternet) return false;

	hConnect = (HINSTANCE)InternetConnect(hInternet, lpHostName, sPort, NULL, "HTTP/1.1", INTERNET_SERVICE_HTTP, 0, 0);
	if (!hConnect) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		return false;
	}
	char lpMethod[] = "GET";
	hRequest = (HINSTANCE)HttpOpenRequest(hConnect,lpMethod, lpUrl, "HTTP/1.1", NULL, NULL, INTERNET_FLAG_RELOAD, 0);
	if (!hRequest) 
	{
		if (hInternet) InternetCloseHandle(hInternet);
		if (hConnect) InternetCloseHandle(hConnect);
		return false;
	}

	bRet = HttpSendRequest(hRequest, NULL, 0, NULL, 0);

	BUF_SIZE_KB = (BUF_SIZE_KB == 0) ? 1024 : BUF_SIZE_KB * 1024;
	char* buf = new char[BUF_SIZE_KB]; //char buf[BUF_SIZE_KB];
	DWORD buf_len, buf_read;
	buf_len = buf_read = BUF_SIZE_KB;

	FILE* fp = fopen(strFullFileName.c_str(), "wb");
	while (true) 
	{
		InternetReadFile(hRequest, buf, buf_len, &buf_read);
		if (buf_read == 0) break;
		fwrite(buf, 1, buf_read, fp);
	}
	delete[] buf;
	fclose(fp);

	if (hRequest) InternetCloseHandle(hRequest);
	if (hConnect) InternetCloseHandle(hConnect);
	if (hInternet) InternetCloseHandle(hInternet);

	return true;
}

int PictureDownloader::regexSplit(string& str, const string str_reg, vector<string>& vect, int pos)
{
	if (pos != -1) pos = 0;  //pos=0 匹配到的位置,pos=-1匹配位置的前一字串
	regex myPattern(str_reg);
	sregex_token_iterator it(str.begin(), str.end(), myPattern, pos);//提取HTML注释
	//begin()函数返回一个迭代器,指向字符串的第一个元素.
	//end()函数返回一个迭代器,指向字符串的末尾(最后一个字符的下一个位置).
	sregex_token_iterator end;
	for (; it != end; ++it) vect.push_back(*it);//182~186删除注释并打印HTML标记
	//push_back()函数的用法:函数将一个新的元素(括号内元素)加到vector的最后面,位置为当前最后一个元素的下一个元素
	return vect.size();  //if 0 没有匹配到,else 匹配到的个数
}

string PictureDownloader::replaceAll(string& s, const string sub1, const string sub2)
{	//字符串中指定字符串sub2替换s所有的子串sub1
	size_t len, pos = 0;
	if (s.empty() || sub1.empty() || sub2.empty()) return s;
	if (s.find(sub1) == s.npos) return s; //sub1不是s的子串就退出
	len = sub1.size();
	while ((pos = s.find(sub1, pos)) != s.npos)
		s.replace(pos++, len, sub2);
	return s;
}

string PictureDownloader::int2str(int i)
{
	string s;
	stringstream ss;
	ss << setw(5) << setfill('0') << i;
	s = ss.str();
	ss.clear();
	return s;
}

int main()
{
	PictureDownloader pd;
	return 0;
}

更改配置

  • Visual Studio 2019写代码前需要更改项目属性
  1. 添加_CRT_SECURE_NO_DEPRECATE到预处理器定义中
    在这里插入图片描述
  2. 字符集由默认的Unicode字符集改为多字节字符集
    在这里插入图片描述
  • Dev-C++ 写代码前需要更改编译选项(Compiler Option),在"编译时加入以下命令"和"在连接器命令行加入以下命令"的栏中加入命令-std=c++11 -lwininet(其实我没搞懂到底应该放在哪个栏里,两个都放进去之后,重启了DevC++才能实现编译无错…)
    在这里插入图片描述
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-14 22:56:44  更:2021-07-14 22:58:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/18 12:39:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码