c++编写网络爬虫

c++爬虫项目

实现图形化界面UI

在这里插入图片描述

安装easyX(需要用的graphisc.h) 我之前的文章详细写到过如何安装。是这篇文章提到的:传送门

easyx官网

创建图形化界面

#define WINDOW_WIDTH 482
#define WINDOW_HEIGHT 300
void initUI() {
	initgraph(WINDOW_WIDTH, WINDOW_HEIGHT,EX_SHOWCONSOLE);
	setbkmode(TRANSPARENT);
}

移动窗口位置

int screenWidth;
int screenHeight;//屏幕分辨率数据
//获取屏幕分辨率
	screenWidth = GetSystemMetrics(SM_CXSCREEN);
	screenHeight = GetSystemMetrics(SM_CYSCREEN);
	hwnd = GetHWnd(); //获取当前窗口句柄
	//减去原有上方白色标题栏
	SetWindowLong( //设置窗口属性说
		hwnd,
		GWL_STYLE, //设定一个新的窗口风格。
		//GetWindowLong 获取指定串口的属性
		GetWindowLong(hwnd, GWL_STYLE) - WS_CAPTION);//WS_CAPTION 带标题栏的窗口风格

MoveWindow(hwnd, screenWidth * 0.7, 100, WINDOW_WIDTH, WINDOW_HEIGHT, false);

绘制UI界面

	MoveWindow(hwnd, screenWidth * 0.7, 100, WINDOW_WIDTH, WINDOW_HEIGHT, false);
	loadimage(&imgBg, "");
	putimage(0, 0, &imgBg);

编译 运行

在这里插入图片描述

更改代码

MoveWindow(hwnd, screenWidth*0.5, 100, WINDOW_WIDTH, WINDOW_HEIGHT, true);

完美绘制 true参数表示是否重新绘制
在这里插入图片描述

初始化按钮

制作按钮

我们需要从背景图上扣出两个按钮 一个是原本的标题栏(可以拖动程序的),另一个是程序的关闭按钮。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

制作的比较随意。。。

最后一个按钮是进入按钮 (方案放弃)

