C++编写爬虫脚本爬取网站图片

您所在的位置:网站首页 爬虫爬取图片保存到本地 C++编写爬虫脚本爬取网站图片

C++编写爬虫脚本爬取网站图片

2023-12-11 18:04| 来源: 网络整理| 查看: 265

C++编写爬虫脚本爬取网站图片 整体代码设计思路具体功能实现初始化网络库url中爬取图片获取网页源代码连接主机url中获取主机名和文件名html中提取连接html中提取图片链接获取并保存图片 总结 爬虫是一种常用的技术手段,目前大家经常见到的爬虫脚本大多是用Python写的,但本着“C语言是世界上最强大的语言”的信仰,今天我来给大家分析一个用C写的爬虫脚本,可以通过 广度遍历爬取目标网站的图片。其实编写一个这样的简易脚本对于我们理解网络协议和网络通信机制也是很有帮助的,而且用C来写的话能帮助我们深入理解底层逻辑,程序效率也更高,Python毕竟是封装好的东西,个人认为对于基础学习阶段的人来说过多使用Python不是好事。

在这个程序中我们将用到关于C/C++以及网络的以下知识:

SOCKET编程http网络协议C++文件输入输出C++ STL库中容器的使用广度遍历算法 整体代码设计思路

新手小白在刚开始写代码时容易陷入一个误区:写一步看一步。一个完整的程序往往可以分为多个模块,也就是多个函数。一些新手在写代码时可能会想出来一个函数就立马去想办法实现它,但当你把多个函数组合起来时又可能发现不同函数之间难以契合,或者呈现的效果和自己想的不一样。于是又不得不反过头去对原来的函数修修补补,修改过程中还可能会遇到各种各样的BUG,使得工作进度整体效率非常低下。

其实写代码也是需要规划的,但这要求你对这个项目的整体思路和实现方法非常清晰。那么对于我们这个爬虫脚本,我们的目标是爬取网站的图片,那么我们可以将整个过程做如下规划:

//初始化网络库 void InitNetwork(); //从url中爬取图片 void SnapImageFromUrl(string url); //从url中获取主机名和文件名 void GetHostAddrByUrl(string url, string& hostAddr, string& fileAddr); //连接主机 unsigned int ConnectAddr(string hostAddr); //模拟http协议,发送至主机,并且获取网页源代码 string GetHtmlCode(unsigned int socket, string hostAddr, string fileAddr); //从html中提取出图片链接 void GetImagesUrlfromHtml(const string& html, set& imageUrl); //从html中提取出链接 void GetUrlfromHtml(const string& html, set& Url); //获取并保存图片 void SaveImage(const string& imageUrl); // void _SaveJPG(unsigned int socket, string hostAddr, string fileAddr);

我们可以在代码开头先想好自己可能用到的一些函数,进行定义,当然我们并不一定能在以开始就把用到的函数全部想好,但可以先规划一个整体的思路,一部分一部分的来完成我们的整个项目内容。

本次项目需要用到的头文件如下,这些头文件也对应了前面提到的知识点

#include "stdafx.h" #include //网络头文件 #include #include #include #include #include #include #include //c++ 11 正则表达式 using namespace std; #pragma comment(lib, "ws2_32.lib") //网络库文件

在主函数中,我们只需要

