news 2026/9/15 0:44:22

Python构建英雄联盟知识图谱问答系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python构建英雄联盟知识图谱问答系统

简介:这是一份面向计算机专业本科生的课程设计与期末大作业实战资源,基于Python构建英雄联盟领域知识图谱并实现自然语言问答功能,适用于知识图谱入门实践、NLP基础项目训练及课程设计选题参考。资源包共20个文件,含8个核心Python模块(如build_lol_graph.py构建图谱、chatbot_graph.py实现图查询、question_parser.py完成语义解析)、7个文本配置与说明文件、4个CSV格式的英雄/技能/装备等结构化数据,以及1份详细操作手册.docx,整体仅126KB,轻量易部署。已有141人学习下载,项目经导师指导获评98分,具备完整闭环:从爬虫spider.py采集原始数据、Trie树加速实体识别、多级分类器question_classification.py判断问句意图,到answer_search.py精准返回图谱答案。读者可直接复现知识图谱构建全流程,掌握Neo4j图数据库对接、中文分词与模板匹配结合的轻量级问答策略,以及典型游戏领域知识建模方法。

1. 用 Python 搭建英雄联盟知识图谱问答系统,不是在复刻游戏官网,而是在构建可推理的领域语义网络

你不需要登录拳头服务器,也不用逆向客户端协议——这个项目用不到任何游戏官方 API。它从公开资料(如维基、攻略站、英雄属性表)中爬取结构化数据,清洗后构建成 Neo4j 图数据库,再通过问句分类 + 实体识别 + Cypher 查询生成三步 pipeline,实现“亚托克斯的大招叫什么”“哪些英雄克制德莱文”“伤害类型为魔法的近战英雄有哪些”这类带逻辑关系的自然语言查询。它不是关键词匹配的检索框,而是能理解“克制”“大招”“近战”“魔法伤害”等术语背后语义关系的轻量级知识引擎。适合计算机专业学生做课程设计:代码模块清晰(爬虫→建图→解析→问答)、依赖明确(Python 3.8+、Neo4j 4.x、jieba、py2neo)、无外部服务绑定,本地即可完整跑通。98 分作业的硬核之处在于:它把 NLP 基础能力(分词、词性、依存)和图数据库查询逻辑做了紧耦合封装,而非简单调用现成 QA 框架。

2. 知识图谱构建:从原始网页到 Neo4j 节点关系的全流程实现

2.1 数据采集策略与 spider.py 的关键控制逻辑

spider.py是整个系统的数据源头,它不依赖 Selenium 或浏览器渲染,而是基于requests + BeautifulSoup对静态页面做精准抓取。核心目标是获取英雄基础属性(名称、定位、难度、攻击/防御/魔法/操作四维评分)、技能描述(名称、冷却、消耗、效果文本)、装备属性(价格、加成、合成路径)以及英雄间克制关系(常见于攻略站的“克制榜”表格)。代码中关键控制点有三处:

# spider.py 片段:控制请求频率与反爬标识 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7', 'Referer': 'https://lol.qq.com/' # 模拟来自腾讯LOL官网的跳转 } # 设置随机延迟(0.8~1.5秒),避免触发风控 time.sleep(random.uniform(0.8, 1.5))

提示:Referer头必须设置为真实目标站点域名(如lol.qq.comleagueoflegends.fandom.com),否则部分站点返回 403;time.sleep不是固定值,而是random.uniform生成的浮点数,模拟人工浏览节奏。

抓取后数据统一存入data/raw_data/目录下 JSON 文件,每个英雄一个文件(如ahri.json),结构为:

{ "name": "阿狸", "position": ["法师", "刺客"], "difficulty": 3, "stats": {"attack": 3, "defense": 3, "magic": 8, "difficulty": 3}, "skills": [ { "name": "欺诈宝珠", "type": "Q", "cooldown": "8/7.5/7/6.5/6 秒", "effect": "阿狸发射一枚宝珠,对命中的敌人造成魔法伤害,并治疗自身。" } ], "counter_heros": ["卡牌大师", "瑞兹"] }

2.2 图谱 Schema 设计与 build_lol_graph.py 的节点-关系映射

build_lol_graph.py将 JSON 数据转化为 Neo4j 可加载的图结构。其核心不是“把所有字段塞进图”,而是定义符合领域逻辑的 Schema。本项目采用三层实体模型:

实体类型属性字段关系类型关系方向语义说明
:Heroname, difficulty, position[]:HAS_SKILLHero → Skill英雄拥有技能
:Skillname, type(Q/W/E/R), cooldown, effect:BELONGS_TOSkill → Hero技能属于某英雄(反向)
:Itemname, price, stats{}:COUNTERSHero → Hero英雄克制关系(对称但需双向建边)
:Attributekey("attack"), value(3):HAS_STATHero → Attribute四维评分属性

执行建图前需确保 Neo4j 服务已启动且配置允许远程连接(dbms.connectors.default_listen_address=0.0.0.0)。建图脚本关键步骤如下:

# build_lol_graph.py 片段:批量创建节点与关系 from py2neo import Graph, Node, Relationship graph = Graph("http://localhost:7474", auth=("neo4j", "your_password")) # 创建英雄节点(去重) hero_node = Node("Hero", name="阿狸", difficulty=3, position=["法师","刺客"]) graph.create(hero_node) # 创建技能节点并建立 HAS_SKILL 关系 skill_node = Node("Skill", name="欺诈宝珠", type="Q", cooldown="8/7.5/7/6.5/6 秒") graph.create(skill_node) rel = Relationship(hero_node, "HAS_SKILL", skill_node) graph.create(rel) # 批量处理克制关系(注意:需双向创建,因“A克制B”不等于“B克制A”) for counter in hero_data.get("counter_heros", []): counter_node = graph.nodes.match("Hero", name=counter).first() if counter_node: counter_rel = Relationship(hero_node, "COUNTERS", counter_node) graph.create(counter_rel)

注意:COUNTERS关系是有向边,表示“当前英雄克制目标英雄”。若需支持“谁克制德莱文”类查询,必须确保所有克制对都按实际语义方向建边,不能仅靠MATCH (a:Hero)-[:COUNTERS]-(b:Hero)这种无向写法——这会导致结果包含被克制者。

2.3 中间数据清洗与 mid_data 目录的结构化转换

raw_data是原始 JSON,但直接入库易导致属性类型混乱(如difficulty字段可能为字符串"3"或数字3)。build_lol_graph.py在写入前会调用data/mid_data/下的清洗函数,将数据标准化为图数据库友好格式。例如:

  • position字段统一转为小写英文数组:["mage", "assassin"]
  • stats中的数值型字段强制int类型,缺失值设为0
  • skillseffect文本去除 HTML 标签与多余空格,保留纯文本

该清洗逻辑封装在mid_data/cleaner.py中,核心方法为:

def clean_hero_data(raw_json): cleaned = {} cleaned["name"] = raw_json.get("name", "").strip() cleaned["difficulty"] = int(raw_json.get("difficulty", 0)) cleaned["position"] = [p.lower().replace(" ", "") for p in raw_json.get("position", [])] cleaned["stats"] = { k: int(v) if isinstance(v, (str, int)) else 0 for k, v in raw_json.get("stats", {}).items() } cleaned["skills"] = [ { "name": s.get("name", "").strip(), "type": s.get("type", "").upper(), "effect": re.sub(r'<[^>]+>', '', s.get("effect", "")).strip() } for s in raw_json.get("skills", []) ] return cleaned

清洗后的数据存入mid_data/,作为build_lol_graph.py的输入源。这一步不可跳过——未经清洗的数据会导致 Cypher 查询时类型不匹配(如WHERE h.difficulty > 5对字符串"5"失效)。

3. 问答引擎实现:从自然语言问句到 Cypher 查询的端到端解析

3.1 问句分类器 question_classification.py 的特征工程

question_classification.py并非使用 BERT 微调,而是基于规则 + TF-IDF 的轻量级分类器,覆盖 5 类高频问题:

问题类型触发关键词示例问句对应 Cypher 模板
hero_skill“大招”、“技能”、“Q/W/E/R”“亚托克斯的大招叫什么?”MATCH (h:Hero {name:$hero})-[:HAS_SKILL]->(s:Skill {type:"R"}) RETURN s.name
hero_counter“克制”、“被谁克制”、“怎么打”“哪些英雄克制德莱文?”MATCH (h:Hero {name:$hero})-[:COUNTERS]->(c:Hero) RETURN c.name
hero_attribute“定位”、“难度”、“攻击”、“魔法”“阿狸的定位是什么?”MATCH (h:Hero {name:$hero}) RETURN h.position
skill_effect“效果”、“怎么用”、“造成什么伤害”“欺诈宝珠的效果是什么?”MATCH (h:Hero)-[:HAS_SKILL]->(s:Skill {name:$skill}) RETURN s.effect
item_build“出装”、“怎么合成”、“推荐装备”“卡萨丁怎么出装?”MATCH (h:Hero {name:$hero})-[:RECOMMENDS]->(i:Item) RETURN i.name

分类器训练数据来自data/train_questions.csv,每行含question,text,label。TF-IDF 向量化时,ngram_range=(1,2),即同时考虑单字和双字组合(如“大招”、“克制”、“定位”),并加入停用词过滤(["的", "什么", "叫", "是"])。训练代码关键片段:

# question_classification.py 片段:TF-IDF + LogisticRegression from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline # 自定义中文分词 + 停用词 def chinese_tokenizer(text): words = jieba.lcut(text) return [w for w in words if w not in stopwords] vectorizer = TfidfVectorizer( tokenizer=chinese_tokenizer, ngram_range=(1, 2), max_features=5000 ) classifier = Pipeline([ ('tfidf', vectorizer), ('clf', LogisticRegression()) ]) # 训练(X_train 为问句列表,y_train 为标签列表) classifier.fit(X_train, y_train)

提示:jieba.lcut返回精确模式分词结果,比cut更稳定;max_features=5000防止向量维度爆炸,实测在 200 条训练样本下准确率达 92.3%。

3.2 实体识别与问句解析:trie_tree.py 与 question_parser.py 协同机制

trie_tree.py构建英雄名、技能名、装备名的前缀树(Trie),用于快速匹配问句中的命名实体。例如问句“德莱文的大招是什么?”,Trie 树能 O(m) 时间内定位出“德莱文”(Hero)和“大招”(映射为type="R")。question_parser.py则负责将分类结果、实体识别结果、关键词匹配结果组装成结构化参数:

# question_parser.py 片段:解析问句并提取参数 def parse_question(question): # 步骤1:用 Trie 树提取候选实体 entities = trie_tree.search_entities(question) # 返回 [{"type":"Hero", "name":"德莱文"}, ...] # 步骤2:匹配关键词确定技能类型 skill_type = None if "大招" in question or "R" in question: skill_type = "R" elif "Q" in question or "技能" in question and "一" in question: skill_type = "Q" # 步骤3:根据分类结果组装参数字典 label = classifier.predict([question])[0] params = {"label": label} if entities: params["hero"] = entities[0]["name"] if entities[0]["type"] == "Hero" else None params["skill"] = entities[0]["name"] if entities[0]["type"] == "Skill" else None if skill_type: params["skill_type"] = skill_type return params # 示例输出:{'label': 'hero_skill', 'hero': '德莱文', 'skill_type': 'R'}

该解析结果直接传入answer_search.py,驱动后续 Cypher 查询。

3.3 Cypher 查询生成与 answer_search.py 的动态模板机制

answer_search.py不是硬编码 SQL,而是根据parse_question()输出的参数字典,动态拼接 Cypher 语句。其核心是预置的模板字典:

# answer_search.py 片段:Cypher 模板库 CYPHER_TEMPLATES = { "hero_skill": "MATCH (h:Hero {{name: $hero}})-[:HAS_SKILL]->(s:Skill {{type: $skill_type}}) RETURN s.name", "hero_counter": "MATCH (h:Hero {{name: $hero}})-[:COUNTERS]->(c:Hero) RETURN c.name", "hero_attribute": "MATCH (h:Hero {{name: $hero}}) RETURN h.{attr}", "skill_effect": "MATCH (h:Hero)-[:HAS_SKILL]->(s:Skill {{name: $skill}}) RETURN s.effect" } def generate_cypher(params): template = CYPHER_TEMPLATES.get(params["label"]) if not template: return None # 动态替换属性占位符(如 hero_attribute 模板中的 {attr}) if params["label"] == "hero_attribute": attr_map = {"定位": "position", "难度": "difficulty", "攻击": "stats.attack"} attr_key = next((k for k in attr_map if k in params.get("question", "")), "position") template = template.format(attr=attr_map.get(attr_key, "position")) return template # 执行查询 def search_answer(params): cypher = generate_cypher(params) if not cypher: return "未识别的问题类型" # 使用 py2neo 执行(注意:$hero 是参数化占位符,防注入) result = graph.run(cypher, **params).data() return [r[list(r.keys())[0]] for r in result] if result else ["暂无数据"]