[ 在这里插入图片描述

再次DIY

在这里插入图片描述

对按钮进行初始化

先创建按钮的类

struct Button {
	IMAGE imgNormal;
	IMAGE imgPress;
	int width, highth;
	int x, y;
	int flag; // 按钮的int类型标记
	bool pressed;
};

写一个初始化按钮的函数

void initButton(Button* btn, const char* normalFile, const char* pressFile,
	int width, int highth, int flag) {
	if (!btn) return;
	loadimage(&btn->imgNormal, normalFile, width, highth, true);
	loadimage(&btn->imgPress, pressFile, width, highth, true);

	btn->width = width;
	btn->highth = highth;

	btn->pressed = false;
	btn->flag = flag;
}

定义三个按钮

//定义按钮
Button btnClose;
Button btnTitle;
Button btnEnter;

载入函数

	loadimage(&imgBg, "./UI.jpg");
	putimage(0, 0, &imgBg);
	//初始化关闭按钮
	initButton(&btnClose, "./normal.bmp", "./press.bmp", 37, 29, 0);
	btnClose.x = WINDOW_WIDTH - 37;
	btnClose.y = 0;
	//初始化标题按钮
	initButton(&btnTitle, "./title_normal.jpg", "./title_press.jpg", 445, 29, 0);
	btnTitle.x = 0;
	btnTitle.y = 0;
//初始化进入按钮

	//初始化进入按钮
	initButton(&btnEnter, "./enter_normal.bmp", "./enter_press.bmp", 165, 53, 0);
	btnEnter.x = 145;
	btnEnter.y = 172;

初始化完毕(累死我了 大部份时间都在绘图);

绘制按钮

写一个绘制按钮的函数

在写一个绘制透明图片的函数(easyx默认是不可以使用透明图片的 贝叶斯定理)

void drawButton(Button* btn) {
	if (!btn) return;
	if (btn->pressed) {
		drawPNG(btn->x, btn->y, &btn->imgPress);
	}
	else {
		drawPNG(btn->x, btn->y, &btn->imgNormal);
	}
}

void drawPNG(int  picture_x, int picture_y, IMAGE* picture) //x为载入图片的X坐标,y为Y坐标
{

	// 变量初始化
	DWORD* dst = GetImageBuffer();    // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
	DWORD* draw = GetImageBuffer();
	DWORD* src = GetImageBuffer(picture); //获取picture的显存指针
	int picture_width = picture->getwidth(); //获取picture的宽度,EASYX自带
	int picture_height = picture->getheight(); //获取picture的高度,EASYX自带
	int graphWidth = getwidth();       //获取绘图区的宽度,EASYX自带
	int graphHeight = getheight();     //获取绘图区的高度,EASYX自带
	int dstX = 0;    //在显存里像素的角标

	// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
	for (int iy = 0; iy < picture_height; iy++)
	{
		for (int ix = 0; ix < picture_width; ix++)
		{
			int srcX = ix + iy * picture_width; //在显存里像素的角标
			int sa = ((src[srcX] & 0xff000000) >> 24); //0xAArrggbb;AA是透明度
			int sr = ((src[srcX] & 0xff0000) >> 16); //获取RGB里的R
			int sg = ((src[srcX] & 0xff00) >> 8);   //G
			int sb = src[srcX] & 0xff;              //B
			if (ix >= 0 && ix <= graphWidth && iy >= 0 && iy <= graphHeight && dstX <= graphWidth * graphHeight)
			{
				dstX = (ix + picture_x) + (iy + picture_y) * graphWidth; //在显存里像素的角标
				int dr = ((dst[dstX] & 0xff0000) >> 16);
				int dg = ((dst[dstX] & 0xff00) >> 8);
				int db = dst[dstX] & 0xff;
				draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16)  //公式: Cp=αp*FP+(1-αp)*BP  ; αp=sa/255 , FP=sr , BP=dr
					| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8)         //αp=sa/255 , FP=sg , BP=dg
					| (sb * sa / 255 + db * (255 - sa) / 255);              //αp=sa/255 , FP=sb , BP=db
			}
		}
	}
}

先对一些变量进行初始化

	bool titleDrag = false; //表示“标题栏”是否被单击拖动
	int titleLastX; //窗口的上一次位置(X 坐标位置)
	int titleLastY; //窗口的上一次位置(X 坐标位置)

然后写一个函数判断此时鼠标的位置是否在button上

bool checkButtonSelect(Button* btn, MOUSEMSG* msg) {
	float margin = 0.01;
	if (msg->x >= btn->x + btn->width * margin &&
		msg->x <= btn->x + btn->width * (1 - margin) &&
		msg->y >= btn->y + btn->highth * margin &&
		msg->y <= btn->y + btn->highth * (1 - margin)) {
		return true;
	}
	else {
		return false;
	}
}

实现鼠标的读写 UI界面的综合逻辑代码 Talking is cheap,show me the code;

