C#实战分享--爬虫的基础原理及实现

关注我,持续分享逻辑思维&管理思维; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;
有意找工作的同学,请参考博主的原创:《面试官心得--面试前应该如何准备》,《面试官心得--面试时如何进行自我介绍》《做好面试准备,迎接2024金三银四》。
推荐热榜内容:《架构实战--以海量存储系统讲解热门话题:分布式概念

-------------------------------------正文----------------------------------------

网终爬虫基本原理可以概括为以下几个步骤:

  • 建立待爬取的URL队列。爬虫首先需要确定要抓取的网页集合,这些URL形成一个队列。
  • 下载和解析网页。爬虫从队列中取出一个URL,并下载此URL。然后对该URL对应的网页进行解析。
  • 解析网页内容。爬虫下载网页后,需要解析这些内容,提取出有用的信息,如文本、图像和其他数据。
  • 管理和调度任务。爬虫可能需要管理多个任务,包括URL队列、线程池和排重机制,以确保高效和稳定的运行。
  • 保存数据。爬虫提取的信息需要被保存到适当的存储位置,如数据库、文件系统或其他数据存储服务。

博主曾经运营过一个段子网,当时写了个网络爬虫,抓取网上的段子到自己的网站数据库,以便我的段子网可以直接浏览这些内容。为便于观看,及便于让大家理解,以下代码我节选主要的内容,并在这次分享的过程中,加入了一些注释,以便大家理解。