注意:所有 Cypher 查询均使用$param参数化语法,而非 Python 字符串格式化(如f"MATCH ... {params['hero']}"),彻底规避 Cypher 注入风险。graph.run(cypher, **params)**params将字典键值自动映射为 Cypher 参数。

4. 本地运行与调试:从环境配置到典型问题排错全链路

4.1 依赖安装与 Neo4j 配置验证清单

本项目要求 Python 3.8+,依赖项明确列在requirements.txt中:

py2neo==2021.1.3 jieba==0.42.1 requests==2.31.0 beautifulsoup4==4.12.2 scikit-learn==1.2.2

安装命令:

pip install -r requirements.txt

Neo4j 必须为 4.x 版本(5.x 的 Bolt 协议变更会导致 py2neo 连接失败)。下载地址:https://neo4j.com/download-center/ (选择 “Neo4j Desktop” 或 “Community Edition 4.4.x”)。安装后关键配置检查项:

配置项位置正确值验证方式
HTTP 端口conf/neo4j.confdbms.connectors.default_listen_address=0.0.0.0
dbms.connectors.default_advertised_address=localhost
curl http://localhost:7474返回 HTML
认证开关conf/neo4j.confdbms.security.auth_enabled=true首次访问http://localhost:7474会提示输入账号密码
密码修改Neo4j Browser:set password输入新密码后,build_lol_graph.pyauth=("neo4j", "your_new_password")

提示:若build_lol_graph.py报错ServiceUnavailable: Cannot connect to ...,先检查 Neo4j 是否运行(ps aux | grep neo4j或任务管理器),再确认conf/neo4j.confdbms.connectors.default_listen_address是否为0.0.0.0(而非127.0.0.1)。

4.2 五步快速验证流程与典型错误日志对照表

按顺序执行以下 5 步,每步验证成功后再进行下一步:

  1. 爬取测试:运行python spider.py --test,检查data/raw_data/是否生成至少 3 个英雄 JSON 文件
  2. 清洗测试:运行python build_lol_graph.py --dry-run,输出应显示“已清洗 X 个英雄,Y 个技能”
  3. 建图测试:运行python build_lol_graph.py,终端输出“共创建 N 个节点,M 个关系”,Neo4j Browser 中执行MATCH (n) RETURN count(n)应 > 100
  4. 分类测试:运行python question_classification.py --test,输入“阿狸的定位是什么?”,输出应为hero_attribute
  5. 问答测试:运行python chatbot_graph.py,输入“亚托克斯的大招叫什么?”,应返回技能名称

常见错误与修复方案:

错误现象日志关键词根本原因解决方案
KeyError: 'name'build_lol_graph.py第 45 行raw_data/xxx.json中缺少name字段检查spider.py抓取逻辑,确保name字段必填;或在cleaner.py中添加默认值raw_json.get("name", "unknown")
py2neo.errors.ServiceUnavailable连接超时Neo4j 未启动或端口被占用sudo lsof -i :7474查看占用进程,kill -9 <PID>;或修改neo4j.confdbms.connector.http.listen_address=:7475
No such file or directory: 'data/train_questions.csv'question_classification.py训练数据文件缺失从项目根目录运行python -c "import pandas as pd; pd.DataFrame({'question':['test'],'label':['hero_skill']}).to_csv('data/train_questions.csv',index=False)"生成最小训练集
IndexError: list index out of rangequestion_parser.py第 32 行Trie 树未匹配到任何实体检查trie_tree.pybuild_trie()是否正确加载了data/hero_names.txt;确认问句中英雄名与文件中完全一致(如“德玛西亚皇子”不能简写为“皇子”)

4.3 问答效果优化:三个可立即生效的改进技巧

无需重写核心逻辑,仅修改配置文件即可提升实用性:

技巧1:扩展同义词映射表
data/synonym_dict.json中添加:

{ "大招": ["R", "终极技能", "终极"], "Q技能": ["Q", "1技能", "第一技能"], "克制": ["克制", "天敌", "打不过", "被压制"] }

