俗话说“工欲善其事,必先利其器”,自己做的这款小项目的实现,搭载的平台 VS2013 ,要想做出一款只需要用户输入一个url,俗称网址,就能从互联网上得到该网址所需要的照片,并且保存到用户指定的文件夹中。所需要的步骤如下:
1.用户输入网址. 我们并不是分析这一个网址,而是通过这个网址得到相关网址的照片,所以我们需要声明一个队列,来存储解析该网页得到的html页面的所有url.
2.分析url. 从队列中出队,逐个分析网址,得到网址的协议类型,主机名,和资源名
3.初始化网络。 我们要用到SOCKET通信,加载2.2版本的套接字,创建套接字,绑定,连接
4.得到html页面。模拟发送Get请求,连接网络,得到该页面的html页面。
5.筛选url. 先用正则表达式匹配,出以http://开头,并且没有单双引号的网址,我们压入队列
6.下载照片。 我们从筛选好的网址中匹配,find_last_of查找以.png .gif .gpeg,jpg等结尾的我们将其写到文件中,保存下来。
对应的关键代码如下:
//
#include "Spider.h"
#include<WinSock2.h>
//开始抓取函数
void startCatch(string url)
{
queue<string> q;
q.push(url); //将url压入队列
while (!q.empty()){ //判断队列是不是为空
string currentUrl = q.front();
q.pop(); //出队
//cout<< currentUrl << endl;
CHttp http;
string html=http.GetHtml(currentUrl);
//开始匹配网页
smatch mat;
regex reg("http://[^\\s'\"]+"); //网址以http开头,没有空格没有单双引号
string::const_iterator start = html.begin();
string::const_iterator end = html.end();
while (regex_search(start, end, mat, reg))
{
string per(mat[0].first, mat[0].second);
cout << per << endl;
//下载照片
if (per.find(".jpg") == string::npos&&per.find(".jpeg") == string::npos&&per.find(".gif") == string::npos&&per.find(".png") == string::npos)
{
q.push(per);
}
else
{
//下载图片
string filename = "./res/" + per.substr(per.find_last_of('/') + 1);
http.DownLoadImage(per,filename);
}
start = mat[0].second;
}
}
}
//构造函数
CHttp::CHttp()
{
m_socket = NULL;
}
//析构函数
CHttp::~CHttp()
{
}
//分析url得到主机号和路径名
bool CHttp::analyseUrl(string url)
{
//先判断url是否有效
if (url.length() <= 7)
return false;
//没有找到协议类型
if (url.find("http://") == string::npos&&string::npos == url.find("https://"))
return false;
//网址中找到http://,开始匹配主机名和资源名
if (url.find("http://") != string::npos){ //找到http://
int pos = url.find('/', 7); //从第七个字符开始匹配斜杠
if (pos == string::npos){ //http://www.baidu.com
m_host = url.substr(7);
m_object = '/';
}
else{
m_host = url.substr(7, pos-7); //从第七个开始匹配pos-7个
m_object = url.substr(pos);
}
}
else{ //找到https://
int pos = url.find('/', 8); //从第8个字符开始匹配斜杠
if (pos == string::npos){ //https://www.baidu.com
m_host = url.substr(8);
m_object = '/';
}
else{
m_host = url.substr(8, pos-8);
m_object = url.substr(pos);
}
}
//cout << m_host << endl;
//cout << m_object << endl;
return true;
}
//初始化网络
bool CHttp::ConnectNetwork()
{
//1.加载套接字
WSADATA wd;
if (0 != WSAStartup(MAKEWORD(2, 2), &wd)){
cout << "加载套接字失败!错误码:" << GetLastError() << endl;
return false;
}
//判断是不是2.2版本
if (2 != HIBYTE(wd.wVersion) || 2 != LOBYTE(wd.wVersion)){
cout << "加载套接字2.2版本失败!错误码:" << GetLastError() << endl;
return false;
}
//创建套接字
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_socket == INVALID_SOCKET){
cout << "创建套接字失败!错误码:" << GetLastError() << endl;
return false;
}
//连接服务器
sockaddr_in sa = { AF_INET }; //网络编程结构
if (SOCKET_ERROR ==bind(m_socket, (sockaddr*)&sa, sizeof(sockaddr))){
cout << "绑定失败!错误码:" << GetLastError() << endl;
return false;
}
hostent *p = gethostbyname(m_host.c_str()); //p里面存取的是ip地址
sa.sin_port = htons(80);
memcpy(&sa.sin_addr, p->h_addr, 4);
if (SOCKET_ERROR == connect(m_socket, (sockaddr*)&sa, sizeof(sockaddr))){
cout << "绑定失败!错误码:" << GetLastError() << endl;
return false;
}
return true;
}
//得到html页面
string CHttp::GetHtml(string url)
{
if (false==analyseUrl(url) ){
cout << "分析url失败!错误码:" << GetLastError() << endl;
return false;
}
if (false == ConnectNetwork()){
cout << "连接网络失败!错误码:" << GetLastError() << endl;
return false;
}
string html;
string reqInfo = "GET " + m_object + " HTTP/1.1\r\n"
+ "Host: " + m_host + "\r\n"
//+ "User-Agent: Mozilla/5.0(Windows NT 6.1; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
+ "Connection: Close\r\n\r\n";
if (SOCKET_ERROR == send(m_socket, reqInfo.c_str(), reqInfo.length(), 0)){
cout << "发送Get请求失败!错误码:" << GetLastError() << endl;
return false;
}
//接收头信息
char ch;
int nRecv = 0;
while ((nRecv=recv(m_socket, &ch, sizeof(ch), 0))>0)
{
if (ch == '\r')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\n')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\r')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\n')
{
break;
}
}
}
}
}
//接收数据
char szBuffer[11];
memset(szBuffer, 0, sizeof(szBuffer));
while (recv(m_socket, szBuffer, 10, 0))
{
html += szBuffer;
}
return html;
}
//下载照片
bool CHttp::DownLoadImage(string url,string filename)
{
if (false == analyseUrl(url)){
cout << "分析url失败!错误码:" << GetLastError() << endl;
return false;
}
if (false == ConnectNetwork()){
cout << "连接网络失败!错误码:" << GetLastError() << endl;
return false;
}
string html;
string reqInfo = "GET " + m_object + " HTTP/1.1\r\n"
+ "Host: " + m_host + "\r\n"
//+ "User-Agent: Mozilla/5.0(Windows NT 6.1; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
+ "Connection: Close\r\n\r\n";
if (SOCKET_ERROR == send(m_socket, reqInfo.c_str(), reqInfo.length(), 0)){
cout << "发送Get请求失败!错误码:" << GetLastError() << endl;
return false;
}
//接收头信息
char ch;
int nRecv = 0;
while ((nRecv = recv(m_socket, &ch, sizeof(ch), 0))>0)
{
if (ch == '\r')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\n')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\r')
{
nRecv = recv(m_socket, &ch, sizeof(ch), 0);
if (nRecv > 0 && ch == '\n')
{
break;
}
}
}
}
}
//写文件
FILE *pFile = fopen(filename.c_str(), "wb");
if (pFile == NULL)
return false;
//接收数据
char szBuffer[11];
memset(szBuffer, 0, sizeof(szBuffer));
while ((nRecv=recv(m_socket, szBuffer, 10, 0)))
{
fwrite(szBuffer, 1, nRecv, pFile);
}
fclose(pFile);
return true;
}