当前位置: 首页 > article >正文

增量式网络爬虫通用模板

之前做过一个项目,他要求是只爬取新产生的或者已经更新的页面,避免重复爬取未变化的页面,从而节省资源和时间。这里我需要设计一个增量式网络爬虫的通用模板。可以继承该类并重写部分方法以实现特定的解析和数据处理逻辑。这样可以更好的节约时间。

在这里插入图片描述

以下就是我熬几个通宵写的一个Python实现的增量式网络爬虫通用模板,使用SQLite数据库存储爬取状态,实现URL去重、增量更新和断点续爬功能。

import sqlite3
import hashlib
import time
import requests
from urllib.parse import urlparse, urljoin
from bs4 import BeautifulSoup
import re
import os
import logging
from queue import Queue
from threading import Thread, Lock# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler("incremental_crawler.log"),logging.StreamHandler()]
)
logger = logging.getLogger(__name__)class IncrementalCrawler:def __init__(self, db_path='crawler.db', max_threads=5, max_depth=3, politeness_delay=1.0, user_agent=None):"""增量式网络爬虫初始化参数:db_path: 数据库文件路径max_threads: 最大线程数max_depth: 最大爬取深度politeness_delay: 请求延迟时间(秒)user_agent: 自定义User-Agent"""self.db_path = db_pathself.max_threads = max_threadsself.max_depth = max_depthself.politeness_delay = politeness_delayself.user_agent = user_agent or "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"# 初始化数据库self._init_database()# 线程安全锁self.lock = Lock()# 请求会话self.session = requests.Session()self.session.headers.update({"User-Agent": self.user_agent})# 爬取队列self.queue = Queue()# 统计信息self.stats = {'total_crawled': 0,'total_links_found': 0,'start_time': time.time(),'last_crawl_time': 0}def _init_database(self):"""初始化数据库结构"""with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()# 创建URL表cursor.execute('''CREATE TABLE IF NOT EXISTS urls (id INTEGER PRIMARY KEY,url TEXT UNIQUE NOT NULL,depth INTEGER DEFAULT 0,status TEXT DEFAULT 'pending',content_hash TEXT,last_crawled REAL,created_at REAL DEFAULT (datetime('now')))''')# 创建域延迟表cursor.execute('''CREATE TABLE IF NOT EXISTS domain_delays (domain TEXT PRIMARY KEY,last_request REAL DEFAULT 0)''')conn.commit()def add_seed_urls(self, urls, depth=0):"""添加种子URL到队列和数据库"""with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()for url in urls:# 规范化URLnormalized_url = self._normalize_url(url)if not normalized_url:continue# 检查URL是否已存在cursor.execute("SELECT 1 FROM urls WHERE url = ?", (normalized_url,))if cursor.fetchone():continue# 插入新URLtry:cursor.execute("INSERT INTO urls (url, depth, status) VALUES (?, ?, ?)",(normalized_url, depth, 'pending'))self.queue.put((normalized_url, depth))logger.info(f"Added seed URL: {normalized_url} at depth {depth}")except sqlite3.IntegrityError:pass  # URL已存在conn.commit()def _normalize_url(self, url):"""规范化URL"""parsed = urlparse(url)if not parsed.scheme:return None# 移除URL中的片段标识符return parsed.scheme + "://" + parsed.netloc + parsed.pathdef _get_domain(self, url):"""从URL中提取域名"""return urlparse(url).netlocdef _should_crawl(self, url, depth):"""决定是否应该爬取该URL"""# 检查深度限制if depth > self.max_depth:return False# 检查URL是否已爬取with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("SELECT content_hash, last_crawled FROM urls WHERE url = ?", (url,))row = cursor.fetchone()if not row:return True  # 新URL,需要爬取content_hash, last_crawled = row# 如果从未成功爬取过,则重试if last_crawled is None:return True# 如果上次爬取时间超过24小时,则重新爬取if time.time() - last_crawled > 24 * 3600:return Truereturn Falsedef _respect_politeness(self, domain):"""遵守爬取礼貌规则,避免对同一域名请求过快"""with self.lock:with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("SELECT last_request FROM domain_delays WHERE domain = ?", (domain,))row = cursor.fetchone()last_request = 0if row:last_request = row[0]# 计算需要等待的时间elapsed = time.time() - last_requestif elapsed < self.politeness_delay:wait_time = self.politeness_delay - elapsedlogger.debug(f"Respecting politeness for {domain}, waiting {wait_time:.2f}s")time.sleep(wait_time)# 更新最后请求时间cursor.execute("INSERT OR REPLACE INTO domain_delays (domain, last_request) VALUES (?, ?)",(domain, time.time()))conn.commit()def _fetch_url(self, url):"""获取URL内容"""try:response = self.session.get(url, timeout=10)response.raise_for_status()  # 检查HTTP错误return response.content, response.status_codeexcept requests.RequestException as e:logger.error(f"Error fetching {url}: {str(e)}")return None, Nonedef _extract_links(self, content, base_url):"""从HTML内容中提取链接"""soup = BeautifulSoup(content, 'html.parser')links = set()# 提取所有<a>标签的hreffor a_tag in soup.find_all('a', href=True):href = a_tag['href'].strip()if not href or href.startswith('javascript:'):continue# 解析相对URLabsolute_url = urljoin(base_url, href)normalized_url = self._normalize_url(absolute_url)if normalized_url:links.add(normalized_url)return list(links)def _calculate_hash(self, content):"""计算内容哈希值"""return hashlib.sha256(content).hexdigest()def _process_page(self, url, depth, content, status_code):"""处理页面内容"""# 计算内容哈希content_hash = self._calculate_hash(content)# 检查内容是否已存在with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("SELECT id FROM urls WHERE content_hash = ?", (content_hash,))existing_id = cursor.fetchone()if existing_id:logger.info(f"Content already exists for {url}, skipping processing")else:# 处理内容 - 用户可重写此部分self.process_content(url, content)# 更新数据库with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("""UPDATE urls SET status = ?, content_hash = ?, last_crawled = ? WHERE url = ?""",('completed', content_hash, time.time(), url))conn.commit()# 提取链接links = self._extract_links(content, url)new_depth = depth + 1new_urls = []# 添加新链接到数据库和队列with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()for link in links:# 检查是否应该爬取if not self._should_crawl(link, new_depth):continue# 插入新URL或更新现有URLtry:cursor.execute("""INSERT INTO urls (url, depth, status) VALUES (?, ?, ?)ON CONFLICT(url) DO UPDATE SET depth = ?, status = ?""",(link, new_depth, 'pending', new_depth, 'pending'))new_urls.append(link)except sqlite3.Error as e:logger.error(f"Error adding URL {link}: {str(e)}")conn.commit()# 添加新URL到队列for link in new_urls:self.queue.put((link, new_depth))# 更新统计with self.lock:self.stats['total_crawled'] += 1self.stats['total_links_found'] += len(links)self.stats['last_crawl_time'] = time.time()logger.info(f"Crawled: {url} | Depth: {depth} | Links found: {len(links)} | New URLs: {len(new_urls)}")def process_content(self, url, content):"""处理页面内容的方法 - 用户应重写此方法以实现具体业务逻辑参数:url: 当前页面的URLcontent: 页面内容(字节)"""# 示例: 保存HTML文件domain = self._get_domain(url)path = urlparse(url).pathfilename = re.sub(r'[^\w\-_\.]', '_', path) or "index.html"# 创建域目录os.makedirs(f"crawled_pages/{domain}", exist_ok=True)# 保存文件with open(f"crawled_pages/{domain}/{filename}", "wb") as f:f.write(content)logger.debug(f"Saved content for {url}")def _worker(self):"""爬虫工作线程"""while True:url, depth = self.queue.get()# 检查是否应该爬取if not self._should_crawl(url, depth):self.queue.task_done()continuedomain = self._get_domain(url)self._respect_politeness(domain)# 获取URL内容content, status_code = self._fetch_url(url)# 处理响应if content:self._process_page(url, depth, content, status_code)else:# 标记为失败with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("UPDATE urls SET status = ? WHERE url = ?",('failed', url))conn.commit()logger.warning(f"Failed to crawl {url}")self.queue.task_done()def start_crawling(self, resume=False):"""启动爬虫参数:resume: 是否从上次中断处继续"""logger.info("Starting incremental crawler")if resume:# 恢复未完成的URLwith sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()cursor.execute("SELECT url, depth FROM urls WHERE status IN ('pending', 'failed')")pending_urls = cursor.fetchall()for url, depth in pending_urls:self.queue.put((url, depth))logger.info(f"Resuming pending URL: {url} at depth {depth}")# 启动工作线程for i in range(self.max_threads):t = Thread(target=self._worker, daemon=True)t.start()# 等待队列完成self.queue.join()# 计算总时间total_time = time.time() - self.stats['start_time']logger.info(f"Crawling completed! Total URLs crawled: {self.stats['total_crawled']}")logger.info(f"Total links found: {self.stats['total_links_found']}")logger.info(f"Total time: {total_time:.2f} seconds")def print_stats(self):"""打印爬虫统计信息"""with sqlite3.connect(self.db_path) as conn:cursor = conn.cursor()# 获取URL统计cursor.execute("SELECT status, COUNT(*) FROM urls GROUP BY status")status_counts = cursor.fetchall()# 获取最常爬取的域名cursor.execute('''SELECT domain, COUNT(*) as count FROM (SELECT CASE WHEN INSTR(url, '://') > 0 THEN SUBSTR(url, INSTR(url, '://') + 3, INSTR(SUBSTR(url, INSTR(url, '://') + 3), '/') - 1) END as domain FROM urls) WHERE domain IS NOT NULL GROUP BY domain ORDER BY count DESC LIMIT 5''')top_domains = cursor.fetchall()print("\n===== Crawler Statistics =====")print(f"Total URLs crawled: {self.stats['total_crawled']}")print(f"Total links found: {self.stats['total_links_found']}")print("\nURL Status Summary:")for status, count in status_counts:print(f"  {status}: {count}")print("\nTop Domains:")for domain, count in top_domains:print(f"  {domain}: {count} URLs")if self.stats['last_crawl_time'] > 0:last_crawl = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.stats['last_crawl_time']))print(f"\nLast crawl time: {last_crawl}")# 使用示例
if __name__ == "__main__":# 创建爬虫实例crawler = IncrementalCrawler(max_threads=3,max_depth=2,politeness_delay=2.0)# 添加种子URLseed_urls = ["https://example.com","https://www.wikipedia.org","https://github.com"]crawler.add_seed_urls(seed_urls)# 启动爬虫 (resume=True 可以从上次中断处继续)try:crawler.start_crawling(resume=False)except KeyboardInterrupt:logger.info("Crawler interrupted by user")# 打印统计信息crawler.print_stats()

