一个简单的爬虫程序,爬取网站的图片

2023-05-16

最简单的爬虫是分析网页,如果要爬取图片,就要将图片在网页中的格式进行分析,取到图片的连接,接着下载图片;

由于网页中还会链接到其他的网页,所以需要将其中的所有网页取出,一般有两种算法:BFS广度优先遍历:和DFS深度优先遍历。

对于一个简单的爬虫程序,需要以下几部分组成

1.解析网页URL得到对应的主机名和资源值

2.需要向服务端发送http请求,得到相应的相应

3.提取所有的url及图片地址

4.文件的下载和存储

5.一个BFS或者DFS算法

用类CrawlerC来实现:

主要的成员函数有:

bool analyUrl(const string&url,string&host,string&resource);//通过对URL的解析得到对应的主机地址和资源

bool getHttpRes(const string&url,char*&response,int &bytesRead);//向对应的主机发送http请求

void getHtmlRes(string &htmlResponse,vector<string>&imgUrl,const string&host);//通过解析响应得到网页上的图片地址

string getFileName(const string&url);//依据URL建立文件名,便于区分哪个网站的图片

void downLoadImg(vector<string>&imgUrls,const string&url);//从远端下载图片

void BFS(const string &url);//广度优先遍历

bool CrawlerC::analyUrl(const string &url, string &host, string &resource)
{
	//url中http是协议,之后的www什么的是域名,之后的/后面是资源
	if (strlen(url.c_str()) > 2000){
		cout << "url长度过长,可能是错误的url" << endl;
		return false;
	}
	//判断字符串2是不是字符串1的子串,如果是,返回首次出现的地址
	const char*pos = strstr(url.c_str(), "http://");//在URL中找到http出现的位置
	if (pos == NULL)
		pos = url.c_str();//没有找到http,则默认是http
	else
		pos += strlen("http://");//有http需要将其跳过
	if (strstr(pos, "/") == 0)//需要取到第一个/之前的值,这部分值是主机的地址
		return false;
	char pHost[100];
	char pResource[2000];
	sscanf(pos, "%[^/]%s", pHost, pResource);//格式化字符串,取到以/为结束的字符串
	host = pHost;//得到主机名
	resource = pResource;//得到资源
	return true;
}