while (1) {
		MOUSEMSG m = GetMouseMsg();
		FlushMouseMsgBuffer(); //不能少,后缀快速拖动顶部的标题按钮,将导致鼠标消息太多,
		//出现混乱!
		switch (m.uMsg) {
		case WM_MOUSEMOVE:
			if (checkButtonSelect(&btnTitle, &m)) {
				if (btnTitle.pressed == true) {
					if (titleDrag == false) { // 此时标题栏已经被点击按下,正准备拖动
						titleLastX = m.x; // 记录初始坐标
						titleLastY = m.y;
						titleDrag = true;
					}
					else { // 此时标题栏已经被点击按下,正在拖动
						// 计算拖动偏移量
						int offX = m.x - titleLastX;
						int offY = m.y - titleLastY;
						moveWindow(hwnd, offX, offY); // 根据拖动偏移量,移动窗口
					}
				}
			}
			else if (checkButtonSelect(&btnEnter, &m)) {
				btnEnter.pressed = true;
				drawButton(&btnEnter);
			}
			else if (checkButtonSelect(&btnClose, &m)) {
				btnClose.pressed = true;
				drawButton(&btnClose);
			}
			else {
				// 检查鼠标是否从按钮内移动到按钮之外
				if (btnClose.pressed == true) { // 鼠标从关闭按钮移出
					btnClose.pressed = false;
					drawButton(&btnClose);
				}
				if (btnEnter.pressed == true) { // 鼠标从发送按钮移出
					btnEnter.pressed = false;
					drawButton(&btnEnter);
				}
			}
			break;
		case WM_LBUTTONDOWN:
			if (checkButtonSelect(&btnTitle, &m)) {
				btnTitle.pressed = true; // 单击按下标题栏
			}
			else if (checkButtonSelect(&btnClose, &m)) {
				btnClose.pressed = true;
				drawButton(&btnClose);
			}
			else if (checkButtonSelect(&btnEnter, &m)) {
				btnEnter.pressed = true;
				drawButton(&btnEnter);
			}
			break;
		case WM_LBUTTONUP:
			if (checkButtonSelect(&btnClose, &m)) {
				closegraph();
				exit(0);
			}
			else if (checkButtonSelect(&btnEnter, &m)) {
			//to do
			}
			else if (checkButtonSelect(&btnTitle, &m))
			{
				// 松开标题栏按钮(左键抬起)
				btnTitle.pressed = false;
				titleDrag = false;
			}
			break;
		}
	}

绘制按钮

	drawButton(&btnEnter);
	drawButton(&btnClose);
	drawButton(&btnTitle);

这样差不多UI就设计完成

run一下
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

获取url地址(主角登场)

//URL
wchar_t url[1024];
.....
else if (checkButtonSelect(&btnEnter, &m)) {
			//to do
		
			//输入URL 正戏开始
			InputBox((LPTSTR)url, 1024, "请输入URL地址");//easyX提供的函数进行数据的读入
			 
		}

InputBox函数

bool InputBox(
	LPTSTR	pString,
	int		nMaxCount,
	LPCTSTR	pPrompt = NULL,
	LPCTSTR	pTitle = NULL,
	LPCTSTR	pDefault = NULL,
	int		width = 0,
	int		height = 0,
	bool	bHideCancelBtn = true
);

参数

pString

指定接收用户输入字符串的指针。

nMaxCount

指定 pString 指向的缓冲区的大小,该值会限制用户输入内容的长度。缓冲区的大小包括表示字符串结尾的 ‘\0’ 字符。当允许多行输入时,用户键入的回车占两个字符位置。

pPrompt

指定显示在对话框中的提示信息。提示信息中可以用“\n”分行。InputBox 的高度会随着提示信息内容的多少自动扩充。如果该值为 NULL,则不显示提示信息。

pTitle

指定 InputBox 的标题栏。如果为 NULL,将显示应用程序的名称。

pDefault

指定显示在用户输入区的默认值。

width

指定 InputBox 的宽度(不包括边框),最小为 200 像素。如果为 0,则使用默认宽度。

height

指定 InputBox 的高度(不包括边框)。如果为 0,表示自动计算高度,用户输入框只允许输入一行内容,按“回车”确认输入信息;如果大于 0,用户输入框的高度会自动拓展,同时允许输入多行内容,按“Ctrl+回车”确认输入信息。

bHideCancelBtn

指定是否隐藏取消按钮禁止用户取消输入。如果为 true(默认),InputBox 只有一个“确定”按钮,没有“X”关闭按钮,按 ESC 无效;如果为 false,InputBox 有“确定”和“取消”按钮,允许点“X”和按 ESC 关闭窗口。

更新代码

