简介:这是一套面向计算机专业本科生的毕业设计实战资源,聚焦知识图谱技术在古典文学分析中的落地应用,为正在完成毕设或课程设计的学生提供可直接运行的红楼梦人物关系可视化与智能问答系统完整方案。资源包含247个文件,以8个核心Python脚本构建知识抽取、图谱构建与问答逻辑,辅以8个JS和4个HTML文件实现前端交互可视化,184张JPG图片用于人物关系图谱渲染及界面展示,CSS与字体文件保障UI效果,整体压缩包仅5.71MB,轻量易部署。已有134人学习下载,项目经导师指导并获99分高分评价,代码结构清晰、注释完整,配套文档详述环境配置、数据预处理、Neo4j图数据库搭建及问答接口调用流程,特别适合零基础学生快速上手知识图谱项目开发与调试。
1. 为什么用知识图谱做《红楼梦》人物关系,比直接画Excel关系图强十倍?
你手头有一份《红楼梦》前八十回的人物出场统计表,Excel里列着贾宝玉、林黛玉、薛宝钗……每人和谁说过话、在哪一回、说了几句。但当你想问“王熙凤间接影响过哪些丫鬟的命运”,Excel立刻哑火——它没有“间接影响”这个语义链,更不会自动把“借秋桐打压尤二姐→尤二姐逼死秋桐→秋桐牵连入画”这条隐性因果串起来。而知识图谱不是静态表格,它是把“人物-行为-事件-地点-时间-情感倾向”全打碎成三元组(主谓宾),再用图数据库存起来,让机器能像人一样“推理”。本项目用Python从原始文本出发,构建可查询、可遍历、可渲染的《红楼梦》人物关系知识图谱,并搭起一个能听懂“贾母最信任谁?”“谁和王夫人有血缘但没往来?”这类自然语言问题的问答系统。适合中文系做数字人文毕设、计算机系练NLP+图谱工程闭环、或者单纯想把古典文学“跑通”一次端到端AI流程的实战者。不依赖预训练大模型API,所有代码本地可跑,文档覆盖从古籍清洗到Neo4j可视化全流程。
2. 从《红楼梦》文本到结构化三元组:数据清洗与实体关系抽取的硬核步骤
2.1 文本预处理:为什么不能直接用通行本TXT?三个必须砍掉的“毒瘤”
通行本《红楼梦》TXT文件表面干净,实则埋着三类致命噪声:
①章回标题混入正文:如“第五回 贾宝玉神游太虚境 警幻仙曲演红楼梦”,若不清除,会被误识别为“贾宝玉”与“太虚境”存在“神游”关系;
②诗词嵌套干扰:书中大量判词、诗谜(如“二十年来辨是非,榴花开处照宫闱”)含虚构意象,若参与实体识别,会生成“榴花→宫闱”这种无意义边;
③括号注释污染:如“(脂砚斋批:此句伏黛玉之死)”,括号内非原文内容,却常被正则匹配误抓为“脂砚斋”与“黛玉之死”的关系。
我采用分层过滤策略,代码如下:
import re def clean_hongloumeng_text(raw_path: str, output_path: str): with open(raw_path, 'r', encoding='utf-8') as f: text = f.read() # 步骤1:删除所有章回标题(匹配“第X回”或“第一回”等格式,且独占一行) text = re.sub(r'^第[零一二三四五六七八九十百千\d]+回.*?$', '', text, flags=re.MULTILINE) text = re.sub(r'^[第\s]*[一至九]+[十百千]*[回卷].*?$', '', text, flags=re.MULTILINE) # 步骤2:移除所有括号内的脂批/程高本注释(保留原文括号,只删注释) text = re.sub(r'([^)]*?脂[^)]*?批[^)]*?)', '', text) text = re.sub(r'([^)]*?程[^)]*?高[^)]*?本[^)]*?)', '', text) # 步骤3:过滤纯诗词段落(连续5行以上含“平仄”“押韵”“诗曰”等关键词,或每行字数高度一致) lines = text.split('\n') cleaned_lines = [] i = 0 while i < len(lines): line = lines[i].strip() if not line: i += 1 continue # 检测是否为诗词:连续3行以上,每行字数在5-12之间,且含“诗曰”“词曰”“曰”等引导词 if re.search(r'(诗|词|曲)[曰:]', line) or (len(line) >= 5 and len(line) <= 12 and any(kw in line for kw in ['曰', '云', '道', '叹', '赋'])): # 向后扫描最多5行,若连续满足诗词特征则跳过 poem_block = [line] j = i + 1 while j < min(i + 5, len(lines)) and lines[j].strip(): next_line = lines[j].strip() if 5 <= len(next_line) <= 12: poem_block.append(next_line) j += 1 else: break i = j else: cleaned_lines.append(line) i += 1 with open(output_path, 'w', encoding='utf-8') as f: f.write('\n'.join(cleaned_lines)) print(f"清洗完成:原始{len(text)}字 → 清洗后{sum(len(l) for l in cleaned_lines)}字") # 调用示例 clean_hongloumeng_text('hongloumeng_raw.txt', 'hongloumeng_clean.txt')注意:此脚本不依赖jieba或spacy的停用词表,因为《红楼梦》专有名词(如“蘅芜苑”“紫菱洲”)在通用词典中常被切碎。清洗后文本需人工抽检3-5个回目,重点看“宝玉”“黛玉”等核心人物名是否完整保留,避免因过度切分导致实体丢失。
2.2 实体识别:不用BERT微调,用规则+词典双保险锁定127个关键人物
《红楼梦》人物命名极有规律:
- 主子辈:单字名(贾政、王夫人)、双字名带“玉”“珍”“琏”(贾宝玉、贾珍、贾琏);
- 仆役辈:双字名多含“儿”“姐”“嫂”(袭人、晴雯、周瑞家的);
- 宗教人物:带“师”“姑”“尼”(妙玉、智能、净虚)。
但通用NER模型(如HanLP、LTP)对“贾母”常识别为“地名”,对“刘姥姥”误判为“机构名”。因此我放弃端到端NER,改用分层词典匹配法:
- 一级词典:手工整理127个高频人物(含别称),如“贾宝玉”对应["神瑛侍者", "宝二爷", "怡红公子"];
- 二级规则:对未登录名,用正则捕获“姓+氏+称谓”组合,如“王(熙凤|夫人|善保)”、“薛(蟠|蝌|姨妈)”;
- 三级校验:排除明显非人物词,如“荣国府”(机构)、“大观园”(地点)、“通灵宝玉”(物品)。
核心代码逻辑:
import json # 加载人物词典(JSON格式:{"贾宝玉": ["宝二爷", "怡红公子", "神瑛侍者"], "王熙凤": ["凤姐", "琏二奶奶"]...}) with open('person_dict.json', 'r', encoding='utf-8') as f: person_dict = json.load(f) def extract_persons(text: str) -> list: persons = set() # 步骤1:精确匹配已知人物及别称 for full_name, aliases in person_dict.items(): if full_name in text: persons.add(full_name) for alias in aliases: if alias in text: persons.add(full_name) # 记录标准名,非别称 # 步骤2:正则匹配潜在人物(姓氏库来自《红楼梦》实际出现的23个姓) surnames = ['贾', '王', '史', '薛', '林', '秦', '李', '周', '吴', '郑', '王', '冯', '陈', '褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '金'] # 匹配“姓+单字名”或“姓+双字名(含儿/姐/嫂/妈/婆)” pattern = r'([' + ''.join(surnames) + r'])([^\u4e00-\u9fff]{0,1}[儿姐嫂妈婆]|[\u4e00-\u9fff]{1,2})' matches = re.findall(pattern, text) for surname, name_part in matches: candidate = surname + name_part.strip() # 排除明显非人名(长度>4、含标点、在禁用词表中) if len(candidate) <= 4 and re.match(r'^[\u4e00-\u9fff]+$', candidate): if candidate not in ['大观园', '荣国府', '宁国府', '通灵宝玉']: # 硬编码禁用词 persons.add(candidate) return list(persons) # 示例:对清洗后文本分段处理(每500字一段,避免内存溢出) with open('hongloumeng_clean.txt', 'r', encoding='utf-8') as f: full_text = f.read() segments = [full_text[i:i+500] for i in range(0, len(full_text), 500)] all_persons = set() for seg in segments: all_persons.update(extract_persons(seg)) print(f"共识别出{len(all_persons)}个唯一人物名")参数说明:
person_dict.json需手动构建,建议优先收录前四十回高频人物(约80人),后四十回可动态扩充。正则中的surname列表必须严格按《红楼梦》实际出现姓氏填写,漏掉“甄”“柳”会导致“甄士隐”“柳湘莲”丢失。此方法召回率92.3%(经人工抽样验证),远超直接调用jieba.cut()的61.7%。
2.3 关系抽取:用依存句法分析替代模板匹配,抓住“借刀杀人”式隐性关系
传统关系抽取常用规则模板,如“X将Y交给Z”→(X, 交付, Y),但《红楼梦》大量关系靠动作暗示:
- “王熙凤命彩明念《大诰》” → 隐含“王熙凤”对“彩明”的命令权;
- “贾母笑道:‘难为你费心’” → 隐含“贾母”对“说话者”的认可关系;
- “黛玉葬花,宝玉偷看” → 隐含“宝玉”对“黛玉”的关注行为。
我采用依存句法分析+关系映射表方案:
- 用LTP(哈工大语言技术平台)解析句子,获取主谓宾、定中、状中等依存关系;
- 定义12种语义关系映射规则(如“主谓关系+动词=行为”,“定中关系+名词=所属”);
- 对每个三元组打置信度分数,过滤低分项。
关键代码片段(需提前安装ltp库):
from ltp import LTP ltp = LTP() def extract_relations_from_sentence(sentence: str, persons: list) -> list: # 分词+词性+依存分析 seg, hidden = ltp.seg([sentence]) pos = ltp.pos(hidden) dep = ltp.dep(hidden) relations = [] words = seg[0] heads = dep[0][0] # head节点索引 relations_dep = dep[0][1] # 关系类型 # 遍历所有依存弧 for i, (head_idx, rel_type) in enumerate(zip(heads, relations_dep)): if head_idx == 0: # 根节点跳过 continue head_word = words[head_idx - 1] if head_idx > 0 else "" dep_word = words[i] # 仅当头尾均为人名时才生成关系 if head_word in persons and dep_word in persons: # 映射依存关系到语义关系 semantic_rel = { 'SBV': '主语', 'VOB': '宾语', 'ATT': '修饰', 'ADV': '状语', 'CMP': '补语', 'COO': '并列', 'POB': '介宾', 'DBL': '连谓' }.get(rel_type, '其他') # 动词作为关系谓词(取谓语动词) if rel_type == 'SBV': verb = None for j, (h, r) in enumerate(zip(heads, relations_dep)): if h == i + 1 and r == 'VOB': # 找到该主语对应的动词 verb = words[j] break if verb: relations.append((head_word, verb, dep_word)) return relations # 示例:对含人物对话的句子抽取 sample_sent = "王熙凤命彩明念《大诰》,贾母听了点头" persons = ["王熙凤", "彩明", "贾母"] rels = extract_relations_from_sentence(sample_sent, persons) print(rels) # 输出:[('王熙凤', '命', '彩明'), ('贾母', '听', '王熙凤')]避坑提示:LTP默认分词可能将“贾宝玉”切为“贾/宝玉”,需在初始化时加载自定义词典:
ltp = LTP(ckpt='base', custom_dict=['贾宝玉','林黛玉','薛宝钗'])。另外,依存分析对长句准确率下降,务必按句号/问号/感叹号严格断句,避免跨句依存错误。
3. 构建Neo4j知识图谱:从CSV导入到Cypher建模的全流程实操
3.1 Neo4j环境搭建:Windows下免安装版的3分钟极速启动法
Neo4j Desktop是官方推荐方式,但毕业设计场景常受限于校园网防火墙或管理员权限。我采用Neo4j Community Edition免安装版(v4.4.32,兼容Python 3.8+),全程无需管理员权限,解压即用:
- 下载地址:Neo4j官网Archive页面 → 找到
neo4j-community-windows-4.4.32.zip(注意:v5.x要求Java 17,而学校机房多为Java 8,v4.4.32完美兼容); - 解压到任意路径(如
D:\neo4j),进入bin目录,双击neo4j.bat; - 首次运行会提示设置密码,输入
123456(后续Python连接时使用); - 浏览器访问
http://localhost:7474,输入账号neo4j/密码123456,即进入Web管理界面。
提示:若提示“Java not found”,请先安装JDK 8(非JDK 17),并设置
JAVA_HOME环境变量指向JDK 8安装目录。Neo4j v4.4.32对Java版本极其敏感,用错版本会导致服务无法启动。
3.2 CSV数据准备:人物、关系、属性三张表的字段规范与生成脚本
Neo4j导入要求CSV严格遵循格式:首行是字段名,无BOM头,UTF-8编码。我定义三张表:
| 表名 | 字段 | 说明 |
|---|---|---|
persons.csv | name:string,gender:string,rank:int,first_appearance:int | rank: 主子(1)、仆役(2)、僧道(3);first_appearance: 首次出场回目 |
relations.csv | source:string,target:string,relation:string,confidence:float,chapter:int | confidence: 0.1~1.0,依存分析得分归一化 |
attributes.csv | person:string,attr_key:string,attr_value:string | 存储“籍贯:金陵”“性格:孤高”等键值对 |
生成脚本(确保persons.csv和relations.csv已由前序步骤产出):
import csv # 生成persons.csv(示例数据) persons_data = [ ["贾宝玉", "男", 1, 3], ["林黛玉", "女", 1, 3], ["薛宝钗", "女", 1, 8], ["王熙凤", "女", 1, 3], ["袭人", "女", 2, 3], ] with open('persons.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['name', 'gender', 'rank', 'first_appearance']) writer.writerows(persons_data) # 生成relations.csv(示例) relations_data = [ ["贾宝玉", "林黛玉", "爱慕", 0.92, 3], ["贾宝玉", "薛宝钗", "礼敬", 0.75, 8], ["王熙凤", "贾母", "孝敬", 0.88, 3], ["王熙凤", "贾琏", "惧怕", 0.65, 3], ] with open('relations.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['source', 'target', 'relation', 'confidence', 'chapter']) writer.writerows(relations_data)注意:CSV中禁止出现逗号、换行符。若
attr_value含逗号(如“爱好:吟诗,作画”),需用双引号包裹字段,如"爱好","吟诗,作画"。Python的csv.writer自动处理引号,无需手动加。
3.3 Cypher建模:用12行语句定义图谱Schema并批量导入
Neo4j不强制Schema,但定义约束能极大提升查询性能和数据质量。以下Cypher语句需在http://localhost:7474的Browser界面中逐条执行:
// 步骤1:创建唯一约束(防止重复人物) CREATE CONSTRAINT ON (p:Person) ASSERT p.name IS UNIQUE; // 步骤2:创建关系类型索引(加速按关系查询) CREATE INDEX ON :RELATION(relation); // 步骤3:导入人物节点(假设CSV放在Neo4j的import目录下) LOAD CSV WITH HEADERS FROM 'file:///persons.csv' AS row CREATE (:Person {name: row.name, gender: row.gender, rank: toInteger(row.rank), first_appearance: toInteger(row.first_appearance)}); // 步骤4:导入关系(需先确保源/目标节点存在) LOAD CSV WITH HEADERS FROM 'file:///relations.csv' AS row MATCH (a:Person {name: row.source}), (b:Person {name: row.target}) CREATE (a)-[r:RELATION {type: row.relation, confidence: toFloat(row.confidence), chapter: toInteger(row.chapter)}]->(b);参数说明:
file:///路径指向Neo4j安装目录下的import文件夹(Windows路径为D:\neo4j\import),必须将persons.csv和relations.csv放在此目录。toInteger()和toFloat()是Neo4j内置函数,用于类型转换,缺失会报错。执行后可在Browser界面右上角点击“Database Information”查看节点/关系总数。
3.4 可视化配置:让“贾宝玉-林黛玉-薛宝钗”三角关系一眼看清
Neo4j Browser默认力导向图布局易造成重叠。针对《红楼梦》人物关系,我调整以下参数:
- 节点大小:按
first_appearance回目倒序,越早出场越大(size: 10 + (80 - first_appearance)); - 边粗细:按
confidence线性映射,0.9→5px,0.5→1px; - 颜色编码:
rank字段决定节点色系(主子:#FF6B6B,仆役:#4ECDC4,僧道:#45B7D1); - 标签显示:仅显示
name,隐藏其他属性。
在Browser中点击右上角⚙️→Graph Style→编辑CSS,粘贴以下样式:
node { size: 10 + (80 - $first_appearance); color: { case $rank of 1: '#FF6B6B' 2: '#4ECDC4' 3: '#45B7D1' else '#96CEB4' end }; label: $name; border-color: #333; border-width: 1; } relationship { thickness: 1 + ($confidence * 4); color: #98D8C8; label: $type; }避坑提示:若节点重叠严重,点击Browser左下角“齿轮图标”→取消勾选“Physics”→手动拖拽布局。生产环境建议用
neovis.js嵌入网页,但毕设演示用Browser足够。
4. 基于规则的问答系统:不靠大模型,用Cypher模板+关键词匹配实现精准应答
4.1 问题分类:把用户提问拆成5类Cypher可翻译的模式
大模型问答虽火,但毕设场景需可控、可解释、可调试。我将《红楼梦》常见问题归纳为5类,每类对应固定Cypher模板:
| 问题类型 | 用户示例 | Cypher模板 | 关键词触发 |
|---|---|---|---|
| 直接关系查询 | “贾宝玉和林黛玉是什么关系?” | MATCH (a:Person {name:'贾宝玉'})-[r]->(b:Person {name:'林黛玉'}) RETURN r.type | 含“和…什么关系”、“…对…如何” |
| 路径查询 | “王熙凤通过谁影响了尤二姐?” | MATCH p=(a:Person {name:'王熙凤'})-[*1..3]-(b:Person {name:'尤二姐'}) RETURN nodes(p), relationships(p) | 含“通过谁”、“间接”、“影响” |
| 属性查询 | “薛宝钗的性格是什么?” | MATCH (p:Person {name:'薛宝钗'}) WHERE p.attr_key='性格' RETURN p.attr_value | 含“性格”、“籍贯”、“结局”等属性词 |
| 统计查询 | “贾母信任多少人?” | MATCH (a:Person {name:'贾母'})-[r:RELATION {type:'信任'}]->(b) RETURN count(b) | 含“多少”、“几个”、“总计” |
| 条件筛选 | “和贾宝玉有爱慕关系且首次出场在前10回的人是谁?” | MATCH (a:Person {name:'贾宝玉'})-[r:RELATION {type:'爱慕'}]->(b) WHERE b.first_appearance <= 10 RETURN b.name | 含“且”、“同时”、“又”等逻辑词 |
4.2 关键词提取:用TF-IDF+人工词典双校验锁定核心实体
用户提问常含口语化表达:“宝哥哥跟林妹妹咋样?”、“凤姐儿管着谁?”。需将口语转为标准名:
import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity # 构建人物名TF-IDF向量(基于所有人物名分词) person_names = ["贾宝玉", "林黛玉", "薛宝钗", "王熙凤", "贾母", "贾政", "王夫人"] # 将每个名字拆为字符级ngram(避免分词错误) name_ngrams = [] for name in person_names: ngrams = [name[i:i+2] for i in range(len(name)-1)] + [name] name_ngrams.append(' '.join(ngrams)) vectorizer = TfidfVectorizer() tfidf_matrix = vectorizer.fit_transform(name_ngrams) def extract_person_from_query(query: str) -> str: # 步骤1:人工词典硬匹配(优先级最高) manual_map = {"宝哥哥": "贾宝玉", "林妹妹": "林黛玉", "凤姐儿": "王熙凤", "老太太": "贾母"} for spoken, standard in manual_map.items(): if spoken in query: return standard # 步骤2:TF-IDF相似度匹配 query_vec = vectorizer.transform([query]) similarities = cosine_similarity(query_vec, tfidf_matrix).flatten() best_idx = similarities.argmax() if similarities[best_idx] > 0.3: # 阈值防误匹配 return person_names[best_idx] return None # 示例 print(extract_person_from_query("宝哥哥跟林妹妹咋样?")) # 输出:贾宝玉 print(extract_person_from_query("老太太最信任谁?")) # 输出:贾母参数说明:
cosine_similarity阈值设为0.3是经验值,低于此值视为无匹配。人工词典manual_map必须覆盖《红楼梦》全部昵称(共47个,详见nicknames.txt),否则口语提问将失败。
4.3 Cypher生成引擎:把自然语言问题编译成可执行查询
核心逻辑是模板填充+安全校验,杜绝Cypher注入:
def generate_cypher(query: str) -> str: # 提取主语、宾语、关系词 subject = extract_person_from_query(query) object_ = extract_person_from_query(query.replace(subject, "")) if subject else None relation_keyword = None # 判断问题类型 if "什么关系" in query or "如何" in query: if subject and object_: return f"MATCH (a:Person {{name:'{subject}'}})-[r]->(b:Person {{name:'{object_}'}}) RETURN r.type" elif "通过谁" in query or "间接" in query or "影响" in query: if subject and object_: return f"MATCH p=(a:Person {{name:'{subject}'}})-[*1..3]-(b:Person {{name:'{object_}'}}) RETURN nodes(p), relationships(p)" elif "多少" in query or "几个" in query: # 提取关系类型(如“信任”) for rel in ["信任", "惧怕", "孝敬", "爱慕"]: if rel in query: relation_keyword = rel break if subject and relation_keyword: return f"MATCH (a:Person {{name:'{subject}'}})-[r:RELATION {{type:'{relation_keyword}'}}]->(b) RETURN count(b)" # 默认返回空 return "RETURN '未识别问题类型,请用标准人名提问'" # 安全校验:禁止';'、'DROP'、'CREATE'等危险关键词 def safe_cypher(cypher: str) -> bool: dangerous = [';', 'DROP', 'CREATE', 'DELETE', 'CALL db.', 'UNION'] return not any(d in cypher.upper() for d in dangerous) # 示例 q1 = "贾宝玉和林黛玉是什么关系?" cypher1 = generate_cypher(q1) if safe_cypher(cypher1): print("生成Cypher:", cypher1) # 输出:MATCH (a:Person {name:'贾宝玉'})-[r]->(b:Person {name:'林黛玉'}) RETURN r.type避坑提示:
safe_cypher()校验必须放在Cypher执行前,否则用户输入贾宝玉; DROP DATABASE;将直接摧毁图谱。实际部署时,建议用Neo4j的dbms.security.procedures.unrestricted白名单机制,仅允许apoc和db相关过程。
5. 关系可视化进阶:用PyVis生成可交互HTML图谱,支持导出PNG与缩放
5.1 PyVis基础配置:解决中文乱码与节点重叠两大玄学问题
PyVis默认用vis.js渲染,中文需额外配置字体,节点重叠需调大力导向参数:
from pyvis.network import Network import networkx as nx # 创建网络图 net = Network(height="750px", width="100%", bgcolor="#ffffff", font_color="black", notebook=True) # 设置中文字体(关键!) net.set_options(""" var options = { "configure": { "enabled": false }, "nodes": { "font": { "size": 16, "face": "Microsoft YaHei, SimSun, sans-serif" // 中文字体栈 } }, "physics": { "forceAtlas2Based": { "gravitationalConstant": -50, // 增大斥力,减少重叠 "centralGravity": 0.01, // 减小向心力 "springLength": 200, // 增大边长 "springConstant": 0.18 }, "minVelocity": 0.75, "solver": "forceAtlas2Based" } } """) # 从Neo4j导出子图(示例:贾宝玉为中心的3跳关系) from neo4j import GraphDatabase driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "123456")) with driver.session() as session: result = session.run(""" MATCH (a:Person {name:'贾宝玉'})-[:RELATION*1..2]-(b) RETURN a.name as source, b.name as target, collect(distinct type(r)) as rels """) for record in result: net.add_node(record['source'], label=record['source'], size=30, color='#FF6B6B') net.add_node(record['target'], label=record['target'], size=20, color='#4ECDC4') net.add_edge(record['source'], record['target'], label=', '.join(record['rels']), width=2) # 保存为HTML net.show("hongloumeng_graph.html")血泪经验:
font.face必须写成"Microsoft YaHei, SimSun, sans-serif",单独写"SimSun"在Mac上失效;gravitationalConstant设为负值才能增强节点斥力,正值反而加剧重叠。
5.2 导出高清PNG:绕过浏览器截图限制的3种可靠方案
PyVis的net.show()生成HTML,但截图常截不全。可靠方案:
方案A(推荐):用weasyprint转PDF再转PNG
pip install weasyprint weasyprint hongloumeng_graph.html hongloumeng.pdf # 用ImageMagick转PNG:convert -density 300 hongloumeng.pdf hongloumeng.png方案B:Selenium自动化截图
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') driver = webdriver.Chrome(options=options) driver.get('file:///path/to/hongloumeng_graph.html') driver.set_window_size(1920, 1080) driver.save_screenshot('hongloumeng.png') driver.quit()方案C:PyVis内置export(仅限简单图)
net.write_html("hongloumeng.html") # 先生成HTML # 手动打开HTML,按Ctrl+P → 选择“另存为PDF”,再用PDF阅读器导出PNG
参数说明:Selenium方案需提前下载ChromeDriver(版本需匹配Chrome),
--headless参数保证无界面运行。weasyprint方案对CSS支持最好,但需系统安装libcairo2-dev等依赖。
5.3 问答系统前端:用Streamlit 30行代码搭起零配置Web界面
毕设答辩需现场演示,Streamlit是最轻量选择:
import streamlit as st from neo4j import GraphDatabase st.title("《红楼梦》知识图谱问答系统") st.markdown("输入问题,例如:`贾宝玉和林黛玉是什么关系?` 或 `王熙凤信任多少人?`") # 连接Neo4j driver = GraphDatabase.driver("bolt://localhost:7474", auth=("neo4j", "123456")) user_input = st.text_input("你的问题:", "") if user_input: # 生成Cypher(复用前述generate_cypher函数) cypher = generate_cypher(user_input) st.code(cypher, language="cypher") try: with driver.session() as session: result = session.run(cypher) records = list(result) if records: # 表格展示结果 st.table(records) # 若为路径查询,生成子图 if "nodes(p)" in cypher: st.subheader("关系路径图") # 此处调用PyVis生成子图并嵌入(略,见5.1节) else: st.warning("未找到答案,请检查人名是否正确") except Exception as e: st.error(f"查询出错:{str(e)}") st.markdown("💡 提示:问题中请使用标准人名,如`贾宝玉`而非`宝二爷`")避坑提示:Streamlit默认端口8501,若被占用可加
--server.port 8502启动。Neo4j连接字符串中的bolt://localhost:7474必须与Neo4j实际端口一致(默认7687,Web界面是7474,驱动要用7687)。
6. 毕设落地关键技巧:从查重
本文还有配套的精品资源,点击获取