bool CrawlerC::getHttpRes(const string &url, char*&response, int &bytesRead)
{
	string host, resource;
	if (!analyUrl(url, host, resource)){//解析URL得到对应的主机名和资源
		cout <<url<<" 不能正确的解析" << endl;
		return false;
	}
	//建立socket连接
	struct hostent *hp = gethostbyname(host.c_str());//返回对应主机名的主机信息
	if (hp == NULL){
		cout << "主机名称不正确" << endl;
		return false;
	}
	SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//创建套接字
	if (sock == -1 || sock == -2){
		cout << "创建套接字失败" << endl;
		return false;
	}

	//建立服务器地址
	SOCKADDR_IN sa;
	sa.sin_family = AF_INET;
	sa.sin_port = htons(80);
	memcpy(&sa.sin_addr,hp->h_addr,4);

	//建立连接
	if (connect(sock, (SOCKADDR*)&sa, sizeof(sa)) != 0){
		cout << "无法连接" << endl;
		return false;
	}

	//准备发送HTTP请求
	string request = "GET " + resource + " HTTP/1.1\r\nHost:" + host + "\r\nConnection:Close\r\n\r\n";
	//发送数据
	if (SOCKET_ERROR == send(sock, request.c_str(), request.size(), 0)){
		cout << "发送失败" << endl;
		closesocket(sock);
		return false;
	}

	//接收数据
	int nContentLeng = 1048576;
	char *pageBuf = (char*)malloc(nContentLeng);
	//char *pageBuf = new char[nContentLeng];
	memset(pageBuf, 0, nContentLeng);

	bytesRead = 0;//已经读取的长度
	int ret = 1;//拷贝完成的长度
	cout << "read:" << endl;
	while (ret > 0){
		//指定接收的缓存及其长度,返回拷贝完成的字节数
		ret = recv(sock, pageBuf + bytesRead, nContentLeng - bytesRead, 0);
		if (ret > 0)
			bytesRead += ret;
		if (nContentLeng - bytesRead < 100){//缓冲区的剩余长度过小
			cout << "重新分配" << endl;
			nContentLeng *= 2;
			pageBuf = (char*)realloc(pageBuf, nContentLeng);
		}
		cout << ret << endl;
	}
	pageBuf[bytesRead] = '\0';//添加结束标志
	response = pageBuf;
	closesocket(sock);
	return true;
}
void CrawlerC::getHtmlRes(string &htmlResponse, vector<string>&imgUrls, const string &host)
{
	//找到所有的连接加入到队列中
	const char* p = htmlResponse.c_str();//需要解析的返回串
	char* tag = "href=\"";//这个匹配href=",找到链接
	const char *pos = strstr(p,tag);
	ofstream ofile("url.txt", ios::app);
	while (pos){//循环取出所有的超链接
		pos += strlen(tag);//pos现在指向了网址的开始
		const char*nextQ = strstr(pos, "\"");//匹配引号
		if (nextQ){//取到网址的结束位
			char *url = new char[nextQ - pos + 1];
			sscanf(pos, "%[^\"]", url);//取到以引号结束的子串
			string surl = url;
			if (visitedUrl.find(surl) == visitedUrl.end()){
				visitedUrl.insert(surl);//不在url中
				ofile << surl << endl;
				hrefUrl.push(surl);
			}
			pos = strstr(pos, tag);
			delete[]url;
		}
	}
	ofile << endl << endl;
	ofile.close();

	tag = "<img ";//开始取图片
	const char*att1 = "src=\"";
	const char*att2 = "lazy-src=\"";
	const char*pos0 = strstr(p, tag);
	while (pos0)
	{
		pos0 += strlen(tag);
		const char*pos2 = strstr(pos0, att2);
		if (!pos2 || pos2 > strstr(pos0, ">")){
			pos = strstr(pos0, att1);
			if (!pos){
				continue;
			}
			else
				pos = pos + strlen(att1);
		}
		else{
			pos = pos2 + strlen(att2);
		}
		const char*nextQ = strstr(pos, "\"");//找后引号
		if (nextQ){
			char*url = new char[nextQ - pos + 1];
			sscanf(pos, "%[^\"]", url);
			cout << url << endl;
			string imgUrl = url;
			if (visitedImg.find(imgUrl) == visitedImg.end()){
				visitedImg.insert(imgUrl);
				imgUrls.push_back(imgUrl);
			}
			pos0 = strstr(pos0, tag);
			delete[]url;
		}
	}
}

std::string CrawlerC::getFileName(const string&url)
{
	string fileName;
	fileName.resize(url.size());
	int k = 0;
	for (int i = 0; i < (int)url.size(); i++){
		char ch = url[i];
		//去除特殊符号
		if (ch != '\\'&&ch != '/'&&ch != ':'&&ch != '*'&&ch != '?'&&ch != '"'&&ch != '<'&&ch != '>'&&ch != '|')
			fileName[k++] = ch;
	}
	return fileName.substr(0, k);
}

void CrawlerC::downLoadImg(vector<string>&imgUrl, const string&url)
{
	//生成保存图片的文件夹
	string foldname = getFileName(url);
	foldname = "./img/" + foldname;
	if (!CreateDirectory(foldname.c_str(), NULL))
		cout << "不能创建文件夹" << endl;
	char*image=NULL;
	int byteRead;
	for (int i = 0; i < imgUrl.size(); i++){
		string str = imgUrl[i];
		int pos = str.find_last_of(".");
		if (pos==string::npos)
			continue;
		else{
			string ext = str.substr(pos + 1, str.size() - pos - 1);
			if (ext != "bmp"&& ext != "jpg" && ext != "jpeg"&& ext != "gif"&&ext != "png")
				continue;
		}
		if (getHttpRes(imgUrl[i], image, byteRead)){
			if (strlen(image)==0)
				continue;
			const char*p = image;
			const char * pos = strstr(p, "\r\n\r\n") + strlen("\r\n\r\n");//图片文件的开始位置
			int index = imgUrl[i].find_last_of("/");
			if (index != string::npos){
				string imgname = imgUrl[i].substr(index, imgUrl[i].size());//截取文件名
				ofstream ofile(foldname + imgname, ios::binary);
				if (!ofile.is_open())
					continue;
				cout << g_ImgCnt++ << foldname + imgname << endl;
				ofile.write(pos, byteRead - (pos - p));
				ofile.close();
			}
			free(image);
		}
	}
}