增量式爬虫核心功能

  1. URL去重与状态管理:

    • 使用SQLite数据库存储所有URL及其状态
    • 基于URL和内容哈希进行去重
    • 记录最后爬取时间,避免重复爬取
  2. 增量更新机制:

    • 仅爬取新URL或24小时内未更新的URL
    • 内容哈希比对,避免处理相同内容
  3. 礼貌爬取策略:

    • 域名级别的请求延迟控制
    • 可配置的延迟时间
    • 避免对同一域名请求过快
  4. 断点续爬功能:

    • 记录爬取状态到数据库
    • 支持从"pending"或"failed"状态恢复爬取
  5. 多线程支持:

    • 可配置的线程数量
    • 线程安全的数据访问

使用说明

  1. 初始化爬虫:

    crawler = IncrementalCrawler(db_path='crawler.db',     # 数据库路径max_threads=5,            # 最大线程数max_depth=3,              # 最大爬取深度politeness_delay=1.0,     # 请求延迟(秒)user_agent="Custom Agent" # 自定义User-Agent
    )
    
  2. 添加种子URL:

    crawler.add_seed_urls(["https://example.com","https://www.example.org"
    ])
    
  3. 自定义内容处理:

    class MyCrawler(IncrementalCrawler):def process_content(self, url, content):# 实现自定义处理逻辑# 例如:解析内容、存储数据等pass
    
  4. 启动爬虫:

    # 首次爬取
    crawler.start_crawling(resume=False)# 断点续爬
    crawler.start_crawling(resume=True)
    
  5. 查看统计信息:

    crawler.print_stats()
    