string url;
char *  URL;

InputBox(URL,1024,NULL,NULL,"请输入URL地址",NULL,NULL,false);

将URL 转为string格式 的url;

url = URL;

创建文件夹 保存爬取资源

创建reptile();函数

void reptile() {
	//输入URL 正戏开始
	InputBox((LPTSTR)url, 1024, "请输入URL地址");

}

优化代码

void reptile() {
	//输入URL 正戏开始
	InputBox((LPTSTR)url, 1024, "请输入URL地址");

}
-------------------------------
.....
else if (checkButtonSelect(&btnEnter, &m)) {
			//to do
		
		reptile();
			 
		}

创建文件夹

	CreateDirectory("./resource", NULL);

	CreateDirectory("./resource/images", NULL);

	CreateDirectory("./resource/videos", NULL);

抓取函数

 bool startCatch(string url)//抓取url
{
	 
	queue<string> q;//url队列
	q.push(url);

	while (!q.empty())
	{

		string currentUrl = q.front();//当前的URL
		q.pop();//删除

		//解析URL
		if (false == AnalysisURL(url))
		{
			continue;
		}
	}



	return true;
}


原理图
在这里插入图片描述

包含头文件queue

#include

 //解析URL
bool AnalysisURL(string url) {
	HWND hwndurl;
	//获取当前窗口句柄
	hwndurl = GetHWnd();

	//找http协议
	if (string::npos == url.find("http://")) {
	
	MessageBox(hwnd, "解析失败未找到协议",NULL,NULL);
	return false;//解析失败

	}

	if (url.length() <= 7)
	{
		MessageBox(hwnd, "url长度过小", NULL, NULL);
		return false;	
	}

	//截取域名
	int pos = url.find('/', 7);//从第七个开始截取
	if (pos == string::npos)
	{
		sHost = url.substr(7);
		sObject = "/";
	}
	else {
		sHost = url.substr(7, pos - 7);
		sObject = url.substr(pos);

	}

	if (sHost.empty() || sObject.empty())
	{
		MessageBox(hwndurl, "host or object is empty ", NULL, NULL);

		return false;
	}

	cout << "域名:" << sHost << endl << "资源:" << sObject << endl;


	return true;
}

 void reptile() {
	

	//创建文件夹保存爬取资源 video img   

	CreateDirectory("./resource", NULL);

	CreateDirectory("./resource/images", NULL);

	CreateDirectory("./resource/videos", NULL);

	loadimage(&imgAnalysis, "./catch.jpg");
	putimage(0, 0, &imgAnalysis);
	drawButton(&btnClose);
	 

	cin >> url;
	//开始抓
	startCatch(url);



}

	else if (checkButtonSelect(&btnEnter, &m)) {
					//to do
					MessageBox(hwndUI, "请在控制台输入URL地址", NULL, NULL);
					cout << "请输入URL" << endl;
					reptile();
				}

优化 使用多线程进行逻辑判断界面操作

好处 可以一直循环使用程序 不需要重开 UI界面一直在运行 不会死

	LPVOID param = 0; 

mainUI(param);//逻辑初始化界面

	DWORD  threadID = 0;
	
	HANDLE handleUI = CreateThread(NULL, NULL,mainUI, NULL, NULL, &threadID);

DWORD WINAPI mainUI(LPVOID param);

