知识图谱实战(一):数据采集与实体关系抽取(完整代码)
从文档/网页里抽"实体和关系",拼成三元组,这是知识图谱的原材料。这篇讲清楚:数据从哪来、怎么把文字变成 (头实体, 关系, 尾实体),给一套完整可运行的抽取代码,并为 《知识图谱(二)Neo4j 构建》/《知识图谱(三)GraphRAG》的图谱构建与调用打底。
前言
上一篇 RAG 系列用向量检索解决"找相似内容",但遇到"任正非和华为是什么关系""华为的产品有哪些"这种精确关系问题,向量检索就力不从心了。知识图谱(Knowledge Graph)是另一种答案:把知识存成"实体—关系—实体"的图结构,用图查询精确回答关系型问题。
完整流程三步:采集数据 → 构建图谱(抽取三元组 + 存进图数据库)→ 调用查询。这篇做第一步:采集与抽取。
一、整体流程(先看地图)
数据采集(爬虫/公开数据) ↓ 清洗与预处理 ↓ 实体关系抽取(规则 / LLM 两种方式) ↓ 输出三元组(head, relation, tail) ↓ 【《知识图谱(二)Neo4j 构建》】Neo4j 建图 → 【《知识图谱(三)GraphRAG》】图查询 / GraphRAG知识图谱三要素:实体(Entity)——人、公司、地点等;关系(Relation)——“创始人”"总部位于"等;三元组——(华为, 创始人, 任正非)一条知识。
二、数据采集(来源与方法)
采集方式取决于数据在哪:
| 数据源 | 方式 | 参考 |
|---|---|---|
| 网页/新闻 | 爬虫(requests + BeautifulSoup) | 《爬虫实战》 |
| 本地文档/Excel | 直接读取(openpyxl / python-docx) | 《办公自动化》 |
| 公开 API | 接口直接拉取 | 《爬虫实战》 |
本文为了"开箱即跑",用一段内置的示例文本演示(真实采集请换成 《爬虫实战》的方法抓取你关心的领域)。
三、环境准备(先装依赖)
pip install requests beautifulsoup4 :: 词典分词可选:pip install jieba四、完整代码:规则式抽取(可运行)
保存为kg_extract.py:
# kg_extract.py — 从文本抽取三元组,输出 triples.csv(完整可运行)importreimportcsv# 1. 数据:实际项目中换成爬虫/文档采集结果(见 《爬虫实战》)docs=["华为技术有限公司成立于1987年,总部位于深圳。","任正非是华为的创始人。","华为生产手机、路由器、交换机等产品。","任正非出生于贵州。",]# 2. 关系规则:正则匹配(原理展示,规则简单清晰)RELATION_PATTERNS=[(r"(.+?)成立于(\d{4}年)","成立时间"),(r"(.+?)总部位于(.+?)","总部位于"),(r"(.+?)是(.+?)的创始人","创始人"),(r"(.+?)生产(.+?)、(.+?)、(.+?)等产品","生产"),]# 3. 抽取defextract_triples(text):triples=[]forpattern,relinRELATION_PATTERNS:m=re.search(pattern,text)ifm:groups=m.groups()head=groups[0]fortailingroups[1:]:triples.append((head,rel,tail))returntriples all_triples=[]fordocindocs:all_triples+=extract_triples(doc)# 4. 去重并输出 CSV(utf-8-sig 防 Excel 乱码)seen=set()rows=[]forh,r,tinall_triples:if(h,r,t)notinseen:seen.add((h,r,t))rows.append([h,r,t])withopen("triples.csv","w",newline="",encoding="utf-8-sig")asf:writer=csv.writer(f)writer.writerow(["head","relation","tail"])writer.writerows(rows)print("抽取到",len(rows),"条三元组:")forrinrows:print(" ",r)运行验证:python kg_extract.py,期望看到打印 5 条左右三元组(如['华为技术有限公司', '总部位于', '深圳']),并生成triples.csv(三列:head/relation/tail)——采集→抽取链路即通。
五、进阶:LLM 抽取(效果更好,直接可用)
规则式覆盖不了复杂句子,换成让大模型抽——本地 Ollama 或云端 API 都行:
# kg_extract_llm.py — LLM 抽取三元组(需本地 Ollama 或改 API)importjsonimportrequestsimportcsv OLLAMA_URL="http://localhost:11434/api/generate"# 本地 OllamaMODEL="qwen2.5:7b"defllm_extract(text):prompt=("你是知识图谱构建助手。从下面的文本中抽取所有三元组,""只输出 JSON 数组,格式:[{\"head\": \"实体\", \"relation\": \"关系\", \"tail\": \"实体\"}]\n"f"文本:{text}")resp=requests.post(OLLAMA_URL,json={"model":MODEL,"prompt":prompt,"stream":False},timeout=60)content=resp.json()["response"]# 提取 JSON 数组部分start=content.find("[")end=content.rfind("]")+1returnjson.loads(content[start:end])text="华为技术有限公司1987年成立于深圳,任正非是其创始人,主要生产手机、路由器等通信设备。"triples=llm_extract(text)print("LLM 抽取结果:")fortintriples:print(" ",t)withopen("triples_llm.csv","w",newline="",encoding="utf-8-sig")asf:w=csv.writer(f)w.writerow(["head","relation","tail"])w.writerows([[t["head"],t["relation"],t["tail"]]fortintriples])运行验证:启动 Ollama(ollama run qwen2.5:7b先拉模型),运行脚本,期望打印带关系的三元组 JSON,并生成triples_llm.csv。没装 Ollama 就把OLLAMA_URL换成任何 OpenAI 兼容接口。
六、常见坑
| 坑 | 解决 |
|---|---|
| 抽取结果重复/实体叫法不一 | 做实体归一化:同义实体映射表(“华为"→"华为技术有限公司”) |
| 规则式漏抽 | 规则覆盖常见句式即可,复杂句子交给 LLM 方式 |
| LLM 输出不是纯 JSON | 按示例截取[到]再 json.loads;或提示词里加"只输出 JSON" |
| 关系类别混乱 | 固定关系集合(创始人/总部位于/成立时间/生产/…),LLM 提示词里给出可选关系 |
| 数据量大抽得慢 | 分段抽取 + 多线程;或用批量接口 |
七、总结
- 一句话:知识图谱 = 三元组集合;三元组 = 采集数据 + 实体关系抽取;
- 这篇产出
triples.csv,正是 《知识图谱(二)Neo4j 构建》 Neo4j 建图的输入——三篇连起来就是完整流程; - 接单角度:企业知识图谱(人事档案、供应链、文档体系)第一步都是"数据抽取成三元组",这套代码是通用起点。
觉得有用点个赞收藏,下一篇《知识图谱(二):Neo4j 构建与导入》见。