数据库结构

urls 表

字段类型描述
idINTEGER主键ID
urlTEXTURL地址(唯一)
depthINTEGER爬取深度
statusTEXT状态(pending/completed/failed)
content_hashTEXT内容哈希值
last_crawledREAL最后爬取时间
created_atREAL创建时间

domain_delays 表

字段类型描述
domainTEXT域名(主键)
last_requestREAL最后请求时间

注意事项

  1. 遵守网站的robots.txt规则
  2. 根据目标网站调整爬取延迟(politeness_delay)
  3. 重写process_content方法实现具体业务逻辑
  4. 避免爬取敏感或受版权保护的内容
  5. 定期备份数据库文件

这个模版就是以前我做过的一个项目,主要提供了增量式爬虫的核心功能,具体情况可以根据需求进行扩展和优化。不管是小型爬虫还是大型增量爬虫都可以完美胜任,如果有问题可以留言讨论。

相关文章:

增量式网络爬虫通用模板

之前做过一个项目&#xff0c;他要求是只爬取新产生的或者已经更新的页面&#xff0c;避免重复爬取未变化的页面&#xff0c;从而节省资源和时间。这里我需要设计一个增量式网络爬虫的通用模板。可以继承该类并重写部分方法以实现特定的解析和数据处理逻辑。这样可以更好的节约…...

