尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python构建漏洞情报采集流水线:从数据抓取到资产匹配的实战指南
1. 为什么我要自己动手搭一套漏洞情报采集流水线做安全运营的同行大概都有类似的体会每天早上打开邮箱各种漏洞通告、安全公告、厂商补丁说明铺天盖地光是筛选出跟自己资产相关的那几条就得花掉一两个小时。更麻烦的是很多漏洞信息分散在不同页面、不同格式里有的用表格有的用列表有的干脆就是一大段纯文本想做个统计或者跟内部资产库做匹配手工整理几乎不可能。我所在的团队负责维护一批线上业务系统资产规模不算特别大但涉及的组件种类很杂从常见的Web中间件到各种开源依赖库都有。之前一直靠人工订阅邮件列表加手工记录的方式做漏洞跟踪效率低不说还经常漏掉一些关键信息。后来我下定决心花了两周时间搭了一套自动化的漏洞情报采集与处理流水线核心目标就一个把公开渠道发布的漏洞数据自动抓下来、清洗干净、结构化存储然后跟内部资产清单做匹配输出一份每天可以直接看的风险清单。这套东西的技术栈其实不复杂Python加上几个常用的解析库就能跑起来。但真正做下来才发现坑比想象中多得多。数据源的页面结构会变、反爬策略会升级、字段格式不统一、去重逻辑要考虑版本号比较……每一个环节都有细节需要打磨。这篇文章就把我整个搭建过程中积累的经验、踩过的坑、以及最终跑通的方案完整分享出来适合有一定Python基础、想做安全数据自动化采集的朋友参考。哪怕你之前没接触过爬虫跟着思路走也能理解整套逻辑。2. 整体架构设计与技术选型思路2.1 为什么选择分层架构而不是一把梭最开始我想得特别简单写个脚本请求页面、解析HTML、存数据库完事。但实际跑起来发现如果把请求、解析、存储全揉在一个函数里后面任何一个环节出问题都得从头调试而且数据源一多代码会变得极其臃肿。所以我最终采用的是分层架构把整个流程拆成四个独立的层采集层负责发送HTTP请求、处理重试和限速只关心“把页面内容拿回来”解析层负责从HTML或JSON中提取结构化字段只关心“把内容变成字典”处理层负责数据清洗、格式统一、去重和版本比较只关心“把字典变成规范记录”存储与输出层负责写入数据库、生成报表、推送通知只关心“把记录送到该去的地方”这样拆的好处非常明显。比如某天某个数据源的页面改版了我只需要改解析层对应的那个解析器采集层和处理层完全不用动。再比如后面想加一个新的输出渠道也只需要在输出层扩展不影响前面的逻辑。提示分层不是目的解耦才是。如果你的数据源只有一个且结构极其稳定硬套分层反而增加复杂度。但只要数据源超过两个或者页面结构可能变化分层就是值得的。2.2 技术栈选择与理由在具体工具的选择上我对比过几种方案最终确定的组合是这样的环节选型选择理由HTTP请求requests 重试装饰器简单直接配合urllib3的Retry机制足够应对大多数场景HTML解析BeautifulSoup lxml容错性强即使页面结构不规整也能提取到内容JSON解析内置json库标准库够用不需要额外依赖数据清洗pandas批量处理字段、做版本比较时非常方便存储SQLite本地/ PostgreSQL团队轻量起步后续可平滑迁移调度schedule库 系统定时任务比crontab更直观适合Python项目内管理通知企业微信机器人Webhook团队日常用的工具接入成本低这里重点说一下为什么用BeautifulSoup而不是Scrapy。Scrapy确实强大自带调度、去重、中间件但它更适合大规模、多站点的爬取任务。我的场景是数据源数量有限大概十几个每个源的页面结构差异大需要针对性地写解析逻辑用Scrapy反而显得笨重。requests加BeautifulSoup的组合更灵活调试也方便出问题了直接print就能定位。至于存储一开始我用的是SQLite单文件、零配置本地跑完全没问题。后来团队其他人也要用这份数据就迁移到了PostgreSQL改的只是连接字符串和少量SQL语法整体迁移成本很低。2.3 数据源分类与采集策略公开的漏洞信息来源大致可以分成几类每类的采集策略不太一样第一类是结构化的漏洞数据库这类源通常有固定的字段和分页机制页面结构相对稳定解析起来最省心。采集频率可以设为每天一次因为更新不会特别频繁。第二类是厂商发布的安全公告页面这类页面的特点是格式不统一有的用表格有的用嵌套的div而且不同厂商的字段命名差异很大。采集频率建议每天两次因为厂商发布补丁往往比较紧急。第三类是社区讨论和邮件列表归档这类源信息量大但噪音也大需要做更多的过滤和关键词匹配。采集频率可以放宽到每两天一次重点是把标题和摘要抓下来做初步筛选。针对不同类型的源我在采集层设置了不同的请求头、超时时间和重试策略。比如结构化数据库的源响应快超时设10秒就够厂商公告页面有时候加载慢超时设到30秒重试次数也多加一次。3. 核心模块拆解与关键实现细节3.1 采集层请求封装与反爬应对采集层看起来简单但细节最多。我封装了一个通用的请求函数核心逻辑包括设置合理的请求头、控制请求间隔、处理超时和重试、记录请求日志。请求头这块最关键的是User-Agent和Accept-Language。很多站点会检查User-Agent来判断是不是正常浏览器访问用一个常见的浏览器UA字符串能避免大部分基础拦截。Accept-Language设成中文环境有些站点会根据这个返回不同的内容版本。请求间隔的控制非常重要。我一开始没注意连续快速请求同一个站点结果IP被临时封了半小时。后来加了一个简单的令牌桶限流对同一个域名的请求间隔至少1.5秒不同域名之间可以并行。实测下来这个间隔既能保证采集效率又不会触发大部分站点的频率限制。重试策略我用的是指数退避第一次失败等2秒重试第二次等4秒第三次等8秒最多重试三次。这样设计的原因是很多临时性失败比如网络抖动、服务端短暂过载在几秒后就能恢复立即重试反而容易连续失败。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry import time def create_session(): session requests.Session() retry Retry( total3, backoff_factor2, status_forcelist[500, 502, 503, 504] ) adapter HTTPAdapter(max_retriesretry) session.mount(http://, adapter) session.mount(https://, adapter) session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept-Language: zh-CN,zh;q0.9,en;q0.8 }) return session _last_request_time {} def rate_limited_get(session, url, min_interval1.5): from urllib.parse import urlparse domain urlparse(url).netloc now time.time() if domain in _last_request_time: elapsed now - _last_request_time[domain] if elapsed min_interval: time.sleep(min_interval - elapsed) resp session.get(url, timeout15) _last_request_time[domain] time.time() return resp这段代码里Retry的backoff_factor2意味着重试间隔会按2的幂次增长配合status_forcelist只对服务端错误重试客户端错误如404不重试避免无意义的等待。注意限流的时间间隔不要设得太死。有些站点在夜间维护时段响应特别慢如果间隔设得太短容易触发超时。我后来把间隔改成了可配置的不同源用不同的值灵活很多。3.2 解析层多格式内容提取与字段映射解析层是整个流水线里最需要耐心的地方因为每个数据源的页面结构都不一样。我的做法是给每个数据源写一个独立的解析器类统一继承一个基类基类定义接口子类实现具体逻辑。基类定义了两个核心方法parse_list负责从列表页提取出详情页的链接parse_detail负责从详情页提取出结构化字段。这样设计的好处是列表页和详情页的解析逻辑分开互不干扰。字段映射是另一个关键点。不同源对同一个概念的叫法完全不同比如漏洞编号有的叫“CVE ID”有的叫“漏洞编号”有的直接就是“ID”。我在解析层统一映射成标准字段名cve_id、title、severity、publish_date、description、affected_products、reference_links。class BaseParser: def parse_list(self, html): raise NotImplementedError def parse_detail(self, html): raise NotImplementedError def normalize_severity(self, raw_text): mapping { critical: 严重, 高危: 高危, high: 高危, medium: 中危, 中危: 中危, low: 低危, 低危: 低危 } raw_lower raw_text.strip().lower() for key, val in mapping.items(): if key in raw_lower: return val return 未知严重程度的归一化特别重要。有的源用英文Critical/High/Medium/Low有的用中文严重/高危/中危/低危还有的用数字评分9.8、7.5。我统一映射成四个等级方便后续做优先级排序。日期格式也是个大坑。有的源用“2024-01-15”有的用“Jan 15, 2024”还有的用“2024年1月15日”。我写了一个通用的日期解析函数用dateutil库做兜底能覆盖绝大多数格式。3.3 处理层数据清洗、去重与版本比较处理层要做的事情比想象中多。原始数据抓下来之后至少需要经过这几道处理第一道是空值处理。有些字段在某些源里可能不存在比如有的公告不写受影响产品版本。对于这种情况我统一填“未提供”而不是留空。留空在后续做统计和匹配时容易出问题。第二道是文本清洗。HTML标签残留、多余的空格和换行、特殊字符编码问题这些都要处理。我写了一个clean_text函数用正则表达式去掉HTML标签把连续空白字符合并成一个空格再统一编码格式。第三道是去重。去重不能简单地按CVE编号来因为同一个CVE可能在不同源里出现多次而且描述信息可能略有差异。我的策略是优先按CVE编号去重如果CVE编号缺失则按“标题受影响产品”的组合去重。去重时保留信息最完整的那条记录。第四道是版本号比较。这个是最容易出错的环节。漏洞影响范围通常用版本号区间表示比如“影响 2.0.0 到 2.3.5 之间的版本”。要判断某个具体版本是否在影响范围内就需要做版本号比较。版本号比较不能直接按字符串比因为“2.10.0”在字符串比较里小于“2.9.0”但实际版本号是大于的。def compare_version(v1, v2): def normalize(v): parts v.split(.) result [] for p in parts: num .join(filter(str.isdigit, p)) result.append(int(num) if num else 0) return result n1, n2 normalize(v1), normalize(v2) max_len max(len(n1), len(n2)) n1 [0] * (max_len - len(n1)) n2 [0] * (max_len - len(n2)) for a, b in zip(n1, n2): if a b: return 1 if a b: return -1 return 0这个函数把版本号拆成数字列表逐位比较能正确处理“2.10.0 2.9.0”这种情况。对于带字母的版本号如“1.0.0-beta”我做了简化处理只取数字部分因为大多数漏洞影响范围描述不会精确到beta版本。3.4 存储层表结构设计与索引优化存储层的表结构设计直接影响后续查询效率。我最终确定的表结构包含以下字段字段名类型说明id自增主键内部记录IDcve_id字符串漏洞编号唯一索引title字符串漏洞标题severity字符串严重等级publish_date日期发布日期description文本漏洞描述affected_products文本受影响产品JSON格式存储reference_links文本参考链接JSON格式存储source字符串数据来源标识created_at时间戳记录创建时间updated_at时间戳记录更新时间索引方面cve_id建唯一索引publish_date和severity建普通索引。这样设计的原因是日常查询最常用的条件就是“最近一周的高危漏洞”publish_date和severity的组合索引能大幅提升这类查询的速度。受影响产品和参考链接用JSON格式存储是因为这两个字段的内容长度不固定而且后续可能需要做更复杂的查询。如果用的是PostgreSQL可以直接用JSONB类型查询效率更高。SQLite的话就用TEXT存JSON字符串在应用层做解析。提示如果数据量不大几万条以内SQLite完全够用而且备份和迁移特别方便直接复制文件就行。数据量上到十万级以上或者需要多人同时写入再考虑迁移到PostgreSQL。4. 完整实操流程与关键环节实现4.1 环境准备与依赖安装整个项目用Python 3.9以上版本就能跑依赖库不多用pip一次性装好pip install requests beautifulsoup4 lxml pandas python-dateutil schedule如果要用PostgreSQL再加一个psycopg2-binary。我建议用虚拟环境来管理依赖避免跟系统里的其他Python项目冲突python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows pip install -r requirements.txt项目目录结构我习惯这样组织vuln_collector/ ├── config/ │ └── sources.yaml # 数据源配置 ├── collectors/ │ ├── base.py # 采集基类 │ └── http_client.py # 请求封装 ├── parsers/ │ ├── base.py # 解析基类 │ ├── source_a.py # 各源解析器 │ └── source_b.py ├── processors/ │ ├── cleaner.py # 清洗逻辑 │ └── dedup.py # 去重逻辑 ├── storage/ │ └── db.py # 数据库操作 ├── output/ │ └── reporter.py # 报表生成 ├── main.py # 入口 └── requirements.txt这个结构的好处是每个模块职责清晰新人接手时能快速定位到相关代码。配置文件用YAML格式把数据源的URL、解析器类名、采集频率都放在配置里加新源的时候只需要改配置和加一个解析器文件不用动主流程。4.2 数据源配置与解析器编写配置文件的结构大概长这样sources: - name: source_a base_url: https://example-a.com/vuln/list parser: SourceAParser interval_hours: 24 max_pages: 10 - name: source_b base_url: https://example-b.com/security parser: SourceBParser interval_hours: 12 max_pages: 5每个源配置了名称、基础URL、对应的解析器类名、采集间隔和最大翻页数。最大翻页数是为了防止某个源突然出现大量历史数据导致采集时间过长设一个上限只抓最新的几页。解析器的编写我以其中一个源为例说明。假设这个源的列表页是一个表格每行包含漏洞标题、严重等级和详情链接from bs4 import BeautifulSoup from parsers.base import BaseParser class SourceAParser(BaseParser): def parse_list(self, html): soup BeautifulSoup(html, lxml) items [] table soup.find(table, class_vuln-table) if not table: return items for row in table.find_all(tr)[1:]: cols row.find_all(td) if len(cols) 3: continue link cols[0].find(a) if not link: continue items.append({ title: link.get_text(stripTrue), severity: self.normalize_severity(cols[1].get_text(stripTrue)), detail_url: link.get(href) }) return items def parse_detail(self, html): soup BeautifulSoup(html, lxml) result {} result[cve_id] self._extract_text(soup, span, cve-id) result[description] self._extract_text(soup, div, vuln-desc) result[publish_date] self._parse_date( self._extract_text(soup, span, publish-date) ) result[affected_products] self._extract_products(soup) result[reference_links] self._extract_links(soup) return result这里有几个细节值得注意。parse_list里对表格行的处理加了长度检查防止某些异常行导致索引错误。parse_detail里每个字段的提取都做了容错提取不到就返回空字符串而不是抛异常。这样即使页面部分改版也不会导致整个采集任务失败。4.3 去重与版本匹配的实操细节去重逻辑我单独放在一个模块里核心思路是先按CVE编号查重如果已存在则比较更新时间保留更新的那条如果CVE编号为空则按标题的哈希值查重。import hashlib def generate_fingerprint(record): if record.get(cve_id): return fcve:{record[cve_id]} title record.get(title, ).strip().lower() products record.get(affected_products, ) raw f{title}|{products} return fhash:{hashlib.md5(raw.encode()).hexdigest()}版本匹配是另一个核心功能。内部资产清单里记录了每个组件当前使用的版本号我需要判断这个版本是否在漏洞影响范围内。影响范围通常是一个区间我用两个字段表示affected_min和affected_max分别是最小受影响版本和最大受影响版本。def is_version_affected(current_version, min_version, max_version): if min_version and compare_version(current_version, min_version) 0: return False if max_version and compare_version(current_version, max_version) 0: return False return True实际使用中有些漏洞的影响范围不是连续的比如“影响 1.x 所有版本和 2.0 到 2.3”。这种情况我会把影响范围拆成多条记录每条记录对应一个连续区间匹配时只要命中任意一条就算受影响。注意版本号比较一定要处理“没有数字”的情况。我遇到过某个组件的版本号是“latest”这种没法比较只能标记为“需人工确认”。不要试图用字符串比较来蒙混过关否则会漏报或误报。4.4 定时调度与增量采集实现调度我用的是schedule库配合一个while循环每60秒检查一次是否有任务需要执行。这种方式的优点是配置简单所有调度逻辑都在Python代码里不需要额外维护crontab。import schedule import time def job_source_a(): collector Collector(source_a) collector.run() schedule.every(24).hours.do(job_source_a) schedule.every(12).hours.do(job_source_b) while True: schedule.run_pending() time.sleep(60)增量采集的关键是记录每个源上次采集到的最新时间戳或最新ID。我在数据库里建了一张collector_state表记录每个源的最后采集时间和最后一条记录的标识。下次采集时只抓取比这个标识更新的内容。对于支持按时间筛选的源直接在请求参数里带上起始时间。对于不支持时间筛选的源就只能翻页抓取然后在处理层根据发布时间过滤掉已经处理过的记录。虽然后者效率低一些但兼容性更好。4.5 输出报表与通知推送采集和处理完成之后最终要输出一份人能看懂的报表。我生成的报表包含三部分新增漏洞列表、高危漏洞汇总、与内部资产匹配的风险清单。报表格式我用的是Markdown因为可以直接贴到内部文档系统里也方便通过Webhook推送到团队沟通工具。推送内容做了精简只包含最关键的信息漏洞编号、标题、严重等级、受影响组件、修复建议链接。def build_report(new_vulns, matched_risks): lines [## 每日漏洞情报汇总\n] lines.append(f新增漏洞{len(new_vulns)} 条\n) lines.append(f资产匹配风险{len(matched_risks)} 条\n) if matched_risks: lines.append(\n### 需优先处理\n) for risk in matched_risks: lines.append(f- [{risk[severity]}] {risk[cve_id]} f{risk[title]}影响组件{risk[component]}) return \n.join(lines)推送频率我设的是每天一次早上九点推送到团队群。如果遇到严重等级为“严重”的漏洞会额外触发一次即时推送确保团队能第一时间响应。5. 常见问题排查与避坑经验实录5.1 请求被拦截或返回异常内容这是最常见的问题。表现是请求返回403、429状态码或者返回的页面内容明显不对比如返回了一个验证页面。排查思路按这个顺序来先检查请求头是否完整特别是User-Agent和Referer再检查请求频率是否过高适当加大间隔然后检查是否有Cookie要求有些站点需要先访问首页获取Cookie再访问详情页。我遇到过一个比较隐蔽的情况某个站点对请求头里的字段顺序有要求虽然理论上HTTP头是无序的但那个站点的防护逻辑就是按字符串匹配来做的。后来我把请求头的顺序调整成跟浏览器一致问题就解决了。这种问题没有通用解法只能靠抓包对比来定位。提示如果某个源持续返回异常不要死磕。先把它标记为“暂停采集”把其他源跑通回头再单独处理。我一开始在一个源上卡了三天后来发现那个源本身就不稳定隔几天再试就正常了。5.2 解析结果字段缺失或错位页面改版是解析层最大的敌人。表现是某些字段突然提取不到了或者提取到的内容跟预期不符。我的应对策略是在解析器里加字段校验如果关键字段如CVE编号、标题提取失败记录一条警告日志并把原始HTML保存下来供后续分析。同时解析结果里加一个parse_quality字段标记这条记录的解析完整度。完整度低于阈值的记录会被单独挑出来人工检查。另一个常见问题是编码问题。有些页面声明是UTF-8实际内容是GBK导致中文乱码。我的处理方式是在请求返回后先用resp.apparent_encoding检测实际编码再用检测到的编码来解码内容而不是盲目相信响应头里的声明。5.3 去重逻辑误判或漏判去重误判的典型表现是同一条漏洞被当成两条不同记录存了进去或者两条不同的漏洞被误判为同一条。前者的原因通常是CVE编号格式不一致比如有的源写“CVE-2024-1234”有的写“cve-2024-1234”有的写“CVE-2024-1234 ”末尾有空格。解决方法是统一转成大写并去掉首尾空格再做比较。后者的原因通常是标题哈希碰撞或者标题过于相似。比如两个不同的漏洞标题都是“安全更新公告”只是发布日期不同。这种情况我加了一个辅助判断如果标题相同但发布日期相差超过7天就认为是两条不同记录。5.4 版本匹配的边界情况处理版本匹配最容易出问题的地方是边界值。比如影响范围写的是“小于 2.3.5”那2.3.5本身是否受影响不同源的表述习惯不一样有的包含边界有的不包含。我的处理方式是在解析影响范围时同时提取边界包含关系。如果原文写的是“小于等于 2.3.5”那2.3.5就受影响如果写的是“小于 2.3.5”那2.3.5就不受影响。对于表述模糊的比如只写“2.3.5及以下”我统一按包含边界处理宁可多报不可漏报。下面这张表整理了我遇到过的典型问题及对应的解决思路问题现象可能原因排查方向解决方案请求返回403请求头不完整或频率过高检查UA、Referer、请求间隔补全请求头加大间隔返回内容为验证页触发了站点防护检查Cookie和请求特征先访问首页获取Cookie字段提取为空页面结构改版对比新旧HTML结构更新解析器选择器中文乱码编码声明与实际不符检查响应内容编码用apparent_encoding检测去重误判CVE格式不一致检查CVE字段格式统一大写去空格后比较版本匹配错误边界包含关系不明确检查原文表述按“宁多勿漏”原则处理5.5 采集任务中断与恢复长时间运行的采集任务可能会因为网络中断、进程被杀等原因意外停止。我的做法是给每个源维护一个采集状态记录当前处理到第几页、最后一条记录的时间戳。任务重启时从上次中断的位置继续而不是从头开始。状态信息存在数据库里每次成功处理完一页就更新一次。这样即使中断最多只会丢失当前页的数据不会影响整体进度。另外我在主流程外面包了一层异常捕获任何未处理的异常都会被记录到日志文件同时触发一次告警通知。这样即使半夜任务挂了第二天早上也能第一时间知道。6. 我在实际运行中积累的几条硬核经验这套流水线跑了大半年中间经历过几次大的调整有些经验是踩了坑才总结出来的这里分享几条我觉得最有价值的。第一条不要追求一次抓全先跑通再优化。我一开始想把所有能想到的字段都抓下来结果解析器写得极其复杂调试了两周还没跑通。后来砍掉了一半非核心字段先把主流程跑起来后面再逐步补充。这个思路帮我节省了大量时间。第二条日志要详细但不要泛滥。每个请求都打日志会导致日志文件迅速膨胀但关键节点如解析失败、去重命中、版本匹配异常的日志一定要详细。我的做法是分级记录INFO级别只记录每轮采集的汇总信息WARNING和ERROR级别记录具体的问题详情。第三条数据质量比数据量重要。与其抓一万条格式混乱的数据不如抓一千条干净规范的数据。我在处理层花的时间比采集层和解析层加起来还多但这是值得的。干净的数据后续做任何分析都方便脏数据只会带来无尽的麻烦。第四条定期回顾和清理。有些数据源可能过一段时间就失效了或者内容质量下降。我每个月会检查一次各源的采集成功率和数据质量把长期表现不好的源下线把精力集中在高质量源上。第五条留好手动干预的入口。自动化不是万能的总有一些情况需要人工判断。我在流程里保留了手动导入和手动修正的功能遇到解析不了的页面或者需要人工确认的版本匹配可以快速介入处理不用等代码修复。这套东西说到底就是一个思路把重复性的信息收集工作交给程序把人的精力留给真正需要判断和分析的事情。工具本身不复杂难的是持续维护和迭代。希望这些经验对正在做类似事情的朋友有所帮助。
RELATED

相关推荐

用Excel搭建动态数据分析表:数据层-逻辑层-呈现层解耦实践

用Excel搭建动态数据分析表:数据层-逻辑层-呈现层解耦实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/24 6:15:59
PCA9422与PIC18F86J55嵌入式电源管理方案设计与实战

PCA9422与PIC18F86J55嵌入式电源管理方案设计与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/24 6:10:59
俄语字母Unicode映射与编码转换实战指南

俄语字母Unicode映射与编码转换实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/24 6:10:59
MORE NEWS

更多资讯

📰

PaddleOCRv5离线识别全场景多语种验证码:TaoToken统一Key接入与onnxruntime本地推理验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

YOLOv5手势识别实战:从数据标注到模型部署的完整链路与TaoToken API接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

从简历词到真实力:技能构建、刻意练习与组合优势

去年在团队内部做技术评审的时候,我发现一个特别普遍的现象:很多人在聊“skills”这个词的时候,其实都是在聊简历上那一串工具名。你说你会某门语言,结果一追问底层机制,答不上来;你说你熟悉某个平台&#…

📰

技术人如何构建可迁移的技能体系:从问题排查到刻意练习的深度拆解

1. 从“skills”这个标题说起:一个被严重低估的硬核话题“skills”这个词,放在任何技术社区里都显得平平无奇。它不像“高并发”“分布式”“大模型”那样自带流量,也不像“从零手写”“万字长文”那样有标题党潜质。但恰恰是这个看起来最普通…

📰

SpringBoot社区老年康养系统开发与智能提醒实现

1. 项目概述:社区老年康养管理系统的核心价值这个基于SpringBoot的社区老年康养管理系统,是我在参与某社区智慧养老项目时的实战成果。系统最核心的创新点在于"个性化智能提醒"功能模块——它不仅仅是简单的健康档案管理,而是通过算…

📰

基于双口RAM的FPGA计数器设计:原理、实现与仿真验证

简介:这是一份FPGA课程设计报告,围绕“采用RAM实现计数器”展开,面向学习Verilog HDL、Quartus II与Modelsim的数字电路设计者。任务要求用108双口RAM完成10个8位计数器,初值1~10,主时钟50MHz经分频得到1MHz与1Hz计数时…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