ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从零构建医药知识图谱问答系统:数据爬取、Neo4j存储与智能问答实战

2026/8/18 8:50:17 拓冰建站 浏览量
从零构建医药知识图谱问答系统:数据爬取、Neo4j存储与智能问答实战 大家好我是专注于技术实战分享的博主。临近毕业季不少计算机、数据科学相关专业的同学都在为毕设选题和实现发愁。如果你对知识图谱、自然语言处理或智能问答系统感兴趣那么一个结合了数据采集、知识融合、图数据库存储与智能问答的“医药知识图谱问答系统”将是一个极具挑战性和展示度的绝佳选题。本文将带你从零开始完整实现一个医药领域的知识图谱问答系统涵盖从网络数据爬取、知识抽取与融合、Neo4j图数据库存储到最终构建一个可交互的智能问答应用的全流程。无论你是希望完成一个高质量的毕业设计还是想深入学习知识图谱的工程化实践这篇文章都将提供一套可直接复现的代码和清晰的实现思路。1. 项目背景与核心概念解析在开始动手之前我们首先要理解这个项目到底要做什么以及其中涉及的核心技术概念。1.1 什么是知识图谱与智能问答知识图谱本质上是一种用图结构来建模和存储知识的技术。它将现实世界中的实体如“阿司匹林”、“高血压”作为“节点”将实体之间的关系如“治疗”、“属于”作为“边”从而形成一个庞大的语义网络。这种结构非常符合人类的认知方式也便于计算机进行复杂的关联查询和推理。智能问答系统则是基于这个语义网络允许用户用自然语言如“阿司匹林能治疗什么病”进行提问系统通过理解问题意图在图数据库中查询并推理最终返回结构化的答案。这比传统的基于关键词的搜索引擎更加精准和智能。1.2 为什么选择医药领域医药领域知识结构清晰、关系复杂、专业性强非常适合用知识图谱来建模。药物、疾病、症状、基因、副作用等实体之间存在着大量的、多层次的关系。构建一个医药知识图谱不仅能作为毕业设计的优秀案例其技术思路也可以迁移到金融、法律、教育等其他垂直领域。1.3 技术栈全景图本项目将涉及一个完整的技术链路我们可以将其分为四个核心阶段数据采集层使用 Python 爬虫从公开的医药网站或数据库模拟数据源采集原始文本数据。知识处理层利用自然语言处理技术从文本中抽取实体和关系并进行知识融合对齐相同实体。知识存储层将清洗和融合后的结构化知识存入 Neo4j 图数据库。应用层构建一个基于 Spring Boot 或 Flask 的 Web 应用提供图谱可视化界面和智能问答接口。接下来我们将按照这个流程一步步实现。2. 环境准备与版本说明工欲善其事必先利其器。以下是完成本项目所需的主要软件环境。请注意版本号是撰写本文时的常见选择实际操作时请以官方最新稳定版为准核心逻辑通常兼容。2.1 开发环境与工具操作系统Windows 10/11 macOS 或 Linux (如 Ubuntu 20.04) 均可。本文命令以 Linux/macOS 为例Windows 用户可在 Git Bash 或 WSL 下运行。Python 环境Python 3.8 或以上版本。推荐使用 Anaconda 或 Miniconda 创建独立的虚拟环境。Java 环境JDK 8 或 11 (用于运行 Neo4j 和 Spring Boot)。数据库Neo4j 社区版 4.4 或 5.x。IDE/编辑器PyCharm, VS Code, IntelliJ IDEA 等任选。项目管理Maven 3.6 (用于 Spring Boot 项目) pip (用于 Python 包管理)。2.2 核心依赖库清单我们将创建两个主要项目一个 Python 项目用于数据处理一个 Java (Spring Boot) 项目用于构建 Web 应用和问答服务。Python 项目依赖 (requirements.txt)# 网络请求与解析 requests2.28.0 beautifulsoup44.11.0 lxml4.9.0 # 数据处理 pandas1.5.0 numpy1.23.0 # 自然语言处理用于简单的实体抽取毕业设计可用规则或简单模型 jieba0.42.1 # 中文分词 pyltp3.4.0 # 或使用 HanLP, 用于命名实体识别(NER)和关系抽取需自行下载模型文件 # 注对于深度学习的NER可使用 transformers 库但复杂度较高。 # Neo4j 驱动 neo4j5.5.0 # Web框架可选用于提供简单API flask2.2.0Spring Boot 项目依赖 (pom.xml部分)dependencies !-- Web 支持 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Neo4j 驱动 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-neo4j/artifactId /dependency !-- 模板引擎 (用于简单前端) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- 单元测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies3. 第一阶段医药数据爬虫采集实战数据是知识图谱的基石。由于直接使用真实医药数据库如DrugBank可能涉及许可问题我们以一个模拟的、结构清晰的公开医药信息网站为例讲解爬虫的完整流程。请务必遵守目标网站的robots.txt协议控制爬取频率切勿对目标服务器造成压力。3.1 目标分析与页面结构解析假设我们的目标是爬取一个模拟的“常见药品说明书”网站页面列出了药品名称点击进入详情页后有“适应症”、“不良反应”、“相互作用”等字段。首先我们使用浏览器开发者工具F12分析页面结构找到数据所在的HTML标签。3.2 使用 Requests 和 BeautifulSoup 编写爬虫我们将编写一个爬虫先抓取药品列表页再遍历进入每个药品详情页抓取结构化信息。# file: spider/medicine_spider.py import requests from bs4 import BeautifulSoup import pandas as pd import time import random class MedicineSpider: def __init__(self): self.base_url http://example-medicine-site.com # 请替换为实际目标或使用本地Mock self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } self.session requests.Session() self.data_list [] def parse_list_page(self, page_num): 解析药品列表页获取药品详情页链接 list_url f{self.base_url}/list?page{page_num} try: resp self.session.get(list_url, headersself.headers, timeout10) resp.raise_for_status() # 检查请求是否成功 soup BeautifulSoup(resp.text, lxml) # 假设详情页链接在 classmedicine-item 的 a 标签里 medicine_items soup.find_all(a, class_medicine-item) detail_urls [item[href] for item in medicine_items] return detail_urls except requests.RequestException as e: print(f请求列表页 {list_url} 失败: {e}) return [] def parse_detail_page(self, detail_url): 解析药品详情页提取结构化信息 full_url self.base_url detail_url if detail_url.startswith(/) else detail_url try: resp self.session.get(full_url, headersself.headers, timeout10) resp.raise_for_status() soup BeautifulSoup(resp.text, lxml) # 根据实际网页结构定位元素这里为示例 medicine_info { name: self._extract_text(soup, h1.medicine-name), generic_name: self._extract_text(soup, div.generic-name), indications: self._extract_text(soup, section#indications), side_effects: self._extract_text(soup, section#side-effects), interactions: self._extract_text(soup, section#interactions), source_url: full_url } # 简单的数据清洗去除多余空白 for key, value in medicine_info.items(): if isinstance(value, str): medicine_info[key] .join(value.split()) return medicine_info except requests.RequestException as e: print(f请求详情页 {full_url} 失败: {e}) return None def _extract_text(self, soup, selector): 辅助方法根据CSS选择器提取文本若找不到则返回空字符串 element soup.select_one(selector) return element.get_text(stripTrue) if element else def run(self, start_page1, end_page3): 主运行方法 print(开始爬取医药数据...) for page in range(start_page, end_page 1): print(f正在爬取第 {page} 页...) detail_urls self.parse_list_page(page) for idx, url in enumerate(detail_urls): print(f 处理药品 {idx 1}/{len(detail_urls)}: {url}) info self.parse_detail_page(url) if info: self.data_list.append(info) # 礼貌爬取添加随机延迟 time.sleep(random.uniform(1, 3)) time.sleep(2) # 每页之间稍长延迟 # 保存数据到CSV if self.data_list: df pd.DataFrame(self.data_list) df.to_csv(./data/medicine_data_raw.csv, indexFalse, encodingutf-8-sig) print(f数据爬取完成共 {len(self.data_list)} 条已保存至 medicine_data_raw.csv) else: print(未爬取到任何数据。) if __name__ __main__: spider MedicineSpider() spider.run(start_page1, end_page2) # 演示只爬2页3.3 数据清洗与初步处理爬取到的原始数据通常包含噪声。我们需要进行清洗为下一步的知识抽取做准备。# file: data_cleaner.py import pandas as pd import re def clean_medicine_data(input_path, output_path): df pd.read_csv(input_path, encodingutf-8-sig) print(f原始数据形状: {df.shape}) # 1. 去除完全空白的行 df.dropna(howall, inplaceTrue) # 2. 填充部分空值 df.fillna(, inplaceTrue) # 3. 去除重复项根据药品名称和通用名 df.drop_duplicates(subset[name, generic_name], keepfirst, inplaceTrue) # 4. 文本清洗去除特殊字符、多余空格等 text_columns [indications, side_effects, interactions] for col in text_columns: df[col] df[col].apply(lambda x: re.sub(r\s, , str(x)).strip()) # 5. 标准化例如将适应症中的疾病名称统一这里简化处理实际可能需要词典 # df[indications] df[indications].apply(standardize_disease_name) print(f清洗后数据形状: {df.shape}) df.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f清洗后的数据已保存至: {output_path}) return df # 执行清洗 if __name__ __main__: clean_df clean_medicine_data(./data/medicine_data_raw.csv, ./data/medicine_data_cleaned.csv)4. 第二阶段知识抽取、融合与 Neo4j 存储有了清洗后的文本数据我们需要从中抽取出结构化的“实体-关系-实体”三元组。4.1 基于规则的知识抽取对于毕业设计使用基于规则或词典的方法是一个可行的起点。我们定义一些规则来从“适应症”等字段中抽取关系。# file: knowledge_extractor.py import pandas as pd import jieba import jieba.posseg as pseg from collections import defaultdict # 加载自定义词典提高实体识别准确率 jieba.load_userdict(./dict/disease_dict.txt) # 疾病词典 jieba.load_userdict(./dict/drug_dict.txt) # 药物词典 class RuleBasedExtractor: def __init__(self): self.disease_keywords [病, 症, 炎, 癌, 瘤, 感染] # 疾病特征词 self.relation_patterns { 治疗: [用于治疗, 适用于, 主治, 治疗], 引起: [引起, 导致, 可能产生], 相互作用: [与.*合用, 不宜与.*同时使用] } def extract_triplets_from_text(self, text, drug_name): 从一段文本中抽取与特定药物相关的三元组 triplets [] if not text: return triplets # 使用jieba进行分词和词性标注 words pseg.cut(text) # 这里简化处理寻找包含疾病关键词的词语作为疾病实体 # 更复杂的做法可以使用预训练的NER模型如HanLP, LTP potential_diseases [] for word, flag in words: if any(keyword in word for keyword in self.disease_keywords) and len(word) 1: potential_diseases.append(word) # 根据关系模式匹配 for relation, patterns in self.relation_patterns.items(): for pattern in patterns: # 简化匹配逻辑实际应用可能需要更复杂的正则表达式 if pattern in text: for disease in set(potential_diseases): # 去重 triplets.append((drug_name, relation, disease)) return triplets def extract_from_dataframe(self, df): 从整个DataFrame中抽取三元组 all_triplets [] for _, row in df.iterrows(): drug_name row[name] # 从适应症中抽取“治疗”关系 indications_triplets self.extract_triplets_from_text(row[indications], drug_name) # 从副作用中抽取“引起”关系 side_effect_triplets self.extract_triplets_from_text(row[side_effects], drug_name) # 从相互作用中抽取“相互作用”关系 interaction_text row[interactions] # 假设相互作用文本中包含其他药物名称这里需要另一个抽取器 # 为简化我们只做演示 # interaction_triplets extract_interaction(row[interactions], drug_name) all_triplets.extend(indications_triplets) all_triplets.extend(side_effect_triplets) # all_triplets.extend(interaction_triplets) # 转换为DataFrame triplet_df pd.DataFrame(all_triplets, columns[head, relation, tail]) # 简单的知识融合合并相同的实体例如“高血压”和“高血压病” triplet_df[head] triplet_df[head].apply(self._normalize_entity) triplet_df[tail] triplet_df[tail].apply(self._normalize_entity) # 去除重复的三元组 triplet_df.drop_duplicates(inplaceTrue) return triplet_df def _normalize_entity(self, entity): 简单的实体归一化示例 # 这里可以接入更复杂的标准化服务或词典 normalization_map {高血压病: 高血压, 高血脂症: 高血脂} return normalization_map.get(entity, entity) if __name__ __main__: df pd.read_csv(./data/medicine_data_cleaned.csv, encodingutf-8-sig) extractor RuleBasedExtractor() triplet_df extractor.extract_from_dataframe(df) print(f共抽取到 {len(triplet_df)} 个三元组) print(triplet_df.head()) triplet_df.to_csv(./data/knowledge_triplets.csv, indexFalse, encodingutf-8-sig)4.2 Neo4j 安装与启动在将三元组存入图数据库前我们需要安装并启动 Neo4j。下载与安装访问 Neo4j 官网下载中心选择社区版根据你的操作系统下载安装包或使用 Docker。桌面版适合初学者提供图形化界面。命令行版更轻量。Dockerdocker run -d --name neo4j -p 7474:7474 -p 7687:7687 -v neo4j_data:/data -v neo4j_logs:/logs -v neo4j_import:/var/lib/neo4j/import --env NEO4J_AUTHneo4j/your_password neo4j:latest启动与访问启动服务后在浏览器中打开http://localhost:7474使用默认用户名neo4j和你在安装时设置的密码登录。创建数据库在 Neo4j Browser 中你可以直接执行 Cypher 查询语言来操作图数据。4.3 使用 Python 连接并导入数据到 Neo4j我们将使用neo4j官方驱动将上一步生成的三元组 CSV 文件导入到 Neo4j 中。# file: neo4j_importer.py from neo4j import GraphDatabase import pandas as pd class Neo4jImporter: def __init__(self, uri, user, password): self.driver GraphDatabase.driver(uri, auth(user, password)) def close(self): self.driver.close() def create_constraints(self): 创建约束和索引以提高查询性能并保证数据唯一性 with self.driver.session() as session: # 为药品实体创建唯一性约束 session.run(CREATE CONSTRAINT IF NOT EXISTS FOR (d:Drug) REQUIRE d.name IS UNIQUE) # 为疾病实体创建唯一性约束 session.run(CREATE CONSTRAINT IF NOT EXISTS FOR (s:Disease) REQUIRE s.name IS UNIQUE) print(约束创建完成。) def import_triplets_from_csv(self, csv_path): 从CSV文件导入三元组到Neo4j df pd.read_csv(csv_path, encodingutf-8-sig) with self.driver.session() as session: tx session.begin_transaction() try: for _, row in df.iterrows(): head, relation, tail row[head], row[relation], row[tail] # 根据关系类型创建不同的边 if relation 治疗: cypher MERGE (d:Drug {name: $head}) MERGE (s:Disease {name: $tail}) MERGE (d)-[:TREATS]-(s) elif relation 引起: cypher MERGE (d:Drug {name: $head}) MERGE (s:Disease {name: $tail}) MERGE (d)-[:CAUSES]-(s) elif relation 相互作用: # 假设相互作用是药物-药物关系 cypher MERGE (d1:Drug {name: $head}) MERGE (d2:Drug {name: $tail}) MERGE (d1)-[:INTERACTS_WITH]-(d2) else: # 通用关系创建 cypher MERGE (e1:Entity {name: $head}) MERGE (e2:Entity {name: $tail}) MERGE (e1)-[:RELATION {type: $relation}]-(e2) tx.run(cypher, headhead, tailtail, relationrelation) tx.commit() print(f成功导入 {len(df)} 条三元组到 Neo4j。) except Exception as e: tx.rollback() print(f导入失败: {e}) raise if __name__ __main__: # 修改为你的 Neo4j 连接信息 URI bolt://localhost:7687 USER neo4j PASSWORD your_password_here # 务必修改 importer Neo4jImporter(URI, USER, PASSWORD) try: importer.create_constraints() importer.import_triplets_from_csv(./data/knowledge_triplets.csv) finally: importer.close()运行此脚本后你的医药知识图谱就初步构建完成了。可以在 Neo4j Browser 中执行MATCH (n) RETURN n LIMIT 25来可视化图谱。5. 第三阶段构建智能问答系统后端Spring Boot知识图谱建好后我们需要构建一个应用来使用它。这里我们使用 Spring Boot 搭建后端提供问答 API。5.1 项目初始化与配置使用 Spring Initializr 创建一个新项目选择依赖Web, Neo4j, Thymeleaf。application.yml 配置spring: neo4j: uri: bolt://localhost:7687 authentication: username: neo4j password: your_password_here thymeleaf: cache: false # 开发时关闭缓存 server: port: 80805.2 定义 Neo4j 实体与关系// file: src/main/java/com/example/kgqa/entity/Drug.java package com.example.kgqa.entity; import org.springframework.data.neo4j.core.schema.*; import java.util.HashSet; import java.util.Set; Node(Drug) public class Drug { Id GeneratedValue private Long id; Property(name) private String name; Relationship(type TREATS, direction Relationship.Direction.OUTGOING) private SetDisease treatedDiseases new HashSet(); Relationship(type CAUSES, direction Relationship.Direction.OUTGOING) private SetDisease causedSideEffects new HashSet(); Relationship(type INTERACTS_WITH, direction Relationship.Direction.OUTGOING) private SetDrug interactingDrugs new HashSet(); // 构造器、Getter和Setter省略... }// file: src/main/java/com/example/kgqa/entity/Disease.java package com.example.kgqa.entity; import org.springframework.data.neo4j.core.schema.*; Node(Disease) public class Disease { Id GeneratedValue private Long id; Property(name) private String name; // 构造器、Getter和Setter省略... }5.3 创建 Repository 层进行图查询// file: src/main/java/com/example/kgqa/repository/DrugRepository.java package com.example.kgqa.repository; import com.example.kgqa.entity.Drug; import org.springframework.data.neo4j.repository.Neo4jRepository; import org.springframework.data.neo4j.repository.query.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface DrugRepository extends Neo4jRepositoryDrug, Long { Drug findByName(String name); // 查询治疗某种疾病的所有药物 Query(MATCH (d:Drug)-[:TREATS]-(dis:Disease {name: $diseaseName}) RETURN d) ListDrug findDrugsTreatingDisease(Param(diseaseName) String diseaseName); // 查询某种药物的所有副作用导致的疾病 Query(MATCH (d:Drug {name: $drugName})-[:CAUSES]-(dis:Disease) RETURN dis) ListDisease findSideEffectsOfDrug(Param(drugName) String drugName); // 查询与某种药物有相互作用的其他药物 Query(MATCH (d1:Drug {name: $drugName})-[:INTERACTS_WITH]-(d2:Drug) RETURN d2) ListDrug findInteractingDrugs(Param(drugName) String drugName); }5.4 实现简单的问答服务层我们实现一个基于规则的问题理解模块将自然语言问题解析为预设的 Cypher 查询模板。// file: src/main/java/com/example/kgqa/service/QuestionService.java package com.example.kgqa.service; import com.example.kgqa.entity.Disease; import com.example.kgqa.entity.Drug; import com.example.kgqa.repository.DrugRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.regex.Pattern; Service public class QuestionService { Autowired private DrugRepository drugRepository; /** * 解析问题并返回答案 * param question 自然语言问题 * return 答案字符串 */ public String answerQuestion(String question) { question question.trim().toLowerCase(); // 规则1治疗某病的药物有哪些 Pattern treatPattern Pattern.compile(.*治疗.*?(.?)的药.*|.*什么药.*治疗.*?(.?)[?]?); // 规则2某药有什么副作用 Pattern sideEffectPattern Pattern.compile(.*?(.?)的副作用.*|.*什么副作用.*?(.?)[?]?); // 规则3某药和什么药相互作用 Pattern interactPattern Pattern.compile(.*?(.?)和什么药.*相互作用.*|.*相互作用.*?(.?)[?]?); // 尝试匹配规则 java.util.regex.Matcher matcher; matcher treatPattern.matcher(question); if (matcher.find()) { String diseaseName matcher.group(1) ! null ? matcher.group(1) : matcher.group(2); return answerTreatQuestion(diseaseName); } matcher sideEffectPattern.matcher(question); if (matcher.find()) { String drugName matcher.group(1) ! null ? matcher.group(1) : matcher.group(2); return answerSideEffectQuestion(drugName); } matcher interactPattern.matcher(question); if (matcher.find()) { String drugName matcher.group(1) ! null ? matcher.group(1) : matcher.group(2); return answerInteractQuestion(drugName); } return 抱歉我暂时无法理解您的问题。请尝试询问关于药物治疗、副作用或相互作用的信息。; } private String answerTreatQuestion(String diseaseName) { ListDrug drugs drugRepository.findDrugsTreatingDisease(diseaseName); if (drugs.isEmpty()) { return String.format(知识图谱中暂无治疗【%s】的药物信息。, diseaseName); } StringBuilder sb new StringBuilder(); sb.append(治疗【).append(diseaseName).append(】的药物有); drugs.forEach(drug - sb.append(drug.getName()).append()); return sb.toString(); } private String answerSideEffectQuestion(String drugName) { Drug drug drugRepository.findByName(drugName); if (drug null) { return String.format(知识图谱中暂无药物【%s】的信息。, drugName); } ListDisease sideEffects drugRepository.findSideEffectsOfDrug(drugName); if (sideEffects.isEmpty()) { return String.format(药物【%s】在知识图谱中记录的副作用信息为空。, drugName); } StringBuilder sb new StringBuilder(); sb.append(药物【).append(drugName).append(】可能引起的副作用包括); sideEffects.forEach(disease - sb.append(disease.getName()).append()); return sb.toString(); } private String answerInteractQuestion(String drugName) { Drug drug drugRepository.findByName(drugName); if (drug null) { return String.format(知识图谱中暂无药物【%s】的信息。, drugName); } ListDrug interactingDrugs drugRepository.findInteractingDrugs(drugName); if (interactingDrugs.isEmpty()) { return String.format(药物【%s】在知识图谱中暂无相互作用药物记录。, drugName); } StringBuilder sb new StringBuilder(); sb.append(药物【).append(drugName).append(】已知与以下药物存在相互作用); interactingDrugs.forEach(d - sb.append(d.getName()).append()); return sb.toString(); } }5.5 创建控制器和简单前端// file: src/main/java/com/example/kgqa/controller/QAController.java package com.example.kgqa.controller; import com.example.kgqa.service.QuestionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; Controller public class QAController { Autowired private QuestionService questionService; GetMapping(/) public String index(Model model) { model.addAttribute(answer, ); model.addAttribute(question, ); return index; } PostMapping(/ask) public String askQuestion(RequestParam String question, Model model) { String answer questionService.answerQuestion(question); model.addAttribute(answer, answer); model.addAttribute(question, question); return index; } }!-- file: src/main/resources/templates/index.html -- !DOCTYPE html html langzh-CN xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title医药知识图谱问答系统/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet /head body classcontainer mt-5 h1 classmb-4 医药知识图谱智能问答系统/h1 p classlead基于Neo4j图数据库构建尝试询问药品与疾病的关系。/p form th:action{/ask} methodpost classmb-4 div classinput-group input typetext classform-control namequestion th:value${question} placeholder例如治疗高血压的药物有哪些 或 阿司匹林有什么副作用 required button classbtn btn-primary typesubmit提问/button /div /form div th:if${question} classalert alert-secondary strong你的问题/strong span th:text${question}/span /div div th:if${answer} classalert alert-info strong系统回答/strong span th:text${answer}/span /div hr p classtext-muted示例问题/p ul li治疗感冒的药有哪些/li li青霉素有什么副作用/li li布洛芬和什么药相互作用/li /ul /body /html启动 Spring Boot 应用 (KgqaApplication)访问http://localhost:8080即可与你的医药知识图谱问答系统交互。6. 常见问题与排查思路在实现上述流程时你可能会遇到一些典型问题。问题现象可能原因排查思路与解决方案爬虫无法获取数据或被封IP1. 网站有反爬机制如验证码、频率限制。2. 请求头User-Agent被识别。3. 页面结构已更新。1. 添加更完整的请求头如Referer,Accept-Language。2. 使用代理IP池需谨慎合法使用。3. 显著降低请求频率添加随机延迟。4. 检查并更新HTML解析规则。Neo4j 连接失败1. Neo4j 服务未启动。2. 连接地址、端口、用户名或密码错误。3. 防火墙阻止了 Bolt 端口7687。1. 运行neo4j status检查服务状态。2. 确认application.yml或 Python 脚本中的连接信息正确。3. 尝试在浏览器访问http://localhost:7474确认服务可用。4. 检查防火墙设置确保端口开放。知识抽取准确率低1. 规则过于简单无法覆盖复杂句式。2. 分词或实体识别不准确。3. 领域词典不全。1. 考虑使用预训练的中文NER模型如 HanLP, LTP替代规则。2. 扩充自定义词典疾病、药物名。3. 引入依存句法分析来更准确地识别关系。4. 对于毕业设计可以手动标注少量数据结合规则提升效果。问答系统无法理解问题1. 问题解析规则有限。2. 用户问题与规则模式不匹配。3. 问题中的实体未在知识图谱中。1. 增加更多问题匹配模式正则表达式。2. 引入意图识别和实体识别模块可以使用 Rasa 或飞桨PaddleNLP 等框架。3. 在回答前先检查实体是否存在给出友好提示。Spring Boot 无法注入 Neo4j Repository1. 主应用类未添加EnableNeo4jRepositories。2. 包扫描路径不正确。1. 在主应用类上添加EnableNeo4jRepositories注解。2. 确保SpringBootApplication能扫描到 Repository 所在的包。7. 项目优化与扩展建议完成基础版本后你可以从以下方向进行深化和扩展这会让你的毕设更具亮点。7.1 数据与知识层面多源数据融合不仅爬取一个网站可以整合多个公开的医药数据集如中医药方、药品说明书库进行实体对齐和冲突解决。引入更先进的抽取模型使用 BERT、ERNIE 等预训练模型进行命名实体识别和关系联合抽取大幅提升自动化程度和准确率。构建属性图谱不仅存储关系还为实体添加属性如药品的化学式、生产厂商、疾病的高发人群等。7.2 系统架构层面前后端分离将 Spring Boot 仅作为后端 API 服务使用 Vue.js 或 React 构建更美观、交互性更强的独立前端。引入缓存对于热门查询使用 Redis 缓存 Cypher 查询结果提升系统响应速度。容器化部署使用 Docker Compose 将 Neo4j、Spring Boot 后端、前端应用打包实现一键部署。7.3 问答能力层面语义解析升级抛弃简单的规则匹配采用语义解析技术将自然语言问题直接转换为规范的 Cypher 查询。支持多跳查询实现诸如“治疗A病的药物会引起哪些副作用”这类涉及多步关系的复杂问答。答案生成与摘要不仅返回实体列表还能生成连贯的文本摘要作为答案。7.4 工程化与展示增加图谱可视化集成 ECharts 或 G6 等图表库在Web页面上动态展示查询涉及的子图。实现用户登录与问答历史增加用户系统记录用户的提问历史。编写完整的项目文档包括需求分析、系统设计、核心算法、部署手册等这是毕设答辩的重要材料。通过这个从数据采集到智能问答的全流程实战你不仅完成了一个完整的毕业设计项目更深入理解了知识图谱技术的核心环节。这个项目具有很强的可扩展性你可以根据自己的兴趣和时间选择上述任何一个方向进行深化。动手开始搭建吧每一步的实践都会让你对知识图谱有更深刻的认识。如果在实现过程中遇到具体问题欢迎在评论区交流探讨。