DWORD WINAPI mainUI(LPVOID param)
{
	 
		HWND hwndUI;
		hwndUI = GetHWnd();
		bool titleDrag = false; //表示“标题栏”是否被单击拖动
		while (1) {
			MOUSEMSG m = GetMouseMsg();
			FlushMouseMsgBuffer(); //不能少,后缀快速拖动顶部的标题按钮,将导致鼠标消息太多,
			//出现混乱!
			switch (m.uMsg) {
			case WM_MOUSEMOVE:
				if (checkButtonSelect(&btnTitle, &m)) {
					if (btnTitle.pressed == true) {
						if (titleDrag == false) { // 此时标题栏已经被点击按下,正准备拖动
							titleLastX = m.x; // 记录初始坐标
							titleLastY = m.y;
							titleDrag = true;
						}
						else { // 此时标题栏已经被点击按下,正在拖动
							// 计算拖动偏移量
							int offX = m.x - titleLastX;
							int offY = m.y - titleLastY;
							moveWindow(hwnd, offX, offY); // 根据拖动偏移量,移动窗口
						}
					}
				}
				else if (checkButtonSelect(&btnEnter, &m)) {
					
					btnEnter.pressed = true;
					drawButton(&btnEnter);
				}
				else if (checkButtonSelect(&btnClose, &m)) {
					btnClose.pressed = true;
					drawButton(&btnClose);
				}
				else {
					// 检查鼠标是否从按钮内移动到按钮之外
					if (btnClose.pressed == true) { // 鼠标从关闭按钮移出
						btnClose.pressed = false;
						drawButton(&btnClose);
					}
					if (btnEnter.pressed == true) { // 鼠标从发送按钮移出
						btnEnter.pressed = false;
						drawButton(&btnEnter);
					}
				}
				break;
			case WM_LBUTTONDOWN:
				if (checkButtonSelect(&btnTitle, &m)) {
					btnTitle.pressed = true; // 单击按下标题栏
				}
				else if (checkButtonSelect(&btnClose, &m)) {
					btnClose.pressed = true;
					drawButton(&btnClose);
				}
				else if (checkButtonSelect(&btnEnter, &m)) {
					btnEnter.pressed = true;
					drawButton(&btnEnter);
				}
				break;
			case WM_LBUTTONUP:
				if (checkButtonSelect(&btnClose, &m)) {
					closegraph();
					exit(0);
				}
				else if (checkButtonSelect(&btnEnter, &m)) {
					//to do
					MessageBox(hwndUI, "请在控制台输入URL地址", NULL, NULL);
					cout << "请输入URL" << endl;
					reptile();


				}
				else if (checkButtonSelect(&btnTitle, &m))
				{
					// 松开标题栏按钮(左键抬起)
					btnTitle.pressed = false;
					titleDrag = false;
				}
				break;
			}
		}

	 
	return NULL;
}

联网下载html

bool startCatch(string url)//抓取url
{
	 
	queue<string> q;//url队列
	q.push(url);

	while (!q.empty())
	{

		string currentUrl = q.front();//当前的URL
		q.pop();//删除

		//解析URL
		if (false == AnalysisURL(url))
		 
			continue;
		if (false == Connect())
			continue;

			
	}



	return true;
}

bool Connect() {

	//初始化网络
	WSADATA wsadata;
	if (WSAStartup(MAKEWORD(2, 2), &wsadata))  
		return false;
	 
	//创建套接字
	sock_client = socket(AF_INET, SOCK_STREAM, 0);

	if (sock_client == INVALID_SOCKET)
	 return false;
	 

	//解析域名为IP地址
	hostent *p =  gethostbyname(sHost.c_str());

	if (p == NULL)
		return false;
	//连接web服务器
	sockaddr_in sa;
	sa.sin_family = AF_INET;
	sa.sin_port = htons(80);
	//IP地址
	memcpy(&sa.sin_port, p->h_addr, 4);

	if (connect(sock_client, (sockaddr*)&sa, sizeof(sockaddr)))
		return false;


}

获取网页

string GetHtml(string url)//获取网页
{
	//解析URL
	if (false == AnalysisURL(url))

		return "";
	if (false == Connect())
		return "";

	//获取网页,发送Get请求 HTTP协议
	string info;
	info += "GET" + sObject + "HTTP/1.1\r\n";
	info += "Host: " + sHost + "\r\n";
	info += "Connection: Close\r\n\r\n";

	if (SOCKET_ERROR == send(sock_client, info.c_str(), info.length(), NULL))
		return false;
	
	//接受数据
	char ch = 0;
	string html;
	while (recv(sock_client, &ch, sizeof(char), 0)) {
		html += ch;
	}

	return html;

}