void CrawlerC::BFS(const string &url)
{
	char*response=0;
	int bytes;
	if (!getHttpRes(url, response, bytes)){
		cout << "错误的url" << endl;
		return;
	}
	string httpResponse = response;
	free(response);
	string fileName = getFileName(url);

	ofstream ofile("./html/" + fileName);
	if (ofile.is_open()){
		ofile << httpResponse << endl;
		ofile.close();
	}
	vector<string>imageUrl;
	getHtmlRes(httpResponse, imageUrl,url);
	downLoadImg(imageUrl, url);
}
int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2, ), &wsaData) != 0)//这个是初始化socket
		return 1;
	CreateDirectory("./img", 0);
	CreateDirectory("./html", 0);
	CrawlerC test;
	string url = "http://www.nipic.com/index.html";
	test.BFS(url);
	test.visitedUrl.insert(url);
	while (test.hrefUrl.size() > 0){
		string url1 = test.hrefUrl.front();
		//cout << url1 << endl;
		test.BFS(url1);//对于每一个url都要进行搜索
		test.hrefUrl.pop();
	}
	WSACleanup();
	return 0;
}

也可以将提取URL和下载图片作为不同的线程来处理

参考链接:http://blog.csdn.net/huangxy10/article/details/8120106

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一个简单的爬虫程序,爬取网站的图片 的相关文章

  • ESP32的SDK开发之blufi一键配网微信小程序端开发

    上一篇文章简单介绍了blufi配网协议 xff0c 现在来讲讲再微信小程序端实现配网的控制 xff0c 小程序开发是基于官方原生API开发 本人的微信小程序开发也是初学的 xff0c 由于之前没接触过前端开发 xff0c 软件写的很渣渣 蓝
  • 自定义View之渐变色圆形进度条

    先展示下效果图 xff1a 然后按照自定义view的步骤来实现 我们需要将目标定义清楚 xff1a 目标是渐变色圆形进度条 xff0c 那么 xff0c 使用canvas画弧形是基础了 xff0c 另外是渐变色的效果 xff0c 这里使用L
  • NEMA协议解析 (GPS标准协议)

    概述 NMEA是National Marine Electronics Association 的缩写 xff0c 是美国国家海洋电子协会的简称 xff0c 现在是GPS导航设备统一的RTCM标准协议 协议集合 NMEA 0183协议定义的
  • 深入分析websocket协议,从3个方面设计网络应用层协议丨网络编程|网络IO|epoll|socket|网络协议丨c/c++linux服务器开发

    深入分析websocket协议 xff0c 从3个方面设计网络应用层协议 视频讲解如下 xff1a 深入分析websocket协议 xff0c 从3个方面设计网络应用层协议丨网络编程 网络IO epoll socket 网络协议丨c c 4
  • CMake找不到opencv库解决办法

    一 问题详情 在ubuntu中使用CMake编译链接opencv库的程序时FIND PACKAGE OpenCV REQUIRED 报错 xff0c 找不到opencv xff0c 信息如下 xff1a Found OpenCV Windo
  • 详解使用PHP CURL访问HTTPS

    三年前写过一篇 一个简陋的支持HTTPS的PHP CURL封装函数 xff0c 当时只是知其然不知其所以然 xff0c 今天来详细梳理一下 为方便说明 xff0c 先上代码吧 这是今天重新封装的一个函数 curl POST 64 param
  • adb启动Android系统设置命令

    adb打开系统设置的命令 adb命令打开手机设置页面 设置主页面 adb shell am start com android settings com android settings Settings 安全 adb shell am s
  • bootjar和fw配合动态jar

    Gradle 43 bootJar打包SpringBoot工程并分离jar包和配置文件 ruiurrui的博客 CSDN博客 bootjar Gradle 43 bootJar打包SpringBoot工程并分离jar包和配置文件 ruiur
  • 解决 Ubuntu 无法播放 MP4 格式视频的问题的办法

    解决 Ubuntu22 04 无法播放 MP4 格式视频的问题的办法 问题原因 xff1a 系统中的终端上安装缺少的编解码器 解决办法 xff1a 第一条命令将更新存储库 xff1a sudo apt get update 安装以下包含视频
  • 程序查询方式、程序中断、直接内存存取DMA

    程序查询方式 xff1a 当主机进行I O操作时 xff0c 首先发出询问信号 xff0c 读取设备的状态并根据设备状态决定下一步操作究竟是进行数据传输还是等待 这种控制下 xff0c CPU一旦启动I O xff0c 必须停止现行程序的运
  • app监听白天黑夜

    需要在AndroidManifest中的MainActivity配置 xff1a android configChanges 61 34 uiMode 34 64 param newConfig 64 Override public voi
  • 可缩放性ImageView(可以放大缩小)

    由于项目需求的原因 xff0c 最近一直在研究可缩放性ImageView xff0c 用本文来记录一下最近所学 xff1a 该ImageView的实现功能有 xff1a 1 xff09 初步打开时 xff0c 图片按比例满屏 xff08 填
  • 两行代码用camera2 打开闪光灯

    CameraManager c 61 CameraManager getSystemService Context CAMERA SERVICE c setTorchMode c getCameraList 0 true 打开闪光灯 c s
  • 混淆与加固

    https blog csdn net guolipeng network article details 74551968 上面是混淆 加固方面 腾讯乐固首选 xff0c 阿里加固没毛用 xff0c 360次选 原本大小阿里聚安全腾讯云应
  • C语言10进制转16进制

    void decimal to hexaDecimal int a char strs char hex int i 61 0 int j 61 0 int base 61 16 char tmp 16 while a gt 0 j 61
  • socket网络编程select使用方法

    文章以及源码均为原创 xff0c 当然欢迎转载 xff0c 转载请加说明 server h int select int nfds fd set readfds fd set writefds fd set exceptfds const
  • 海豚蓝牙ASIO软件设置方法

    1 为什么要使用海豚蓝牙音箱 xff0c 能带给我什么样的音乐体验 目前市面上的蓝牙音箱基本上是一个全频段的喇叭来播放CD音质的音乐 xff0c 如果是立体声的是不能通过一个喇叭来播放 xff0c 无法实现空间上的立体效果 xff0c 必须
  • pixhawk学习笔记-----mavlink

    mavlink协议 一帧数据的长度为8到263个字节 其具体的解析如下所示 xff1a 字节的索引 值 含义 0 0xFE V1 0 帧头 xff0c 表示一帧数据的开始 1 0 255 表示有效数据的长度 2 0 255 包序列 xff0
  • Ubuntu搭建本地web站点,并内网穿透实现公网访问

    本次教程我们通过搭建Apache服务 xff0c 部署一个简单的静态样例站点 xff0c 并使用cpolar内网穿透将内网样例站点发布到公网来进行演示 1 设置 Apache Web 服务器 1 1 安装Apache Web span cl
  • 为何某些公司不允许使用 C++ STL?

    最初开始禁用 C 43 43 STL xff0c 更多地是早期项目编码实践中留下的惯例 xff0c 被后来的程序员继承下来 老项目中这种选择尤其地多 不过如果有人将其上升到公司行为在不同项目中全面禁用 STL xff0c 则没有必要 xff

