赞
踩
哈喽,各位小伙伴们,今天我给大家分享的是如何用C++实现爬取网页源代码。
本人用的开发环境为visual studio 2013,涉及到的知识点有:构造函数、析构函数、queue队列、分文件编写、WinSock2.h网络编程、fstream文件流等等,总之对于新手而言是非常好的练习代码能力的一个作业。
先是给大家看看演示效果,下图所示的是代码运行后的初始界面:
下图是我将要爬取的源地址,这里我随便在网上搜了个网址。(是因为我这图片有美女吗?上传图片居然不显示,这里留个网址给大家)http://www.jj20.com/bz/nxxz/shxz/25612.html
另外附上这个界面的源码图片,这个可以在浏览器中按F12获取得到,也可以右键选择打开。
下图是我将网址输入后实现的效果:
可以看到在D盘已经生成了一个html.txt文件,用于存放原网页的源代码。大小为11kb。
同时,这里我们以黑窗口打印出html的返回数据,用于验证返回的源代码。
好了,以上就是我们的演示效果,下面废话不多说,直接给大家源码吧,相关注释我已经写得很详细了,有不懂的欢迎留言或者私信我。
一共有三个代码块、分别是http.h、http.cpp和main.cpp
3.1 http.h
#ifndef HTTP_H //目的是为了防止头文件重复包含 #define HTTP_H #include<WinSock2.h> class CHttp { public: std::string m_host; //域名 std::string m_object; //资源路径 bool m_bHttps; SOCKET m_socket; //套接字 public: CHttp(); //构造函数 ~CHttp(); //析构函数 //初始化网络 bool Init(); //解析URL bool AnalyseURL(std::string url); //连接服务器 bool CHttp::Connect(); //下载网页及保存 bool GetHtml(std::string& html); //引用类型的变量 }; #endif
3.2 http.cpp
#include<iostream> #include"http.h" #include<WinSock2.h> //使用套接字的头文件 #include <fstream> //包含文件流的头文件 #define _CRT_SECURE_NO_WARNINGS #pragma comment(lib,"ws2_32.lib") //构造函数 CHttp::CHttp() { m_bHttps = false; //默认一开始不是https协议 m_socket = NULL; } //析构函数 CHttp::~CHttp() { } //解析URL函数 bool CHttp::AnalyseURL(std::string url) { //https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=40770 示例 https //http://www.163.com/ 示例 http //将字符串分别转化为大、小写的函数 //toupper(); tolower(); 因为有些网站用的是大写的HTTPS\HTTP std::string str = url.substr(0, 8); //substr(string, start<,length>):从string的start位置开始提取字符串,length:要提取字符串的长度 if ("https://" == str) { m_bHttps = true; } else if (str.find("http://") !=std::string::npos) { m_bHttps = false; } else return false; //找主机网址的反斜杠位置 int nPos = url.find('/', m_bHttps ? 8 : 7); //如果m_bHttps为真,那么从第8个之后的位置开始找,否则从第七个位置之后开始找 if (nPos == std::string::npos) //这句话的意思就是说如果把最后一个位置都找完了,还没找到。 npos表示string的结束位置, { //http://www.163.com m_host=url.substr(m_bHttps ? 8 : 7); //例如上面这种,如果主机后面没有/,那么直接从http://开始截取,截到最后 m_object = "/"; //像上面这种没有资源路径,那我们就给他们一个斜杠 } else { //如果是这种情况https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=40770 m_host = url.substr(m_bHttps ? 8 : 7, nPos - (m_bHttps ? 8 : 7)); m_object = url.substr(nPos); } if (m_host.empty()) //如果主机内容为空,意味着截取不到 return false; return true; } //初始化网络 bool CHttp::Init() { WSADATA wd; if (0 != WSAStartup(MAKEWORD(2, 2), &wd)) return false; if (LOBYTE(wd.wVersion) != 2 || HIBYTE(wd.wVersion) != 2) //判断请求的是不是2.2版本 return false; //创建套接字 m_socket=socket(AF_INET, SOCK_STREAM, 0); } //连接服务器 bool CHttp::Connect() { //将域名解析成对应的IP地址 HOSTENT * p=gethostbyname(m_host.c_str()); //P存放的内容就是由主机域名解析好后的ip地址, if (p == NULL) return false; //解析失败 //连接服务器 sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_port = htons(80); memcpy(&sa.sin_addr, p->h_addr, 4); if (SOCKET_ERROR == connect(m_socket, (sockaddr*)&sa, sizeof(sockaddr))) return false; return true; } bool CHttp::GetHtml(std::string& html) { std::string get; get += "GET " + m_object + " HTTP/1.1\r\n"; get += "Host: " + m_host + "\r\n"; get += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.37"; get += "Connection: Close\r\n"; get += "\r\n"; //发送GET请求 if(SOCKET_ERROR==send(m_socket, get.c_str(), get.length(), 0)); //套接字、发送的内容、发送多长、flag std::cout << "GET请求发送失败" << std::endl; //接收数据 char ch = 0; std::fstream dataFile; //创建一个文件,用于存放html的内容dataFile.open("D:\\html.txt", std::ios::out); dataFile.open("D:\\html.txt", std::ios::out); if (!dataFile) { printf("文件打开失败!\n"); return false; } while (recv(m_socket, &ch, sizeof(ch), 0)) { html += ch; dataFile << ch; } dataFile.close(); return true; }
3.3 main.cpp
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdlib> #include<cstdio> #include<string> #include<queue> #include"http.h" using namespace std; //欢迎界面 void Welcome(); //开始抓取 bool StartCatch(string url); int main() { Welcome(); cout << "请输入要抓取的URL的地址:"<<endl; string url; cin >> url; StartCatch(url); system("pause"); return EXIT_SUCCESS; } //欢迎界面 void Welcome() { cout << endl; cout << endl; cout << "\t\t-----------------------------------------" << endl; cout << "\t\t-----------------------------------------" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t欢迎使用C++智能爬虫系统\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t 某某大学某某实验室\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-\t\t\t\t\t-" << endl; cout << "\t\t-----------------------------------------" << endl; cout << "\t\t-----------------------------------------" << endl; } //开始抓取 bool StartCatch(string url) { queue<string> q; //创建url队列 因为url是先获取到先处理,所以用queue的数据结构 q.push(url); //将获取到的url队列放入queue中 while (!q.empty()) //判断队列是否为空,如果不为空,那么久一直采集 { string currentUrl = q.front(); //将当前队列中的第一个url取出来 q.pop(); //解析URL ----就是把协议、主机、资源路径给分割出来 CHttp http; http.Init(); http.AnalyseURL(currentUrl); cout << http.m_host << "\t\t" << http.m_object << endl; if (false == http.Connect()) cout << "连接服务器失败" << endl; else cout << "连接服务器成功" << endl; //获取html信息 string html; http.GetHtml(html); cout << html << endl; //这一行可有可无,不过第一次跑的时候最好用上 } return true; }
在网络编程这一块,如果不了解的朋友可以去网上搜一下,有相应的博客说的很好,在http.cpp程序中GET那一块的代码,我用到了fiddler软件用于查找一些信息。有需要的朋友可以去太平洋下载中心进行下载,里面也有汉化教程。
最后,码字不易,如果感觉这篇文章对您有所帮助的话,希望点赞收藏关注走一波,也欢迎在评论区留言,下一篇博客打算写一下如何在爬取到的结果里面筛选出我们需要的信息。
加油,各位小伙伴们!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。