【JVM】三色标记法原理

在JVM中&#xff0c;三色标记法是GC过程中对象状态的判断依据&#xff0c;回收前给对象设置上不同的三种颜色&#xff0c;三色分为白色、灰色、黑色。根据颜色的不同&#xff0c;决定对象是否要被回收。 白色表示&#xff1a; 初始状态&#xff1a;所有对象未被 GC 访问。含义…...

【uniapp开发】picker组件的使用

项目uniapp&#xff0c;结合fastadmin后端开发 picker组件的官方文档说明 https://en.uniapp.dcloud.io/component/picker.html#普通选择器 先看效果&#xff1a; 1、实现设备类型的筛选&#xff1b;2、实现设备状态的筛选&#xff1b; 前端代码&#xff08;节选&#xff0…...

【HarmonyOS Next之旅】DevEco Studio使用指南(三十一) -> 同步云端代码至DevEco Studio工程

目录 1 -> 同步云函数/云对象 1.1 -> 同步单个云函数/云对象 1.2 -> 批量同步云函数/云对象 2 -> 同步云数据库 2.1 -> 同步单个对象类型 2.2 -> 批量同步对象类型 3 -> 一键同步云侧代码 1 -> 同步云函数/云对象 说明 对于使用DevEco Studio…...

如何评估大语言模型效果

评估大模型微调后的效果是一个系统化的过程&#xff0c;需要结合客观指标和主观评估&#xff0c;并根据任务类型&#xff08;分类、生成、回归等&#xff09;选择合适的评估方法。 一、评估前的准备工作 数据集划分&#xff1a; 将数据分为 训练集、验证集 和 测试集&#xff…...

go-zero微服务入门案例

一、go-zero微服务环境安装 1、go-zero脚手架的安装 go install github.com/zeromicro/go-zero/tools/goctllatest2、etcd的安装下载地址根据自己电脑操作系统下载对应的版本&#xff0c;具体的使用自己查阅文章 二、创建一个user-rpc服务 1、定义user.proto文件 syntax &qu…...

Python控制台输出彩色字体指南

在Python开发中&#xff0c;有时我们需要在控制台输出彩色文本以提高可读性或创建更友好的用户界面。本文将介绍如何使用colorama库来实现这一功能。 为什么需要彩色输出&#xff1f; 提高可读性&#xff1a;重要信息可以用不同颜色突出显示更好的用户体验&#xff1a;错误信息…...

零基础在实践中学习网络安全-皮卡丘靶场(第十六期-SSRF模块)

最后一期了&#xff0c;感谢大家一直以来的关注&#xff0c;如果您对本系列文章内容有问题或者有更好的方法&#xff0c;请在评论区发送。 介绍 其形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能,但又没有对目标地址做严格过滤与限制导致攻击者可以传入任意…...

开源之夏·西安电子科技大学站精彩回顾:OpenTiny开源技术下沉校园,点燃高校开发者技术热情