代码优化


​ #Include
​ //接受数据
​ char ch = 0;

fstream dataFile;   //创建一个文件,用于存放html的内容dataFile.open("D:\\html.txt", std::ios::out);
dataFile.open("./html.txt", ios::out);
if (!dataFile)
{
	printf("文件打开失败!\n");
 
}



string html;
while (recv(sock_client, &ch, sizeof(char), 0)) {
	html += ch;
	dataFile << ch;

}
dataFile.close();

运行出现错误connect连接失败;
在这里插入图片描述

更改代码(代码写错了)

	if (SOCKET_ERROR == connect(sock_client, (sockaddr*)&sa, sizeof(sockaddr)) )
		{
			cout << "服务器连接失败" << endl;

			return false;
		}
		else
		{
			cout<<"服务器连接成功"<<endl;
			return true;
		}
	 
	

遍历URL

cout << html << endl;

		//匹配所有URL 正则表达式
		smatch mat;
		string::const_iterator start = html.begin();
		string::const_iterator end = html.end();

		//正则表达式
		regex gex("http://[^\\s'\"<>()]+");
		vector<string> vecUrl;

		while (regex_search(start,end,mat,gex))
		{
			string newurl(mat[0].first, mat[0].second);
			

			 //把新的URL存起来
			vecUrl.push_back(newurl);

			start = mat[0].second;

		}
		
		//遍历所有URL
		for (int i = 0;i < vecUrl.size();i++)
		{
			string filename = "./resource/image/1.jpg";

			//判断是否为图片
			string imgUrl = vecUrl[i];
			if (imgUrl.find(".jpg") != string::npos)
			{
				//这是一个jpg图片
				Download(imgUrl, filename);
			}


		}

下载图片

bool Download(string url, string filename) {

	FILE* fp = fopen(filename.c_str(), "wb");
	char ch = 0;
	char buffer[20] = { 0 };
	int nRecv = 0;
	while (recv(sock_client,buffer,sizeof(buffer),0))
	{
		fwrite(buffer, 1, nRecv, fp);
	}


	fclose(fp);

}

至此,一个图形化界面的c++爬虫就做好了,后续还有代码优化(文件头处理,图片批量下载,代码重构)

源代码:

head.h

#pragma once
#ifndef HEAD_H    //目的是为了防止头文件重复包含
#define HEAD_H
#include <iostream>
#include <string>
#include <conio.h>
#include <queue>
#include <graphics.h>
#include <Windows.h>
#include <regex>
#pragma comment (lib,"WS2_32.lib")

#include <fstream>    //包含文件流的头文件

using namespace std;

#define WINDOW_WIDTH 482
#define WINDOW_HEIGHT 300


//定义按钮
struct Button {
	IMAGE imgNormal;
	IMAGE imgPress;
	int width, highth;
	int x, y;
	int flag; // 按钮的int类型标记
	bool pressed;
};


void initButton(Button* btn, const char* normalFile, const char* pressFile, int width, int highth, int flag);
void drawPNG(int  picture_x, int picture_y, IMAGE* picture);
bool checkButtonSelect(Button* btn, MOUSEMSG* msg);
void moveWindow(HWND hwnd, int offX, int offY);
void drawButton(Button* btn);
void initUI();
DWORD WINAPI mainUI(LPVOID param);

void reptile();//爬虫函数
bool startCatch(string url);
bool AnalysisURL(string url);
bool Connect();//连接网络
string GetHtml(string url);
bool Download(string url,string filename);

int screenWidth;
int screenHeight;
HWND hwnd;//UI窗口句柄
IMAGE imgBg;//背景图片
IMAGE imgAnalysis;
//定义按钮
Button btnClose;
Button btnTitle;
Button btnEnter;