修改question_parser.py,在关键词匹配前先做同义词替换:

def replace_synonyms(question): for src, targets in synonym_dict.items(): for tgt in targets: question = question.replace(tgt, src) return question

技巧2:增加模糊匹配容错
当 Trie 树未匹配到精确英雄名时,启用difflib.get_close_matches

from difflib import get_close_matches if not entities: all_heroes = [node["name"] for node in graph.run("MATCH (h:Hero) RETURN h.name").data()] candidates = get_close_matches(question, all_heroes, n=1, cutoff=0.6) if candidates: params["hero"] = candidates[0]

技巧3:缓存高频查询结果
answer_search.py中加入内存缓存(避免重复查询):

from functools import lru_cache @lru_cache(maxsize=128) def cached_search(cypher, **params): return graph.run(cypher, **params).data() def search_answer(params): cypher = generate_cypher(params) result = cached_search(cypher, **params) return [r[list(r.keys())[0]] for r in result] if result else ["暂无数据"]

这些技巧已在实际课程答辩中验证:同义词扩展使“亚托克斯的终极技能”识别率从 63% 提升至 98%;模糊匹配让“德玛西亚皇子”能响应“皇子”提问;缓存使连续提问响应时间从平均 320ms 降至 45ms。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 0:44:07

怎么为一个网站做外链避坑指南:新手实操全记录

怎么为一个网站做外链避坑指南:新手实操全记录 很多老板刚接触建站,心里发慌:自己不会代码,连服务器都没部署过,怕被外包坑,更怕网站做出来没人看。别急,这期不聊虚的,直接给一份接地气的 避坑指南 。很多新手以为建站就是花钱做个页面,其实真正难的是流量。而流量里,最容易被忽视、又最致命的就是“外链”。…

作者头像 李华
网站建设 2026/9/15 0:43:24

宠物医院预约挂号小程序实战:从微信登录到并发防超卖的完整方案

先说结论&#xff1a;这个项目是个标准的小程序业务系统&#xff0c;技术栈不复杂&#xff0c;但胜在业务链路完整&#xff0c;从微信登录、号源展示再到预约下单、订单管理&#xff0c;几乎把小程序开发里最常踩的坑都趟了一遍。如果你正准备做类似的预约类小程序&#xff0c;…

作者头像 李华
网站建设 2026/9/15 0:42:23

Flutter在OpenHarmony开发视力保护App实践

1. 项目背景与需求分析视力保护提醒App是当前数字健康领域的一个重要应用方向。随着人们每天面对电子屏幕的时间不断增加&#xff0c;用眼健康问题日益突出。医学研究表明&#xff0c;每20分钟让眼睛休息20秒&#xff0c;可以有效缓解视觉疲劳。然而在实际生活中&#xff0c;用…

作者头像 李华
网站建设 2026/9/15 0:41:58

国产CAD替代的硬件账:煤机厂换软件,内存SSD怎么升最划算

直接说结论&#xff1a;煤机厂要换国产工业CAD&#xff0c;决定成败的往往不是软件采购那笔钱&#xff0c;而是硬件账。我见过不止一个项目&#xff0c;License费、培训费、实施费都批了&#xff0c;结果一摸底发现&#xff0c;车间里三十台设计工作站还是五年前的配置——8GB内…

作者头像 李华
网站建设 2026/9/15 0:41:53

基于Rokid Glasses的AR远程协作系统开发实践

1. 为什么选择Rokid Glasses开发远程协作系统作为一名长期从事AR应用开发的工程师&#xff0c;我最初接触Rokid Glasses时就被它的轻量化设计和强大的计算能力所吸引。这款AR眼镜重量仅120克左右&#xff0c;却搭载了高通骁龙845处理器和4GB内存&#xff0c;完全能够满足实时视…

作者头像 李华
网站建设 2026/9/15 0:41:52

共享资源库:GIS资源管理与团队协作实战指南

做GIS、规划、测绘这行的朋友&#xff0c;电脑里多少都攒了一堆“宝贝”&#xff1a;自己调好的符号库、写好的字段计算脚本、处理数据的流程模板、甚至是磨合了几个月才定下来的制图配色方案。以前这些资源散落在各个项目文件夹里&#xff0c;换个电脑就得翻半天&#xff0c;同…

作者头像 李华