开源之夏2025编程活动正在如火如荼的进行中&#xff0c;当前也迎来了报名的倒计时阶段&#xff0c;开源之夏组织方也通过高校行系列活动进入各大高校&#xff0c;帮助高校开发者科普开源文化、开源活动、开源技术。 6月4日 开源之夏携手多位开源技术大咖、经验型选手走进西安电…...

html、css(javaweb第一天)

HTML: 文字、图片、视频组成 由标签组成的语言 行内标签span//无语意 <img src"url">//图片 <a herf"url" target"是否开新页面">点击谁</a>//超链接 <video src"url" controls></video>//controls播放…...

解决数据库重启问题

最近部署软件时&#xff0c;发现mysql会一直在重启&#xff0c;记录下解决办法&#xff1a; 1.删除/home/dataexa/install/docker/datas/mysql路径下的data文件夹 2.重新构建mysql docker-compose up -d --build mysql 3.停掉所有应用&#xff0c;在全部重启&#xff1a; do…...

前后端交互过程中—各类文件/图片的上传、下载、显示转换

前后端交互过程中—各类文件/图片的上传、下载、显示转换 图片补充&#xff1a;new Blob()URL.createObjectURL()替代方案&#xff1a;FileReader.readAsDataURL()​​对比&#xff1a; tiff文件TIFF库TIFF转换通过url转换tiff文件为png通过文件选择的方式转换tiff文件为png 下…...

SparkSQL 优化实操

一、基础优化配置 1. 资源配置优化 # 提交Spark作业时的资源配置示例 spark-submit \--master yarn \--executor-memory 8G \--executor-cores 4 \--num-executors 10 \--conf spark.sql.shuffle.partitions200 \your_spark_app.py 参数说明&#xff1a; executor-memory: 每…...

【vLLM 学习】Cpu Offload Lmcache

vLLM 是一款专为大语言模型推理加速而设计的框架&#xff0c;实现了 KV 缓存内存几乎零浪费&#xff0c;解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ *在线运行 vLLM 入门教程&#xff1a;零基础分步指南 源码 examples/offline_inf…...

数据库同步是什么意思?数据库架构有哪些?

目录 一、数据库同步是什么 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;数据库同步的类型 &#xff08;三&#xff09;数据库同步的实现方式 二、数据库架构的类型 &#xff08;一&#xff09;单机架构 &#xff08;二&#xff09;主从复制架构 &a…...

【数据结构】详解算法复杂度:时间复杂度和空间复杂度

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平 前言&…...

Rest-Assured API 测试:基于 Java 和 TestNG 的接口自动化测试

1. 右键点击项目的文件夹&#xff0c;选择 New > File。 2. 输入文件名&#xff0c;例如 notes.md&#xff0c;然后点击 OK。 3. 选择项目类型 在左侧的 Generators 部分&#xff0c;选择 Maven Archetype&#xff0c;这将为你生成一个基于 Maven 的项目。 4. 配置项目基…...

多模型协同:基于 SAM 分割 + YOLO 检测 + ResNet 分类的工业开关状态实时监控方案

一、技术优势与适配性分析 1. 任务分工的合理性 YOLO&#xff08;目标检测&#xff09; 核心价值&#xff1a;快速定位工业开关在图像中的位置&#xff08;边界框&#xff09;&#xff0c;为后续分割和分类提供ROI&#xff08;感兴趣区域&#xff09;。工业场景适配性&#xf…...

【分销系统商城】

分销商城系统是一种结合电商与社交裂变的多层级分销管理平台&#xff0c;通过佣金激励用户成为分销商&#xff0c;实现低成本快速拓客和销量增长。以下是其核心要点解析&#xff1a; &#x1f6cd;️ ​​一、系统定义与核心价值​​ ​​基本概念​​ ​​核心模式​​&#…...

LangChainGo入门指南:Go语言实现与OpenAI/Qwen模型集成实战

目录 1、什么是langchainGo2、langchainGo的官方地址3、LangChainGo with OpenAI3-1、前置准备3-2、安装依赖库3-3、新建模型客户端3-4、使用模型进行对话 4、总结 1、什么是langchainGo langchaingo是langchain的go语言实现版本 2、langchainGo的官方地址 官网&#xff1a;…...

5.1 HarmonyOS NEXT系统级性能调优:内核调度、I/O优化与多线程管理实战