int titleLastX; //窗口的上一次位置(X 坐标位置)
int titleLastY; //窗口的上一次位置(X 坐标位置
 


	
SOCKET sock_client;//客户端套接字

//URL
string url;
string sObject;
string sHost;//主机名


#endif

还有的资源(图片等)我会提供压缩包在我的主页可进行下载。麻烦关注~

参考文献

参考文献

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/35860.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SwiftUI的优缺点

2019年WWDC大会上&#xff0c;苹果在压轴环节向大众宣布了基于Swift语言构建的全新UI框架——SwiftUI&#xff0c;开发者可通过它快速为所有的Apple平台创建美观、动态的应用程序。推荐大量使用struct代替类。 SwiftUI 就是⼀种声明式的构建界面的用户接口工具包。 SwiftUI使用…

机器学习与深度学习——自定义函数进行线性回归模型

机器学习与深度学习——自定义函数进行线性回归模型 目的与要求 1、通过自定义函数进行线性回归模型对boston数据集前两个维度的数据进行模型训练并画出SSE和Epoch曲线图&#xff0c;画出真实值和预测值的散点图&#xff0c;最后进行二维和三维度可视化展示数据区域。 2、通过…

SpringBoot + Vue前后端分离项目实战 || 五:用户管理功能后续

系列文章&#xff1a; SpringBoot Vue前后端分离项目实战 || 一&#xff1a;Vue前端设计 SpringBoot Vue前后端分离项目实战 || 二&#xff1a;Spring Boot后端与数据库连接 SpringBoot Vue前后端分离项目实战 || 三&#xff1a;Spring Boot后端与Vue前端连接 SpringBoot V…

企业内部安全:利用 ADAudit Plus 管理与加强安全审计

在现代数字化时代&#xff0c;企业面临着日益复杂和不断变化的安全威胁。为了保护敏感数据、遵守合规要求以及防范内部威胁&#xff0c;企业需要有效的安全审计解决方案。ADAudit Plus 是一款强大而全面的安全审计工具&#xff0c;可以帮助企业管理和加强内部安全。 ADAudit Pl…