一、根据上面的步骤,我们先建立URL队列,一般有3个队列(待抓取,已抓取,坏链)

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using xu_common.log;namespace data_collection.spider_core
{class UrlSet{public UrlSet(){// 定义三个队列,准备抓取的URL集合,已经抓取的URL集合,错误的URL集合// 这里需要注意,因为我是定向抓取,这里URL不会太多。如果是全网抓取,URL太多,要防止内存泄露_going_to_parse = new ArrayList(); _already_parse = new ArrayList();_error_link = new ArrayList();}private static UrlSet __instance = null;public static UrlSet instance{get{if (__instance == null){__instance = new UrlSet();}return __instance;}            }private ArrayList _going_to_parse = null;private ArrayList _already_parse = null;private ArrayList _error_link = null;// 判断URL是否抓取过,并根据参数add是否true来判断这个URL是否入库private bool is_url_parsed(string url, bool add){bool rv;lock (_already_parse.SyncRoot){rv = _already_parse.Contains(url);if (!rv && add)_already_parse.Add(url);}return rv;}// 判断URL是否抓取过private bool is_url_parsed(string url){return is_url_parsed(url, false);}// 判断URL是否在待抓取列表,并根据参数add是否true来判断这个URL是否加入待抓取private bool is_url_going_to_parse(string url, bool add){bool rv;lock (_going_to_parse.SyncRoot){rv = _going_to_parse.Contains(url);if (!rv && add)_going_to_parse.Add(url);}return rv;}// 判断URL是否在待抓取列表private bool is_url_going_to_parse(string url){return is_url_going_to_parse(url, false);}// 判断URL是否在错误URL列表,并根据add来确定是否要加入此列表private bool is_url_error_lnk(string url, bool add){bool rv;lock (_error_link.SyncRoot){rv = _error_link.Contains(url);if (!rv && add)_already_parse.Add(url);}return rv;}private bool is_url_error_lnk(string url){return is_url_error_lnk(url, false);}/// <summary>/// 把一个Url加到待解析列表中./// 如果已经解析过,返回-1; /// 如果是坏链,返回-2/// 如果已经在待解析列表中,返回1./// 否则加入待解析列表,并且返回0/// </summary>/// <param name="url"></param>/// <returns>>=0:OK, <0:ERROR</returns>public int add_going_parse_url(string url){lock (_going_to_parse.SyncRoot){if (is_url_parsed(url)){return -1;}if (is_url_error_lnk(url)){return -2;}if (is_url_going_to_parse(url, true))return 1;//_going_to_parse.Add(url);return 0;}}/// <summary>/// 添加一个已经抓取过的链接,如果此链接在待抓取或者坏链中,删除/// 如果已经在抓取过列表中,返回-1,否则返回0/// </summary>/// <param name="url"></param>/// <returns>0:OK,-1:ERROR</returns>public int add_parsed_url(string url){// already parse, not use to parse again.if (is_url_going_to_parse(url)){_going_to_parse.Remove(url);}if (is_url_error_lnk(url)){_error_link.Remove(url);//return -1;}if (is_url_parsed(url, true)){return -1;}//_already_parse.Add(url);return 0;}/// <summary>/// 添加一个错误的链接.如果该链接在待抓取列表中,删除(说明不应该抓取)/// </summary>/// <param name="url"></param>/// <returns>0:OK; -1:ERROR</returns>public int add_error_url(string url){if (is_url_going_to_parse(url)){_going_to_parse.Remove(url);}/*if (is_url_parsed(url)){return -2;//都已经解析过了,还加进这里去干嘛? never go to here,因为解析过的话,不可能再拿来解析,然后到这里的}* */if (is_url_error_lnk(url, true)){return -1;}//_error_link.Add(url);return 0;}/// <summary>/// 把代解析的第一个节点抓下来,成功,则url有值,并返回true, 不然返回错误/// </summary>/// <param name="url"></param>/// <returns></returns>public bool pop_going_to_parse_url(ref string url){url = "";bool rv = false;lock (_going_to_parse.SyncRoot){if (_going_to_parse.Count <= 0){rv = false;}else {url = _going_to_parse[0].ToString();_going_to_parse.RemoveAt(0);rv = true;}}return rv;}public int going_to_parse_url_num(){int ret = 0;lock (_going_to_parse.SyncRoot){ret = _going_to_parse.Count;}return ret;}private string[] _no_parse_keyword = null;private int _no_parse_type = 3;public void SetNoParseKeyWord(string str, string split, int type){_no_parse_keyword = xu_common.CommonOperator.Split(str, split);_no_parse_type = type;}public bool IsNoParse(string url){LogMsg.LogError(url + ", no_parse_type="+ _no_parse_type.ToString());if (_no_parse_type == 1){for (int i = 0; i < _no_parse_keyword.Length; i++){if (url.Contains(_no_parse_keyword[i]))return false;}return true;}else if(_no_parse_type==2){for (int i = 0; i < _no_parse_keyword.Length; i++){if (url.Contains(_no_parse_keyword[i]))return true;}return false;}return false;}#region write back to file: ToStringpublic string GoingParseToString(){string ret = "";int count = _going_to_parse.Count;for (int i = 0; i < count-1; i++){ret += _going_to_parse[i].ToString() + "\r\n";}if (count > 0)ret += _going_to_parse[count - 1].ToString();_going_to_parse.Clear();return ret;}public string AlreadyParsedToString(){string ret = "";int count = _already_parse.Count;for (int i = 0; i < count - 1; i++){ret += _already_parse[i].ToString() + "\r\n";}if (count > 0)ret += _already_parse[count - 1].ToString();_already_parse.Clear();return ret;}public string ErrorUrlToString(){string ret = "";int count = _error_link.Count;for (int i = 0; i < count - 1; i++){ret += _error_link[i].ToString() + "\r\n";}if (count > 0)ret += _error_link[count - 1].ToString();_error_link.Clear();return ret;}#endregion}
}

二、下载和解析网页

以下代码是一个线程抓取文件(非网页内容,如图片、文件)的主要代码:设置一个URL,抓取后把URL放到已抓取队列,并保存网页内容到文件中; 抓取失败,则把URL放到错误队列。

有些注释掉的 MessageBox是博主当时调试用的,大家可忽略。

namespace data_collection.spider_core
{// 继承的Task类是博主写的公共类,这里不是关键代码,不再贴出。// 这里是一个子线程。需要设置抓取的URL和网页文件保存路径。为了加快抓取速度,需要启动多线程。class DownFileTask : xu_common.thread.Task{public override void Run(){FileStream fileStream = new FileStream(_filepath, FileMode.Append|FileMode.Create, FileAccess.Write);Stream inStream = null;try{HttpWebRequest myre = (HttpWebRequest)WebRequest.Create(_url);if (fileStream.Length == myre.ContentLength){//MessageBox.Show("你已完成下载该程序了", "ok");return;}myre.AddRange(Convert.ToInt32(fileStream.Length));//接上次下载的字节开始下载文件   HttpWebResponse response = (HttpWebResponse)myre.GetResponse();inStream = response.GetResponseStream();//this.progressBar1.Maximum = total;//this.progressBar1.Minimum = 0;int length = 1024;byte[] buffer = new byte[1025];int readerLength = 0, currentLength = 0;while ((readerLength = inStream.Read(buffer, 0, length)) > 0){currentLength += readerLength;fileStream.Write(buffer, 0, readerLength);//this.progressBar1.Value = currentLength + countByte;fileStream.Flush();}fileStream.Close();inStream.Close();//File.Delete(Application.StartupPath + @"\FileLength.txt");//MessageBox.Show("down   成功", "ok");// 抓取成功,这个URL放入已抓取队列UrlSet.instance.add_parsed_url(_url);}catch (Exception ex){xu_common.log.LogMsg.LogError("down file:" + _url + ", error.msg:" + ex.ToString());// 抓取失败,这个URL放入已失败队列UrlSet.instance.add_error_url(_url);}}public void SetUrl(string url) { _url = url; }private string _url;public void SetFilePath(string filepath) { _filepath = filepath; }private string _filepath;}
}

以上是下载文件和图像的代码。
------------------------------------------

以下代码是下载网页的核心代码。
1.初始化代抓取的URL,设置浏览器代理(欺骗对应网站)。
2.下载URL对应的网页内容。
3.从URL的内容中解析结构化内容(根据规则),同时根据网页内容,解析到很多URL(网页的外链)。
3.1 如果URL是需要下载的文件或图片,则新起一个线程,用上面的代码下载。
3.2 如果URL是要继续抓取的网页URL,则放到待爬取链接。

注(重要):此处,抓取到的内容(保存为_content)的解析,这里博主写了一个规则处理器。所以下面代码一句话带过。规则处理器比较简单,根据下载的网站不同。比如你想下载 http://xx.com/a/1.html,网站的内容,一般是通用的,我们就可以设置一个规则:只要满足URL为http://xx.com/a/*.html的,则按以下规则处理:跳过<html>………………等代码,直到找到内容开始处,这里需要我们实际看一下http://xx.com/a/1.html的源码,一般是<div>或<panel>等特征开始。我们把这些内容跳过即可。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using xu_common.log;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Security.Policy;namespace data_collection.spider_core
{class WebGetHtml{// 初始化要抓取的网页URLpublic WebGetHtml(string parse_url){_url = parse_url;}string _url = null;string _tag_base_url = null;string _title;string _base_url = null;string _base_top_url = null;string _content;private void GenBaseUrl(){if (_tag_base_url == null){_base_top_url = global_var.RegTopUrl.Match(_url).Value;string not_param_url = null;int qPisition = this._url.IndexOf("?");if (qPisition < 0){not_param_url = _url;}else{not_param_url = _url.Substring(0, qPisition);}_base_url = Regex.Replace(not_param_url, "(?<=.*/)[^/]*$", "", RegexOptions.Compiled | RegexOptions.IgnoreCase);}else{_base_top_url = global_var.RegTopUrl.Match(_tag_base_url).Value;_base_url = _tag_base_url;}if (_base_url.EndsWith("/")){_base_url = _base_url.Substring(0, _base_url.Length - 1);}}public class UrlType{public static int UrlTypeImg = 1;public static int UrlTypeFile = 2;public static int UrlTypeHtml = 3;public static int UrlTypeError = 4;public static int UrlTypeSelf = 5;public static int UrlTypeOtherFile = 6;};// 检查URL类型private int CheckUrl(string UrltoCheck){if (Regex.IsMatch(UrltoCheck, "^#*$", RegexOptions.IgnoreCase | RegexOptions.Compiled))return UrlType.UrlTypeError;else if (UrltoCheck == _url || (UrltoCheck + "/") == _url || UrltoCheck == (_url + "/"))return UrlType.UrlTypeSelf;else if (UrltoCheck.EndsWith(".css"))return UrlType.UrlTypeOtherFile;else if (UrltoCheck.EndsWith(".wmv"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".asf"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".mp3"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".avi"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".mpg"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".mpeg"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".rmvb"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".rm"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".doc"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".rar"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".zip"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".tar"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".xls"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".pdf"))return UrlType.UrlTypeFile;else if (UrltoCheck.EndsWith(".jpg"))return UrlType.UrlTypeImg;else if (UrltoCheck.EndsWith(".jpeg"))return UrlType.UrlTypeImg;else if (UrltoCheck.EndsWith(".ico"))return UrlType.UrlTypeImg;else if (UrltoCheck.EndsWith(".gif"))return UrlType.UrlTypeImg;else if (UrltoCheck.EndsWith(".bmp"))return UrlType.UrlTypeImg;else if (UrltoCheck.EndsWith(".png"))return UrlType.UrlTypeImg;else if (UrltoCheck.StartsWith("ftp://"))return UrlType.UrlTypeError;else if (UrltoCheck.StartsWith("telnet://"))return UrlType.UrlTypeError;else if (UrltoCheck.StartsWith("mms://"))return UrlType.UrlTypeError;else if (UrltoCheck.StartsWith("rstp://"))return UrlType.UrlTypeError;else if (UrltoCheck.StartsWith("mailto"))return UrlType.UrlTypeError;else if (UrltoCheck.StartsWith("javascript"))return UrlType.UrlTypeError;elsereturn UrlType.UrlTypeHtml;}//确定URL是否属于要抓取的网站private bool CheckUrlThisSite(string NewUrltoCheck){//return Form1.instance.CheckUrlToParse(NewUrltoCheck);return global_var.Instance.IsInSite(NewUrltoCheck);            }// 有些网页的链接不是以http开头,而是相对链接。需要处理这些不规则链接private string GenUrl(string incomeUrl){if (incomeUrl.StartsWith("http://"))return incomeUrl;else{/** /x.aspx直接是base里的(或者窗口TopUrl)顶级域名,加上x.aspxx.aspx直接是base里的(或者窗口TopUrl)Url,加上x.aspx./x.aspx直接是base里的(或者窗口TopUrl)Url,加上x.aspx(同上)../x.aspx直接是base里的(或者窗口TopUrl)Url的上一层,加上x.aspx*/if (incomeUrl.StartsWith("/")){                   string trueUrl = _base_top_url + incomeUrl;return trueUrl;}int parent_depth = 0;                while (incomeUrl.StartsWith(".")){if (incomeUrl.StartsWith("../")){parent_depth += 1;incomeUrl.Substring(3, incomeUrl.Length - 3);}else if (incomeUrl.StartsWith("./")){incomeUrl = incomeUrl.Substring(2, incomeUrl.Length - 2);}elsereturn null;}string head_str = _base_url;if (parent_depth > 0){for (int i = 0; i < parent_depth; i++){int qposition = head_str.LastIndexOf("/");if (qposition < 0){// not_http_lenght_not_enoughhead_str = _base_top_url;break;}head_str = head_str.Substring(0, qposition);}}if (head_str.StartsWith("http:") && head_str.Length < "http://".Length){//不是http开头的,长度不够的情况,在前面 not_http_lenght_not_enough 处理了//说明:如果base_url=http://a.com/b/,但是有../../../a.html,没有足够的目录的情况下//Url是指:http://a.com/b/a.htmlhead_str = _base_top_url;}return head_str + "/" + incomeUrl;}}//下载网页内容private bool WebGetContent(){HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(_url);// 设置浏览器代理myRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36";//Encoding encode = System.Text.Encoding.GetEncoding(936);HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();//myResponse.ContentEncoding;//LogMsg.instance.LOG(LogMsg.DEBUG, "begin to parse response");//LogMsg.LogDebug("begin to parse response");if (myResponse.StatusCode != HttpStatusCode.OK){LogMsg.LogError("get url:" + _url + ", error. status:"+myResponse.StatusDescription);return false;}Stream src_stream = myResponse.GetResponseStream();StreamReader myStreamReader = null;if (myResponse.ContentEncoding != null &&myResponse.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase)){myStreamReader = new StreamReader(new GZipStream(src_stream, CompressionMode.Decompress),Encoding.Default);}else{myStreamReader = new StreamReader(src_stream,Encoding.Default);}ArrayList response_bytes_array = new ArrayList();int i_byte = 0;while (true){i_byte = myStreamReader.BaseStream.ReadByte();if (i_byte == -1)break;response_bytes_array.Add(Convert.ToByte(i_byte));}byte[] response_bytes = new byte[response_bytes_array.Count];for(int i=0;i<response_bytes_array.Count; i++){response_bytes[i] = Convert.ToByte( response_bytes_array[i]);}string tmphtml = Encoding.Default.GetString(response_bytes);string encoding_name = "";if (global_var.RegCharset.IsMatch(tmphtml)){encoding_name = global_var.RegCharset.Match(tmphtml).Groups["charset"].Value;}else if (myResponse.CharacterSet != string.Empty){encoding_name = myResponse.CharacterSet;}Encoding encoding = null;if (encoding_name != null){try{encoding = Encoding.GetEncoding(encoding_name);}catch{encoding = Encoding.Default;}}elseencoding = Encoding.Default;// 获取到此网页的内容_content = encoding.GetString(response_bytes);          //LogMsg.LogInfo("Encoding:" + encoding.EncodingName + ", encoding_name:"//+ encoding_name);myStreamReader.Close();myResponse.Close();//LogMsg.LogDebug("end to parse response");return true;}public void GetCode(){try{// LogMsg.LogDebug("begin to parse url: [" + _url + "]!");bool get_html = WebGetContent();if (!get_html){UrlSet.instance.add_error_url(_url);LogMsg.LogInfo("url: [" + _url + "] error!");return;}_content = _content.Replace("&amp;", "&");if (global_var.RegBaseUrl.IsMatch(_content)){_tag_base_url = global_var.RegBaseUrl.Match(_content).Value;}//解析到有没有base标签后,开始生成本地的链接的相对对象GenBaseUrl();_title = global_var.RegTitle.Match(_content).Value;LogMsg.LogDebug("url: [" + _url + "] title: [" + _title + "]!");if (_title == ""){// LogMsg.LogDebug("content:"+_content);}//下面如果Return false,则表明此Url不处于规则中,不用保存,解析其Url即可bool save = RoleSet.Instance.OSave(_url, _content, _title);MatchCollection myMC = global_var.RegAnchor.Matches(_content);int num = 0;foreach (Match i in myMC){if (i.Value == "")continue;string newUrl = i.Value;int url_type = CheckUrl(newUrl);if (url_type == UrlType.UrlTypeError ||url_type == UrlType.UrlTypeOtherFile ||url_type == UrlType.UrlTypeSelf)continue;newUrl = GenUrl(newUrl);if (url_type == UrlType.UrlTypeHtml){bool pass = CheckUrlThisSite(newUrl);if (pass && !UrlSet.instance.IsNoParse(newUrl)){                            //bool pass3 = RoleSet.Instance.OIsMatch(newUrl);//if (pass3)//{//LogMsg.LogDebug("add going parse url:"+newUrl);num++;UrlSet.instance.add_going_parse_url(newUrl);//}}}else{//如果是文件,图片,不用检查是否本站,但是要规则处检查,如果通过,直接起线程下载之Role role = RoleSet.Instance.OIsMatchRole(newUrl);if (role != null){//下载DownFileTask downfile_task = new DownFileTask();downfile_task.SetUrl(newUrl);string save_as_path = role.GetSaveAsPath();string filename = newUrl.GetHashCode().ToString("X");filename += "_"+Path.GetFileName(newUrl);save_as_path += filename;downfile_task.SetFilePath(save_as_path);//不另外起线程了,谁叫你保存File的UrlSet.instance.add_going_parse_url(newUrl);xu_common.thread.ThreadPoll.Execute(downfile_task);}}}MatchCollection myMC2 = global_var.RegAnchor2.Matches(_content);foreach (Match i in myMC2){if (i.Value == "")continue;string newUrl = "http://" + i.Value;int url_type = CheckUrl(newUrl);if (url_type == UrlType.UrlTypeError ||url_type == UrlType.UrlTypeOtherFile ||url_type == UrlType.UrlTypeSelf)continue;// newUrl = GenUrl(newUrl);if (url_type == UrlType.UrlTypeHtml){bool pass = CheckUrlThisSite(newUrl);if (pass && !UrlSet.instance.IsNoParse(newUrl)){//bool pass3 = RoleSet.Instance.OIsMatch(newUrl);//if (pass3)//{//LogMsg.LogDebug("add going parse url:"+newUrl);num++;UrlSet.instance.add_going_parse_url(newUrl);//}}}else{//如果是文件,图片,不用检查是否本站,但是要规则处检查,如果通过,直接起线程下载之Role role = RoleSet.Instance.OIsMatchRole(newUrl);if (role != null){//下载DownFileTask downfile_task = new DownFileTask();downfile_task.SetUrl(newUrl);string save_as_path = role.GetSaveAsPath();string filename = newUrl.GetHashCode().ToString("X");filename += "_" + Path.GetFileName(newUrl);save_as_path += filename;downfile_task.SetFilePath(save_as_path);//不另外起线程了,谁叫你保存File的UrlSet.instance.add_going_parse_url(newUrl);xu_common.thread.ThreadPoll.Execute(downfile_task);}}}UrlSet.instance.add_parsed_url(_url);LogMsg.LogDebug(_url + ", have:" + num.ToString() + " urls, now going parse url num:" + UrlSet.instance.going_to_parse_url_num() + (save ? "save" : "nosave"));}                catch(Exception ex){LogMsg.LogError("error:" + ex.ToString());UrlSet.instance.add_error_url(_url);}}}
}

三、管理和调度任务

这里管理和调度任务不再赘述,如上面下载文件和图片一样,可以使用多线程进行下载。这些管理代码不难,且上面已有示例,下载网页的任务调度类似。保存在Url队列中,按顺序启动多线程去获取内容。

四、保存数据

这块代码比较隐私。博主在上面的代码处也有说明。是根据对应的网站,在获取网页源码内容后,需要自己写规则解析,解析到对应的内容后,直接调用DB操作类写入到数据库。

以上就是本次分享的内容。如果有兴趣的,欢迎关注博主私聊。

博主其它经典原创:《管理心得--工作目标应该是解决业务问题,而非感动自己》,《管理心得--如何高效进行跨部门合作》,《管理心得--员工最容易犯的错误:以错误去掩盖错误》,《技术心得--如何成为优秀的架构师》、《管理心得--如何成为优秀的架构师》、《管理心理--程序员如何选择职业赛道》。欢迎大家阅读。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rust - 将windows剪贴板的截图保存为png

本文提供了将windows系统的截图另存为png格式图片的方法。 添加依赖 cargo add clipboard-win cargo add image cargo add windows配置修改windows依赖特性 [dependencies] image "0.25.0"[target.cfg(windows).dependencies] windows "0.51.1" clipb…

ubuntu 如何使用阿里云盘

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

(简单成功)Mac:命令设置别名

案例&#xff1a;给"ls -l"命令&#xff0c;设置别名通过”ll“快速访问 1、在项目根目录底下查看有无.bash_profile文件&#xff0c;注意这个是个隐藏文件&#xff0c;需要使用ls -a命令查看&#xff1a; 没有.bash_profile新建一个文件&#xff0c; 在最后添加一行…

【C++】AVL树的两单旋和两双旋

目录 1. 新节点插入较高左子树的左侧---左左&#xff1a;右单旋 代码 2. 新节点插入较高右子树的右侧---右右&#xff1a;左单旋 代码 3. 新节点插入较高左子树的右侧---左右&#xff1a;先左单旋再右单旋 ​编辑 代码 4. 新节点插入较高右子树的左侧---右左&#xff1a;先…

Java基础知识总结(7)

StringBuffer类 StringBuffer类,线程安全的可变字符序列&#xff0c;初始化容量为16个字符。一个类似于String的字符串缓冲区&#xff0c;但是不能修改。 常用的是append和insert方法,可以重载&#xff0c;以接纳不同类型的参数类型 x.append("y") 等价于 insert(x…

(总结)OpenOFDM接收端信号处理流程

Overview — OpenOFDM 1.0 documentation 本篇文章为学习OpenOFDM之后的产出PPT&#xff0c;仅供学习参考。

Linux实践 - 命令行解释器 简易版

~~~~ 前言解决的问题为什么shell要以子进程的方式执行我们的命令&#xff1f;为什么直接使用程序名ls&#xff0c;而不是路径/usr/bin/ls&#xff1f; 头文件包含命令行提示符接受用户命令行输入解析用户的输入内建命令&&特殊处理ls 时目录等文件不带高亮颜色cd时目录不…

LabVIEW NV色心频率扫描

LabVIEW NV色心频率扫描 通过LabVIEW软件开发一个能够实现对金刚石氮空位&#xff08;Nitrogen-Vacancy&#xff0c;NV&#xff09;色心的频率扫描系统。系统通过USB协议与硬件设备通信&#xff0c;对NV色心进行高精度的频率扫描&#xff0c;满足了频率在2.6 GHz到3.2 GHz范围…

nginx搭建及部署

目录 一、nginx是什么&#xff1f; 二、安装部署 1.下载 2.配置 3.代理Swagger服务 4.nginx命令 一、nginx是什么&#xff1f; 是用于 Web 服务、反向代理、内容缓存、负载均衡、媒体流传输等场景的开源软件。它最初是一款专为实现最高性能和稳定性而设计的 Web 服务器。…

Laravel框架项目首页内容修改

#Laravel# 安装Laravel框架成功后运行项目&#xff0c;看到下面这个图就说明安装框架成功了 需要根据自己的需求修改页面时&#xff0c;先找到首页的文件 首页对应的页面文件为项目根目录下的resources/views/welcome.blade.php文件 <!DOCTYPE html> <html lang&quo…

C++特性三:多态---案例三(电脑组装)

案例描述&#xff1a; 电脑主要组成部件为 CPU&#xff08;用于计算&#xff09;&#xff0c;显卡&#xff08;用于显示&#xff09;&#xff0c;内存条&#xff08;用于存储&#xff09; 将每个零件封装出抽象基类&#xff0c;并且提供不同的厂商生产不同的零件&#xff0c;例…

Android 逆向(三)-adb常用逆向命令

Android 逆向(三)-adb常用逆向命令 本文主要介绍下逆过程中会用到的一些adb命令. 1: adb shell dumpsys dbinfo 该命令可以查看指定包名的数据库存储信息 用法: adb shell dumpsys dbinfo [pkgname] Applications Database Info:** Database info for pid 29729 [com.sohu…

npm install报错: https://registry.npm.taobao.org/: certificate has expired

npm安装依赖时&#xff0c;出现报错&#xff1a; https://registry.npm.taobao.org/: certificate has expired&#xff0c;这是因为之前设置过npm使用淘宝源&#xff0c;而淘宝源证书过期了。 解决方法——替换新的淘宝源 https://registry.npmmirror.com/ 打开终端 #查看 np…

网格bfs,LeetCode 2684. 矩阵中移动的最大次数

一、题目 1、题目描述 给你一个下标从 0 开始、大小为 m x n 的矩阵 grid &#xff0c;矩阵由若干 正 整数组成。 你可以从矩阵第一列中的 任一 单元格出发&#xff0c;按以下方式遍历 grid &#xff1a; 从单元格 (row, col) 可以移动到 (row - 1, col 1)、(row, col 1) 和…

GO-Gin-Example 第六部分 将Golang应用部署到Docker

文章目录 Dockerdocker相关部分知识docker安装winddows环境 本部分目标实现编写dockerfile拉取mysql镜像修改配置文件构建镜像验证镜像创建并运行容器,将golang容器和MySql容器关联挂载主机目录重新运行golang容器验证 Docker docker相关部分知识 runoob docker教程 同站友人…

Visual Studio 2013 - 调试模式下查看监视窗口

Visual Studio 2013 - 调试模式下查看监视窗口 1. 监视窗口References 1. 监视窗口 Ctrl Alt W&#xff0c;1-4&#xff1a;监视窗口 (数字键不能使用小键盘) or 调试 -> 窗口 -> 监视 -> 监视 1-4 调试状态下使用&#xff1a; 在窗口中点击空白行&#xff0c;…

如何在 Ubuntu 14.04 VPS 上添加和删除用户

简介 在新的 Linux 服务器上&#xff0c;你应该知道如何添加和删除用户是最基本的任务之一。当你创建一个新的服务器时&#xff0c;默认情况下只会给你 root 账户。 虽然这给了你很大的权力和灵活性&#xff0c;但也很危险&#xff0c;可能会造成破坏。通常最好的做法是添加一…

目标检测——PP-PicoDet算法解读

PP-YOLO系列&#xff0c;均是基于百度自研PaddlePaddle深度学习框架发布的算法&#xff0c;2020年基于YOLOv3改进发布PP-YOLO&#xff0c;2021年发布PP-YOLOv2和移动端检测算法PP-PicoDet&#xff0c;2022年发布PP-YOLOE和PP-YOLOE-R。由于均是一个系列&#xff0c;所以放一起解…

Java八股文(RabbitMQ)

Java八股文のRabbitMQ RabbitMQ RabbitMQ RabbitMQ 是什么&#xff1f;它解决了哪些问题&#xff1f; RabbitMQ 是一个开源的消息代理中间件&#xff0c;用于在应用程序之间进行可靠的异步消息传递。 它解决了应用程序间解耦、消息传递、负载均衡、故障恢复等问题。 RabbitMQ …

长安链智能合约标准协议第二草案——BNS与DID协议邀请社区用户评审

长安链智能合约标准协议 在智能合约编写过程中&#xff0c;不同的产品及开发人员对业务理解和编程习惯不同&#xff0c;即使同一业务所编写的合约在具体实现上也可能有很大差异&#xff0c;在运维或业务对接中面临较大的学习和理解成本&#xff0c;现有公链合约协议规范又不能完…