HarmonyOS NEXT系统级性能调优&#xff1a;内核调度、I/O优化与多线程管理实战 在HarmonyOS NEXT的全场景生态中&#xff0c;系统级性能调优是构建流畅、高效应用的关键。通过内核调度精细化控制、存储与网络I/O深度优化&#xff0c;以及多线程资源智能管理&#xff0c;开发者…...

react public/index.html文件使用env里面的变量

env文件 ENVdevelopment NODE_ENVdevelopment REACT_APP_URL#{REACT_APP_URL}# REACT_APP_CLIENTID#{REACT_APP_CLIENTID}# REACT_APP_TENANTID#{REACT_APP_TENANTID}# REACT_APP_REDIRECTURL#{REACT_APP_REDIRECTURL}# REACT_APP_DOMAIN_SCRIPT#{REACT_APP_DOMAIN_SCRIPT}#pu…...

chili3d 笔记17 c++ 编译hlr 带隐藏线工程图

这个要注册不然emscripten编译不起来 --------------- 行不通 ---------------- 结构体 using LineSegment std::pair<gp_Pnt, gp_Pnt>;using LineSegmentList std::vector<LineSegment>; EMSCRIPTEN_BINDINGS(Shape_Projection) {value_object<LineSegment&g…...

创建一个纯直线组成的字体库

纯直线组成的字体&#xff0c;一个“却”由五组坐标点组成&#xff0c;存储5个点共占21字节&#xff0c;使用简单&#xff0c;只要画直线即可&#xff0c; “微软雅黑”&#xff0c;2个轮廓&#xff0c;55坐标点&#xff0c;使用复杂&#xff0c;还填充。 自创直线字体 “微软…...

接口不是json的内容能用Jsonpath获取吗,如果不能,我们选用什么方法处理呢?

JsonPath 是一种专门用于查询和提取 JSON 数据的查询语言&#xff08;类似 XPath 用于 XML&#xff09;。以下是详细解答&#xff1a; ​JsonPath 的应用场景​ ​API 响应处理​&#xff1a;从 REST API 返回的 JSON 数据中提取特定字段。​配置文件解析​&#xff1a;读取 J…...

使用 Docker Compose 从零部署 TeamCity + PostgreSQL(详细新手教程)

JetBrains TeamCity 是一款专业的持续集成&#xff08;CI&#xff09;服务器工具&#xff0c;支持各种编程语言和构建流程。本文将一步一步带你用 Docker 和 Docker Compose 快速部署 TeamCity&#xff0c;搭配 PostgreSQL 数据库&#xff0c;并确保 所有操作新手可跟着做。 一…...

Go 语言实现高性能 EventBus 事件总线系统(含网络通信、微服务、并发异步实战)

前言 在现代微服务与事件驱动架构&#xff08;EDA&#xff09;中&#xff0c;事件总线&#xff08;EventBus&#xff09; 是实现模块解耦与系统异步处理的关键机制。 本文将以 Go 语言为基础&#xff0c;从零构建一个高性能、可扩展的事件总线系统&#xff0c;深入讲解&#…...

Linux进程(中)

目录 进程等待 为什么有进程等待 什么是进程等待 怎么做到进程等待 wait waitpid 进程等待 为什么有进程等待 僵尸进程无法杀死&#xff0c;需要进程等待来消灭他&#xff0c;进而解决内存泄漏问题--必须解决的 我们要通过进程等待&#xff0c;获得子进程退出情况--知…...

【计算机组成原理】计算机硬件的基本组成、详细结构、工作原理

引言 计算机如同现代科技的“大脑”&#xff0c;其硬件结构的设计逻辑承载着信息处理的核心奥秘。从早期程序员手动输入指令的低效操作&#xff0c;到冯诺依曼提出“存储程序”概念引发的革命性突破&#xff0c;计算机硬件经历了从机械操控到自动化逻辑的蜕变。本文将深入拆解…...

npm error Cannot read properties of null (reading ‘matches‘)

当在使用 npm 命令时遇到 Cannot read properties of null (reading matches) 错误&#xff0c;这通常表示代码尝试访问一个 null 对象的 matches 属性。以下是综合多个来源的解决策略&#xff0c;按优先级排列&#xff1a; 一、核心解决方法 1. 清理缓存与重新安装依赖&…...