int _tmain(int argc, _TCHAR* argv[]) { InitNetwork(); //URL地址可以根据自己喜好随意更改,这里只是一个示例 string beginURL = "http://www.win4000.com/meitu.html"; SnapImageFromUrl(beginURL); return 0; }

至此,我们整体的代码实现思路已经有了,接下来进行具体功能的实现

具体功能实现 初始化网络库 void InitNetwork() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2);//规定使用的协议版本 err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return; } } url中爬取图片 void SnapImageFromUrl(string url) { //采用广度遍历的方式,爬网站下所有的图片 queue queUrls; queUrls.push(url); map tempMap; //用于记录已访问过的网址 while (!queUrls.empty()) { try{ string curUrl = queUrls.front(); //取出队列中最开始被放进去的元素 queUrls.pop(); //自行研究 if (curUrl.size() > 256) continue; //将已经访问过的放到map中 tempMap[curUrl]++; printf("当前处理的:%d %s\n", queUrls.size(), curUrl.c_str()); //将curUrl下的图片全部爬下来 string hostAddr, fileAddr; SOCKET socket = 0; //获取主机名和文件名 GetHostAddrByUrl(curUrl, hostAddr, fileAddr); if ((socket = ConnectAddr(hostAddr)) == 0) { printf("连接主机失败 : %s\n", hostAddr); } //获取网页源代码 string html = GetHtmlCode(socket, hostAddr, fileAddr); if (!html.empty()) { //提取出网页中的图片链接 set imageUrl; GetImagesUrlfromHtml(html, imageUrl); for (set::iterator it = imageUrl.begin(); it != imageUrl.end(); it++) { //printf("%s\n", (*it).c_str()); //SaveImage(*it); //去弄个路径 } //提取当前网页中的所有链接,并且将其放到队列中 set urls; GetUrlfromHtml(html, urls); for (set::iterator it = urls.begin(); it != urls.end(); it++) { //printf("%s\n", (*it).c_str()); //将提取出来的链接放到队列中,放进去之前,需要判断,是否访问过 if (tempMap[(*it)] == 0) //该网址没有被访问过 { queUrls.push((*it)); } else { printf("%s 已访问过\n", (*it).c_str()); } } } } catch (...){ int err = 11; } } int a = 20; } 获取网页源代码 string GetHtmlCode(unsigned int socket, string hostAddr, string fileAddr) { //模拟http协议 string reqInfo = "GET " + fileAddr + " HTTP/1.1\r\n" + "HOST:" + hostAddr + "\r\n" + "Connection:Close\r\n\r\n"; //将模拟的http协议发送给主机 int sendLen = send(socket, reqInfo.c_str(), reqInfo.size(), 0); if (sendLen 0 }; int recvLen = recv(socket, recvBuff, 1024, 0); if (recvLen //创建套接字 SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == sock) return INVALID_SOCKET; //有主机地址获取对应的IP地址 hostent* pHost = gethostbyname(hostAddr.c_str()); if (pHost == 0) return 0; //准备要连接的IP地址 sockaddr_in addr; addr.sin_family = AF_INET; //地址族域 addr.sin_port = htons(80); //端口号, h(host) to n(net) s(short), 将主机字节序转换成网络字节序 memcpy(&(addr.sin_addr.S_un.S_addr), pHost->h_addr, 4); //ip地址拷贝 //连接服务器 if (SOCKET_ERROR == connect(sock, (sockaddr*)&addr, sizeof(sockaddr))) return 0; return sock; } url中获取主机名和文件名 void GetHostAddrByUrl(string url, string& hostAddr, string& fileAddr) { //strstr(); //搜索字符串函数 //strtok(); //分割字符串函数 char szArr[256] = { 0 }, szHostAddr[256] = { 0 }, szfileAddr[256] = { 0 }; const char* pFind = strstr(url.c_str(), "http://"); if (pFind) { strcpy(szArr, url.c_str() + strlen("http://")); } else { strcpy(szArr, url.c_str()); } char* pFileAddr = strstr(szArr, "/"); if (pFileAddr) { strncpy(szHostAddr, szArr, pFileAddr - szArr); strcpy(szfileAddr, pFileAddr); //memcpy(); } hostAddr = szHostAddr; fileAddr = szfileAddr; } html中提取连接 void GetUrlfromHtml(const string& html, set& Url) { smatch mat; //用于匹配的 regex patten("a href=\"http://(www..*?\\.html)\""); //在一个范围内进行搜索,将搜索到的结果,匹配到smatch中 string::const_iterator beginIter = html.begin(); string::const_iterator endIter = html.end(); while (regex_search(beginIter, endIter, mat, patten)) { //找到了匹配项, 其结果在mat中 string url(mat[1].first, mat[1].second); Url.insert(url); beginIter = mat[0].second; //修改搜索的起始位置 } } html中提取图片链接 //利用正则表达式,提取图片链接 //用于匹配 //用于搜索 //用于修改 void GetImagesUrlfromHtml(const string& html, set& imageUrl) { smatch mat; //用于匹配的 regex patten("\"http://(.*?\\.jpg)\""); //在一个范围内进行搜索,将搜索到的结果,匹配到smatch中 string::const_iterator beginIter = html.begin(); string::const_iterator endIter = html.end(); while (regex_search(beginIter, endIter, mat, patten)) { //找到了匹配项, 其结果在mat中 string url(mat[1].first, mat[1].second); imageUrl.insert(url); beginIter = mat[0].second; //修改搜索的起始位置 } } 获取并保存图片 void SaveImage(const string& imageUrl) { //1.获取主机名和文件名 string hostAddr, fileAddr; GetHostAddrByUrl(imageUrl, hostAddr, fileAddr); unsigned int sock = ConnectAddr(hostAddr); if (sock > 0) { _SaveJPG(sock, hostAddr, fileAddr); } } void _SaveJPG(unsigned int socket, string hostAddr, string fileAddr) { //模拟http协议 string reqInfo = "GET " + fileAddr + " HTTP/1.1\r\n" + "HOST:" + hostAddr + "\r\n" + "Connection:Close\r\n\r\n"; //将模拟的http协议发送给主机 int sendLen = send(socket, reqInfo.c_str(), reqInfo.size(), 0); if (sendLen char recvBuff[1024] = { 0 }; int recvLen = recv(socket, recvBuff, 1024, 0); if (recvLen


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3