简介这是一份面向网络安全初学者与Python爬虫实践者的综合项目源码包围绕CNVD、CNNVD、CVE三大权威漏洞库的数据采集、存储与可视化展开适合想打通爬虫、数据库与GUI开发链路的学习者。包内共72个文件以py脚本、toc与spec打包配置、csv与xlsx数据样例、exe可执行程序及pyc缓存为主另有xml、txt、html等辅助文件压缩包约137.95MB目录涵盖爬虫模块、GUI主程序与打包产物。项目通过requests与解析库抓取漏洞名称、编号、描述及影响程度借助mysql-connector写入MySQL分表存储并用GUI实现增量或完整爬取与信息预览同时涉及延时、代理等可持续运行策略。已有1584人学习下载读者可据此理解漏洞数据从采集到落库再到界面展示的完整流程掌握多站点爬虫组织方式与打包发布思路并注意遵守数据来源的使用协议。1. 从一份漏洞库爬虫包说起它到底能帮你省下多少手工活如果你做过安全运维或者漏洞管理大概率经历过这种场景领导要一份近三个月的漏洞清单你打开浏览器挨个访问厂商公告页、CVE 列表、漏洞库站点一条条复制标题、编号、危害等级、发布时间再粘贴到 Excel 里。几十条还能忍几百条就是纯体力活而且手工整理必然漏、必然错、必然格式不统一。这份python爬取漏洞库.zip解决的就是这个环节——用 Python 把漏洞数据从目标站点批量抓下来结构化写入 MySQL后续查询、统计、导出都从数据库走不再依赖手工复制粘贴。它适合三类人一是刚接触爬虫、想找一个有真实数据结构的练手项目二是做安全运营、需要定期汇总漏洞情报的从业者三是想把爬虫和数据库串起来理解完整数据链路的开发者。核心关键词就是 python爬虫、mysql、漏洞库三者在这份资源里是一条完整链路不是三个孤立的知识点。下面我按实际拆包复现的顺序把这份资源讲透。2. 拆开压缩包先看结构爬虫、解析、入库三段式怎么分工拿到一个爬虫项目我习惯先看目录结构再跑代码因为结构决定了它的设计思路也决定了你改起来顺不顺手。这份资源整体是典型的三段式请求层负责拿页面解析层负责从 HTML 里抠字段存储层负责写 MySQL。三段分开的好处是目标站点改版时你只需要动解析层换数据库时只需要动存储层不会牵一发动全身。2.1 请求层requests 加会话复用与重试请求层一般用requests关键是别每次请求都新建连接。漏洞库站点通常有反爬频率一高就给你返回空页面或者直接封 IP。常见做法是用Session复用连接再配一个重试策略。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def build_session(): session requests.Session() # 重试策略失败 3 次退避因子 0.5针对 500/502/503/504 retry Retry( total3, backoff_factor0.5, status_forcelist[500, 502, 503, 504], ) adapter HTTPAdapter(max_retriesretry) session.mount(http://, adapter) session.mount(https://, adapter) # 伪装成普通浏览器很多站点不校验 UA 但校验缺失 session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 }) return session这段代码的逻辑是Session让 TCP 连接在多次请求间复用减少握手开销Retry让偶发的 5xx 错误自动重试避免因为一次网络抖动就丢数据。backoff_factor0.5表示第一次重试等 0.5 秒第二次等 1 秒依次递增给目标站点喘息时间也降低被封的概率。参数上total3是重试总次数别设太大否则一个坏链接会拖慢整批任务。2.2 解析层BeautifulSoup 与字段映射解析层负责把 HTML 变成结构化字典。漏洞库页面通常是一个列表页加若干详情页列表页给编号和链接详情页给危害等级、影响版本、修复建议。用BeautifulSoup配合 CSS 选择器是最稳的写法比正则可读性好比 XPath 在 Python 里更常见。from bs4 import BeautifulSoup def parse_list(html): soup BeautifulSoup(html, html.parser) items [] # 假设列表项在 table 的 tbody 里每行一条 for row in soup.select(table.vuln-list tbody tr): cols row.find_all(td) if len(cols) 4: continue items.append({ vuln_id: cols[0].get_text(stripTrue), # 漏洞编号 title: cols[1].get_text(stripTrue), # 标题 level: cols[2].get_text(stripTrue), # 危害等级 publish_date: cols[3].get_text(stripTrue), # 发布时间 detail_url: row.find(a)[href] if row.find(a) else , }) return items逻辑说明select用 CSS 选择器定位表格行find_all(td)拿到每列。get_text(stripTrue)去掉首尾空白和换行这是解析里最容易翻车的地方——不去空白入库后字段里全是\n和空格查询时对不上。len(cols) 4是防御性判断页面里可能有表头行或空行不跳过就会写入脏数据。detail_url做存在性判断因为不是每行都有链接。2.3 存储层MySQL 建表与批量写入存储层用 MySQL建表时字段类型要跟数据特征匹配。漏洞编号是字符串危害等级可以用枚举或字符串发布时间用DATE或DATETIME。批量写入比逐条INSERT快一个数量级用executemany一次提交。import pymysql def get_conn(): return pymysql.connect( host127.0.0.1, userroot, passwordyour_password, databasevuln_db, charsetutf8mb4, ) def save_batch(conn, rows): sql INSERT INTO vuln_info (vuln_id, title, level, publish_date, detail_url) VALUES (%s, %s, %s, %s, %s) ON DUPLICATE KEY UPDATE titleVALUES(title), levelVALUES(level), publish_dateVALUES(publish_date), detail_urlVALUES(detail_url) with conn.cursor() as cur: cur.executemany(sql, rows) conn.commit()ON DUPLICATE KEY UPDATE是这份资源里很关键的一处设计漏洞库是增量更新的同一个编号可能被修正用这个语法可以在重复时更新而不是报错或插入重复行。前提是vuln_id上建了唯一索引。charsetutf8mb4别省漏洞标题里偶尔有特殊字符用utf8会截断报错。executemany把多条拼成一次网络往返几百条数据几秒就写完。3. 把爬虫跑起来从建库到入库的完整操作链结构看懂了接下来是实际跑通。这一章按顺序走建库建表、配置连接、启动爬取、验证数据。每一步都有具体的命令和参数照着做就能复现。3.1 建库建表字段类型和索引怎么定先建数据库和表。字段类型的选择直接影响后续查询效率别全用VARCHAR(255)糊弄。CREATE DATABASE IF NOT EXISTS vuln_db DEFAULT CHARACTER SET utf8mb4; USE vuln_db; CREATE TABLE IF NOT EXISTS vuln_info ( id INT AUTO_INCREMENT PRIMARY KEY, vuln_id VARCHAR(64) NOT NULL COMMENT 漏洞编号, title VARCHAR(512) NOT NULL COMMENT 漏洞标题, level VARCHAR(16) DEFAULT NULL COMMENT 危害等级, publish_date DATE DEFAULT NULL COMMENT 发布时间, detail_url VARCHAR(1024) DEFAULT NULL COMMENT 详情链接, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_vuln_id (vuln_id), KEY idx_publish_date (publish_date), KEY idx_level (level) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明vuln_id建唯一索引uk_vuln_id这是ON DUPLICATE KEY UPDATE生效的前提。publish_date和level各建普通索引因为实际查询里最常用的过滤条件就是时间和危害等级。title给到 512漏洞标题偶尔很长256 可能不够。detail_url给 1024有些站点链接带一堆参数。created_at记录入库时间方便排查数据是哪一批写进来的。3.2 配置与启动把参数抽到配置文件硬编码数据库密码是新手常犯的错换环境就得改代码。常见做法是把配置抽到config.py或环境变量里。# config.py DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, password: your_password, database: vuln_db, charset: utf8mb4, } CRAWL_CONFIG { base_url: https://example-vuln-site.com/list, max_pages: 20, # 最多翻多少页 delay: 1.5, # 每次请求间隔秒数 timeout: 10, # 单次请求超时 }delay这个参数别调太小。我一般设 1.5 到 2 秒看起来慢但能稳定跑完不中断。设成 0.1 秒前几十条可能没事后面大概率被限流反而要重跑。max_pages是安全阀先跑 20 页验证流程没问题再放开。启动脚本把三段串起来import time from config import DB_CONFIG, CRAWL_CONFIG from crawler import build_session, parse_list from storage import get_conn, save_batch def main(): session build_session() conn get_conn() for page in range(1, CRAWL_CONFIG[max_pages] 1): url f{CRAWL_CONFIG[base_url]}?page{page} resp session.get(url, timeoutCRAWL_CONFIG[timeout]) if resp.status_code ! 200: print(fpage {page} status {resp.status_code}, skip) continue rows parse_list(resp.text) if not rows: print(fpage {page} empty, stop) break data [(r[vuln_id], r[title], r[level], r[publish_date], r[detail_url]) for r in rows] save_batch(conn, data) print(fpage {page} saved {len(data)} rows) time.sleep(CRAWL_CONFIG[delay]) conn.close() if __name__ __main__: main()逻辑说明循环翻页每页请求后判断状态码非 200 就跳过而不是中断保证单页失败不影响整体。parse_list返回空列表时说明翻到头了直接break。数据先转成元组列表再交给save_batch因为executemany接收的是序列。time.sleep放在每页末尾控制频率。3.3 验证入库三条 SQL 确认数据质量跑完之后别急着高兴先用 SQL 验证数据是不是真的对。-- 总数和去重数是否一致不一致说明有重复写入 SELECT COUNT(*) AS total, COUNT(DISTINCT vuln_id) AS uniq FROM vuln_info; -- 看危害等级的分布确认解析没把等级字段搞错位 SELECT level, COUNT(*) FROM vuln_info GROUP BY level ORDER BY COUNT(*) DESC; -- 抽查最近入库的几条人工核对标题和链接 SELECT vuln_id, title, publish_date FROM vuln_info ORDER BY created_at DESC LIMIT 10;第一条如果total大于uniq说明唯一索引没生效或者写入逻辑有问题。第二条看等级分布如果出现一堆空值或者明显不该出现的值多半是解析时列索引对错了。第三条抽查点开链接核对标题这是最直接的验证方式。三条都过了这份爬虫才算真正跑通。4. 避坑与排查那些让我重跑过三次的坑爬虫项目看起来简单实际跑起来坑不少。下面这几条是我在类似项目里真实踩过的每条按现象、原因、解决来说你对照排查能省不少时间。4.1 现象跑几十条后返回空页面或 403原因请求频率过高触发站点限流或者 UA 被识别。有些站点还会校验Referer和Cookie。解决把delay调到 2 秒以上给Session补上Referer头指向站点的首页。如果还不行检查是不是需要先访问列表页拿到 Cookie 再请求详情页。别用多线程硬刚漏洞库站点数据量不大单线程慢跑最稳。4.2 现象入库后标题字段带一堆换行和空格原因get_text()没加stripTrue或者 HTML 里有br标签被解析成换行。解决所有取文本的地方统一加stripTrue必要时用re.sub(r\s, , text)把连续空白压成一个空格。入库前做一次清洗比入库后再UPDATE省事。4.3 现象ON DUPLICATE KEY UPDATE没生效出现重复行原因vuln_id上没建唯一索引或者建表时字符集和连接字符集不一致导致唯一性判断失效。解决SHOW INDEX FROM vuln_info;确认uk_vuln_id存在。字符集统一用utf8mb4连接参数和建表语句都要写。如果表已经建好且没索引ALTER TABLE vuln_info ADD UNIQUE KEY uk_vuln_id (vuln_id);补上但补之前先清理已有重复数据。4.4 现象发布时间入库变成0000-00-00原因页面上的日期格式和 MySQL 的DATE类型不匹配比如2024/01/15或2024年1月15日直接写入会失败或被置零。解决入库前统一转成YYYY-MM-DD。用datetime.strptime按实际格式解析再strftime输出。格式不固定时先用正则把数字抠出来重组。import re from datetime import datetime def normalize_date(raw): nums re.findall(r\d, raw) if len(nums) 3: y, m, d nums[0], nums[1], nums[2] return f{int(y):04d}-{int(m):02d}-{int(d):02d} return None4.5 现象详情页字段抓不全部分记录只有编号原因详情页是异步加载的requests拿到的 HTML 里没有目标数据或者详情页需要登录。解决先确认详情页是不是服务端渲染。用浏览器禁用 JavaScript 再访问如果数据还在说明是服务端渲染检查选择器是不是写错了。如果数据消失说明是异步加载需要找接口或者换方案。登录才能看的页面这份资源不覆盖别硬爬。5. 进阶用法把爬虫变成可调度的增量采集器跑通单次采集只是起点实际用起来你需要它定期自动跑而且只抓新增的。这一章讲两个进阶点增量判断和定时调度最后给一个验证采集完整性的技巧。5.1 增量采集用最大日期做水位线每次全量重跑浪费资源也没必要。常见做法是记录上次采集到的最新发布时间下次只抓比它新的。def get_last_date(conn): with conn.cursor() as cur: cur.execute(SELECT MAX(publish_date) FROM vuln_info) row cur.fetchone() return row[0] if row and row[0] else None def should_stop(row_date, last_date): # 列表通常按时间倒序遇到比水位线旧的就停 if last_date and row_date and row_date str(last_date): return True return False逻辑说明get_last_date从库里取最大发布时间作为水位线。列表页一般按时间倒序排列翻页时逐条判断遇到比水位线旧的就停止翻页。这样每次只抓新增部分几十条的量几秒就跑完。注意水位线用DATE类型比较字符串比较在YYYY-MM-DD格式下是等价的但别混入带时间的格式。5.2 定时调度crontab 还是 APScheduler如果跑在 Linux 上crontab最简单不用改代码。每天凌晨跑一次# 每天 02:30 执行采集脚本日志追加到 crawl.log 30 2 * * * cd /path/to/project /usr/bin/python3 main.py crawl.log 21如果跑在 Windows 或者需要更灵活的控制比如失败重试、任务依赖用APScheduler在脚本里起一个调度器。两种方式都行看部署环境。我一般服务器上用crontab本地调试用APScheduler。5.3 验证采集完整性对比列表页总数和入库数增量采集最怕漏数据。一个简单的验证方法是采集前先抓列表页的总条数很多站点会显示「共 N 条」采集后查库里的总数两者对比。检查项预期结果不一致时的排查方向列表页显示总数与库内总数接近差得多说明翻页中断或解析漏行本次新增条数与水位线之后的新增数一致偏少说明停止条件触发过早空字段比例关键字段空值低于 5%偏高说明选择器失效或页面改版这个表我每次改完解析逻辑都会跑一遍。有一次站点把表格从table改成div布局列表页总数对得上但详情字段全空就是靠空字段比例这一行发现的。从那以后我每次调整解析规则都强制走一遍这三项对比确认没问题再让它自动跑。采集频率别贪高漏洞库更新没那么快一天一次足够。频率越高被封的风险越大维护成本也越高。把增量判断和调度配好这份爬虫就能从一次性脚本变成长期可用的数据管道。希望帮到你。本文还有配套的精品资源点击获取