Jenkins持续集成,在Linux中安装最新版Jenkins(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 去年从6月28日发布…

git学习1

打标签 与其他版本控制系统&#xff08;VCS&#xff09;一样&#xff0c;Git 可以给仓库历史中的某一个提交打上标签&#xff0c;以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点&#xff08; v1.0 、 v2.0 等等&#xff09;。 列出标签 在 Git 中列出已有的…

SpringCloud:微服务技术

一、认识微服务&#xff1a; 首先&#xff0c;微服务架构不等于SpringCloud&#xff0c;微服务架构是一种经过良好架构设计的分布式架构方案&#xff0c; &#xff0c;它将应用构建成一系列按业务领域划分模块的&#xff0c;小的自治服务&#xff0c;并解决服务拆分所产生的各种…

github搜索技巧笔记

一、了解 GitHub Watch按钮 Watch可以理解为关注的意思&#xff0c;默认情况下是Not watching&#xff0c;当选择Watch后&#xff0c;你会收到这个GitHub项目的所有动态。比如&#xff1a;有人发起pull request或者issue等。接收动态方式包括个人通知中心或者邮箱。 如果某个…

GIT版本控制常规性操作演示汇总

文章目录 GIT基本操作GIT配置个人信息配置&#xff1a;GIT查看个人信息配置&#xff1a;GIT的三大区域GIT回滚&#xff1a;git resetGIT恢复日志&#xff1a;git reflogGIT三大区域转换GIT新建分支GIT合并分支GIT删除分支码云上创建项目GIT变基&#xff1a;git rebase合并提交记…

设计模式- 一、设计原则-1

一、设计原则 当涉及到软件设计和开发原则时&#xff0c;有一些常见的原则和准则可以帮助我们编写高质量、可维护和可扩展的代码。以下是其中一些重要的原则和准则&#xff1a; SOLID原则&#xff1a; 单一职责原则&#xff08;Single Responsibility Principle&#xff0c;SRP…

举例说明ChatGPT模型是怎么进行无监督学习的

ChatGPT&#xff0c;也称为生成式预训练Transformer&#xff08;GPT&#xff09;&#xff0c;是一种基于Transformer架构的自然语言处理模型。虽然在实际应用中&#xff0c;它主要用于有监督学习任务&#xff0c;但在训练初期&#xff0c;它会经历无监督学习阶段。以下是一个简…

【简单认识LVS及LVS-NAT负载均衡群集的搭建】

文章目录 一、LVS群集简介1、群集的含义2、性能扩展方式3、群集的分类4、负载均衡群集架构1、负载均衡的结构 5、三种负载调度工作模式1、NAT模式2、TUN模式3、DR模式 二、LVS虚拟服务器1、Linux Virtual Server简介2、启用LVS虚拟服务3、LVS调度算法&#xff08;1&#xff09;…

Atcoder Beginner Contest 309——D-F讲解

前言 由于最近期末考试&#xff0c;所以之前几场都没打&#xff0c;给大家带了不便&#xff0c;非常抱歉。 这个暑假&#xff0c;我将会持续更新&#xff0c;并给大家带了更好理解的题解&#xff01;希望大家多多支持。 由于&#xff0c; A ∼ C A\sim C A∼C 题比较简单&am…

Git 上传Github 超时问题

提交代码到GitHub总是超时&#xff0c;偶尔会直接上传成功。 提供一下解决方案 1.首先找到网络 2. 找到代理 3. 把自动检查设置全部关闭&#xff0c;然后打开手动设置代理&#xff0c;然后输入ip地址和你代理的端口号&#xff0c;保存即可。 4. 最后使用git push origin mast…

java中如何将一个集合list转成以逗号隔开的字符串

事例代码 代码&#xff1a; package com.air.app;import java.util.ArrayList; import java.util.List;public class ListToStringTest {public static void main(String[] args) {//定义list集合List<String> list new ArrayList<>();list.add("1");…

基于低代码平台打造的焙乐道销售支持系统

编者按&#xff1a;低代码平台说了那么多&#xff0c;在实际应用中又是怎样体现的它的种种优势呢&#xff1f;今天小编结合实际案例来说说。 本文是以最大的烘焙原料产商——焙乐道的销售支持系统为例子&#xff0c;进行说明。 客户说明&#xff1a;焙乐道是一家国际性集团公司…

Python一行命令搭建HTTP服务器并外网访问+-+内网穿透

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章&#xff1a;【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…

CentOS Linux上安装JDK11、MySQL8.0、Minio等软件(rpm脚本模式)

本地环境&#xff1a;Windows 10家庭版 16G内存 512G硬盘 软件&#xff1a;VMWare WorkStation 16.0 FinalShell 4.0.1 一、下载必要软件包 下载软件均选择x86架构64位&#xff01;&#xff01;&#xff01;&#xff08;可根据自己的电脑配置选择&#xff09; CentOS Linu…

数字图像处理(三)

目录 实验六、图像分割方法 实验七、图像识别与分类 实验六、图像分割方法 一、实验目的 了解图像分割技术相关基础知识&#xff1b;掌握几种经典边缘检测算子的基本原理、实现步骤理解阈值分割、区域分割等的基本原理、实现步骤。理解分水岭分割方法的基本原理、实现方法。…

ModaHub魔搭社区:Zilliz Cloud快速开始教程(一)

目录 前提条件 创建 Collection 查看 Collection 插入数据 本教程涵盖以下 Zilliz Cloud 集群操作指南: 创建 Collection查看 Collection插入数据向量搜索、向量查询、通过 ID 获取 Entity删除 Entity删除 Collection 前提条件 在本文档中,我们将使用 Milvus 的 SDK。…