随机推荐

  • 手把手教你无线数传电台透明传输的配置全攻略

    透明传输 功能说明 任意电台发送数据 xff0c 具有相同地址且相同信道的电台均可同时接收数据 数据以透明方式发送和接收 xff0c 所发即所收 电台设置 1 需将 OPTION 特殊功能寄存器的第 7 位配置成 0 xff0c 透明传输模
  • unity寻路插件(A* Pathfinding)小结

    A Pathfinding 详细使用教程 基本功能 1 astarpath cs 核心组件 xff0c 相当于 路 xff0c 场景里应该只存在一个 2 Seeker cs 相当于一个代理 xff0c 寻找路径 xff0c 需要绑定在每一个
  • Socket网络通信C++编程总结

    概述 Socket编程有三种 xff0c 流式套接字 xff08 SOCK STREAM xff09 数据报套接字 SOCK DGRAM 原始套接字 SOCK RAW 前两者较常用 xff0c 这里简单总结前两种编程步骤以及一些参考资料 编
  • 【STM32+cubemx】0029 HAL库开发:HMC5883L磁力计的应用(电子指南针)

    今天我们来学习电子磁力计HMC5883L的使用 先介绍磁力计的基础知识 xff0c 再给一个获取磁力计数据的例子 xff0c 最后讲解HMC5883L磁力计的校准 xff0c 以及一些使用中的经验 1 xff09 HMC5883L磁力计的基
  • stm32F103R6之BKP(备份寄存器)

    目录 概述 侵入检测 RTC校准 概述 Stm32F103有42个16位的备份寄存器 他们处在备份域里 xff0c 当VDD电源被切断 xff0c 他们仍然由VBAT维持供电 当系统在待机模式下被唤醒 xff0c 或系统复位或电源复位时 x
  • Stm32F103R6之控制器局域网

    目录 概述 双CAN bxCAN工作模式 CAN协议特点 xff1a ISO11898标准CAN物理特性 CAN协议帧 概述 bxCAN是基本扩展CAN Basic Extended CAN 的缩写 xff0c 它支持CAN协议2 0A和2
  • 如何输出一个数的二进制数

    实现思想 xff1a 二进制数是以bit为操作数 xff0c 所以要想将一个数转换为二进制数 xff0c 我们需要先要将输入的数字转化为二进制数 xff0c 然后从高位到低位判断每一位是 1 还是 0 xff0c 最后把对应的 1 和 0
  • 使用libcurl库编写HTTP客户端(包括GET/POST/HTTPS)

    最近在写一个应用程序 需要与HTTP服务器进行数据交互 于是乎自己写了一个类似wget的功能的客户端 实现很简单 但是功能不给力 只可基本功能 于是又在网上找了找 发现使用libcurl库很方便 很强大 比起wget之类的 强大不是一点点
  • 修改系统默认shell为bash

    xfeff xfeff 从 ubuntu 6 10 开始 xff0c ubuntu 就将先前默认的bash shell 更换成了dash shell xff1b 其表现为 bin sh 链接倒了 bin dash而不是传统的 bin bas
  • 如何解决Reporting Services目录数据库文件存在的问题

    出处 xff1a http blog sina com cn s blog 6bace3cc0101jlxv html 错误提示 xff1a 自检时提示 Reporting Services目录数据库文件存在 失败 xff0c Report
  • 无线射频专题《射频合规,2.4GHz WIFI测试指标详解》

    目录 引言 Transmitter Power 发送功率 Transmit Spectrum Mask 发送信号频谱模版 Frequency Error 频率误差 EVM 矢量误差幅度 Band Edges and harmonics 频带
  • 获取当前访问的路径

    String returl 61 request getRequestURL 43 request getQueryString 61 61 null 34 34 34 34 43 request getQueryString
  • 抓取百度关键词排名

    最近在做百度关键词排名的功能 xff0c 发现网上资源比较少 xff0c 于是自己琢磨了一下 xff0c 写一下笔记 xff1b 本文重点在于提供思路 xff0c 请不要过分依赖 xff0c 本文主要靠抓取页面标签来完成 xff0c 如果百
  • 抓取百度关键词排名、标题、连接、描述

    抓取百度关键词排名 标题 连接 描述 转载请标明出处 最近在做百度关键词排名的功能 xff0c 发现网上资源比较这里写代码片少 xff0c 于是自己琢磨了一下 xff0c 写一下笔记 xff1b 本文重点在于提供思路 xff0c 请不要过分
  • Windows Server 2008远程桌面端口的修改

    Windows Server 2008远程桌面端口系统默认的是3389端口 xff0c 但出于安全考虑 xff08 谁都不希望任何都可以远程连接到自己的服务器吧 xff0c 哈哈 xff09 xff0c 经常我们把系统默认的3389端口更改
  • jsp 登陆成功后,显示登录的用户名

    首先在登陆界面将用户名保存起来 xff0c 我这里是将用户名提交到Servlet然后再进行保存 xff0c 从登录界面取用户参数 String uname 61 request getParameter 34 userName 34 req
  • 背景图片随网页的变化而变化(指大小)

    lt DOCTYPE HTML PUBLIC 34 W3C DTD HTML 4 01 Transitional EN 34 gt lt html xmlns 61 34 http www w3 org 1999 xhtml 34 gt l
  • 为什么vscode用久了电脑速度变慢?

    1 vscode 插件占用的内存 1 已经安装的插件 2 插件占用内存 我可以看到 xff0c vs code 这个程序下面有多个进程在跑 xff0c 插件占用的内存比我电脑剩下所有占用的内存还要高 xff0c 但是vs code 性能还是
  • ROS基础学习(一)---创建工作空间

    工作空间 xff08 workspace xff1a 是一个存放工程开发相关文件的文件夹 src xff1a 代码空间 xff08 Source Space build 编译空间 xff08 Build Space devel 开发空间 x
  • 一个简单的爬虫程序,爬取网站的图片

    最简单的爬虫是分析网页 xff0c 如果要爬取图片 xff0c 就要将图片在网页中的格式进行分析 xff0c 取到图片的连接 xff0c 接着下载图片 xff1b 由于网页中还会链接到其他的网页 xff0c 所以需要将其中的所有网页取出 x