ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于requests+bs4+jieba+Neo4j的可问答知识图谱语料构建

基于requests+bs4+jieba+Neo4j的可问答知识图谱语料构建 简介本资源是一份面向计算机专业本科生的毕业设计项目实践材料聚焦知识图谱在自动问答系统语料库构建中的落地应用适用于学习图数据库建模、NLP预处理与结构化知识抽取的中阶开发者。压缩包共含若干核心文件具体数量未提供主体为Python爬虫脚本基于requestsbs4、中文分词与实体识别模块集成jieba、Neo4j图谱Schema定义及导入脚本辅以语料清洗与三元组生成逻辑完整覆盖从网页数据采集、文本结构化到图数据库存储的全流程。资源大小559KB轻量紧凑便于快速部署与调试已有1016人学习下载反映出其在毕设选题与知识图谱入门实践中的高参考价值。读者可直接复用爬虫模板、语料处理流程与Neo4j导入方案获得可运行的问答语料库构建骨架同时掌握实体关系抽取、图谱schema设计等关键工程能力。1. 这不是又一个“爬完就扔”的语料包它用requestsbs4爬得稳、jieba分得准、Neo4j存得清专为自动问答系统打磨的可复用知识图谱语料库很多毕设项目里的“语料库”实际是静态 CSV 或 JSON 文件字段模糊、关系缺失、无法支撑问答逻辑。而这个资源从源头就按知识图谱范式设计爬虫不只抓文本还同步提取实体类型人物/机构/事件、属性成立时间/所属领域/发生地点和显式关系“任职于”“隶属于”“导致”jieba不仅分词还结合停用词表与自定义词典做领域增强最终所有三元组都按(:Entity {name:, type:})-[:RELATION]-(:Entity)标准结构导入Neo4j。它解决的不是“有没有数据”而是“数据能否直接驱动 SPARQL 查询或 Cypher 模式匹配”——适合需要快速验证问答逻辑如“XX公司的创始人是谁”“哪些事件发生在2023年”的毕设、课程设计或轻量级知识服务原型。如果你正卡在“爬下来的数据没法进图数据库”或“分完词找不到主谓宾关系”这个包里每一步的参数、过滤条件和映射规则都已实测过。2. 爬虫层用requests控制请求节奏用bs4解析结构化 HTML规避429 Too Many Requests的核心策略构建高质量语料库的第一道关卡从来不是“能不能爬”而是“爬得是否可持续、是否符合目标站点结构”。本项目未使用 Selenium 或 Playwright全部基于requestsbs4实现原因很实际目标数据源如政府公开文件页、行业白皮书列表页、高校科研成果公示页均为静态 HTML动态渲染非必需而requests的可控性远高于浏览器自动化工具——尤其在应对反爬响应码时。2.1 请求策略设计从429 Too Many Requests错误反推服务器限流逻辑网络热词中高频出现的exceeded retry limit, last status: 429 too many requests并非偶然。该错误本质是服务端对客户端单位时间请求数的硬性限制。本项目通过三重机制规避请求头精细化模拟不仅设置User-Agent还注入Accept、Accept-Language、Referer等字段使请求更接近真实浏览器行为动态延迟与指数退避非固定time.sleep(1)而是根据响应状态码动态调整间隔会话复用与连接池管理避免重复建立 TCP 连接带来的额外开销与指纹暴露。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def create_session_with_retry(): session requests.Session() # 配置重试策略遇到 429、500、502、503、504 时重试最多 3 次 retry_strategy Retry( total3, status_forcelist[429, 500, 502, 503, 504], backoff_factor1, # 第一次重试延迟 1s第二次 2s第三次 4s allowed_methods[HEAD, GET, OPTIONS] ) adapter HTTPAdapter(max_retriesretry_strategy) session.mount(http://, adapter) session.mount(https://, adapter) return session # 使用示例 session create_session_with_retry() headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,image/apng,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Referer: https://www.example.gov.cn/ }提示backoff_factor1表示重试间隔按backoff_factor * (2^(retry_number - 1))计算即第1次重试等待1秒第2次等待2秒第3次等待4秒。这比线性等待更能适应服务端的限流窗口。2.2bs4解析关键定位结构化节点而非全文匹配提升鲁棒性目标页面常存在“标题-摘要-正文-附件”四段式结构。若用正则全文匹配一旦页面微调如div classcontent改为section idmain整个解析链路即断裂。本项目采用bs4的select()方法基于 CSS 选择器精准定位标题soup.select(h1.title, h2.article-title)发布时间soup.select(span.date, time.pub-date)正文段落soup.select(article p, .content p, #main-content p)关键实体锚点soup.select(a[href*/person/], a[href*org/])from bs4 import BeautifulSoup import re def parse_page(html_content): soup BeautifulSoup(html_content, lxml) # 提取标题优先取 h1无则取 h2 title_elem soup.select_one(h1, h2) title title_elem.get_text(stripTrue) if title_elem else # 提取发布时间匹配 2023年12月25日 或 2023-12-25 date_elem soup.select_one(span.date, time) date_text date_elem.get_text(stripTrue) if date_elem else date_match re.search(r(\d{4}[-年]\d{1,2}[-月]\d{1,2}日?), date_text) publish_date date_match.group(1) if date_match else None # 提取正文所有段落文本并过滤广告/导航文字 paragraphs [] for p in soup.select(article p, .content p): text p.get_text(stripTrue) if len(text) 20 and not any(kw in text for kw in [友情链接, 返回顶部, 联系我们]): paragraphs.append(text) return { title: title, publish_date: publish_date, content: \n.join(paragraphs) } # 调用示例 response session.get(https://www.example.gov.cn/news/12345.html, headersheaders, timeout10) if response.status_code 200: parsed parse_page(response.text) print(f标题{parsed[title]}, 段落数{len(parsed[content].split(。))})注意soup.select_one()返回第一个匹配元素比find()更符合 CSS 语义timeout10避免单个请求无限挂起len(text) 20过滤短文本如“点击查看详情”确保段落具备语义完整性。2.3 实体识别前置在爬虫阶段标记潜在实体减少 NLP 层压力传统流程是“先存全文再统一 NLP”但本项目在爬虫解析环节即进行轻量级实体标注所有a标签中href含/person/、/org/、/event/的视为命名实体候选记录其text和hrefstrong或em包裹的文本视为强调内容高概率为关键实体页面meta namekeywords中的逗号分隔值作为补充标签。这些信息随正文一并存入中间 JSON后续jieba处理时可直接加载为自定义词典大幅提升“中国石油大学北京”、“塔里木油田分公司”等长实体的识别准确率。字段来源示例值用途entity_candidatesa href/person/zhangsan张三/a[{name: 张三, type: person, url: /person/zhangsan}]初始化jieba自定义词典emphasized_textstrong钻井液密度/strong[钻井液密度]作为领域关键词加入停用词表反向过滤meta_keywordsmeta namekeywords content石油, 钻探, 安全规范[石油, 钻探, 安全规范]构建领域主题向量初始种子3. NLP 层jieba不是黑盒定制词典停用词依存分析补全让分词结果可直接映射到 Neo4j 节点爬虫获取的是原始 HTML 文本而知识图谱需要的是结构化的三元组主语-谓词-宾语。jieba在此环节承担“文本→结构”的桥梁作用但默认配置对专业术语、长实体、动宾关系识别严重不足。本项目通过三层增强使分词输出可直接用于 Cypher 创建语句生成。3.1 领域词典构建从爬取的entity_candidates动态生成jieba加载词典jieba默认词典覆盖通用词汇但对“涪陵页岩气田”“随钻测量系统LWD”等专业名词完全无感。本项目在爬虫阶段收集的entity_candidates经清洗后生成.txt词典文件格式为词语 词频 词性涪陵页岩气田 10000 n 随钻测量系统 5000 n LWD 3000 nz 塔里木油田分公司 8000 nt加载方式如下import jieba import jieba.posseg as pseg # 动态加载领域词典路径需替换为实际位置 jieba.load_userdict(data/dict/knowledge_graph_dict.txt) # 验证加载效果 text 涪陵页岩气田的LWD系统在塔里木油田分公司成功应用 words pseg.cut(text) for word, flag in words: print(f{word}/{flag}, end ) # 输出涪陵页岩气田/n LWD/nz 系统/n 在/p 塔里木油田分公司/nt 成功/ad 应用/vn提示jieba.posseg.cut()返回词性标注结果n名词、nz其他专有名词、nt机构名、v动词、vn名动词是构建三元组的关键依据。LWD被标为nz而非x未知证明词典生效。3.2 停用词表升级不止过滤“的了是”更剔除领域干扰项通用停用词表如哈工大停用词表会保留“钻机”“压裂”“套管”等关键术语但本项目停用词表包含两类特殊条目伪实体干扰词如“详见”“参见”“附件1”“图2所示”这些在 HTML 中高频出现但无实体意义关系弱动词如“进行”“开展”“实施”它们常与真实谓词如“钻探”“压裂”“固井”组合但单独出现时不构成有效关系。# 加载停用词表含领域定制 def load_stopwords(): stopwords set() with open(data/stopwords/industry_stopwords.txt, r, encodingutf-8) as f: for line in f: word line.strip() if word and not word.startswith(#): # 忽略注释行 stopwords.add(word) return stopwords STOPWORDS load_stopwords() def filter_words(words_pos_list): 过滤停用词并保留名词、动词、专有名词 filtered [] for word, pos in words_pos_list: if word.strip() and word not in STOPWORDS: if pos in [n, nz, nt, v, vn, vd]: # 保留实体与动作词 filtered.append((word, pos)) return filtered # 示例 text 公司开展了钻探作业并对压裂施工进行了技术评估 words_pos list(pseg.cut(text)) filtered filter_words(words_pos) print(filtered) # 输出[(公司, nt), (钻探, v), (作业, n), (压裂, v), (施工, n), (技术, n), (评估, n)]3.3 三元组抽取逻辑基于依存句法的主谓宾识别而非简单关键词匹配仅靠分词无法确定“中石化勘探分公司在塔里木盆地钻探了克深21井”中的主语、谓语、宾语。本项目采用规则启发式方法模拟依存分析主语紧邻动词前的nt机构或nz地名类名词谓语动词v或名动词vn且不在停用词表中宾语动词后的nz/nt/n且长度 ≥ 2 字过滤“井”“厂”等单字泛称。import re def extract_triples_from_sentence(sentence): words_pos list(pseg.cut(sentence)) filtered filter_words(words_pos) triples [] for i, (word, pos) in enumerate(filtered): if pos in [v, vn] and word not in [进行, 开展, 实施]: # 向前找主语最近的 nt/nz subject None for j in range(i-1, max(-1, i-5), -1): if filtered[j][1] in [nt, nz]: subject filtered[j][0] break # 向后找宾语最近的 nz/nt/n且非单字 obj None for j in range(i1, min(len(filtered), i6)): candidate, cand_pos filtered[j] if cand_pos in [nz, nt, n] and len(candidate) 2: obj candidate break if subject and obj: triples.append({ subject: subject, predicate: word, object: obj, sentence: sentence }) return triples # 测试 sent 中石化勘探分公司在塔里木盆地钻探了克深21井 triples extract_triples_from_sentence(sent) print(triples) # 输出[{subject: 中石化勘探分公司, predicate: 钻探, object: 克深21井, sentence: 中石化勘探分公司在塔里木盆地钻探了克深21井}]注意i-5和i6是经验性窗口长度覆盖 90% 的中文主谓宾距离len(candidate) 2过滤掉“井”“厂”“队”等无区分度单字避免生成(某公司)-[:钻探]-(井)这类无效边。4. 图谱层Neo4j数据模型设计与批量导入确保 Cypher 查询低延迟、高精度爬虫与 NLP 层产出的是扁平化的三元组列表而Neo4j要求明确的节点标签、关系类型与属性约束。本项目采用“实体-关系-实体”标准模型但针对问答场景做了三项关键优化节点去重合并、关系类型标准化、属性索引预置。4.1 节点模型(:Entity)统一标签 type属性支持多类型共存不同于为每类实体创建独立标签如(:Person),(:Organization)本项目采用单标签(:Entity)通过type属性区分类型。此举极大简化 Cypher 查询——无需UNION多个标签且便于未来扩展新类型如新增type: Regulation无需改 Schema。// 创建节点示例机构节点 CREATE (:Entity { name: 中国石油大学北京, type: Organization, source_url: https://www.cup.edu.cn/, crawl_time: 2024-03-15T10:22:33 }) // 创建节点示例事件节点 CREATE (:Entity { name: 克深21井完钻, type: Event, date: 2023-08-12, location: 塔里木盆地, source_url: https://www.example.gov.cn/news/12345.html })提示source_url和crawl_time是必填属性用于溯源与增量更新type值限定为预定义枚举Organization,Person,Event,Location,Equipment,Technology避免脏数据污染。4.2 关系模型动词标准化映射表将口语化谓词转为规范关系类型NLP 抽取的谓词如“钻探了”“开展了”“隶属于”需映射为标准关系类型。本项目内置映射表predicate_mapping.json{ 钻探: DRILLED, 压裂: FRACTURED, 固井: CEMENTED, 隶属于: BELONGS_TO, 任职于: WORKS_AT, 研发: DEVELOPED, 应用: APPLIED_IN }导入脚本中调用该映射import json with open(data/mapping/predicate_mapping.json, r, encodingutf-8) as f: PREDICATE_MAP json.load(f) def get_standard_relation(predicate): 将原始谓词映射为标准关系类型 return PREDICATE_MAP.get(predicate.strip(了过着), predicate.upper().replace( , _)) # 示例 print(get_standard_relation(钻探了)) # DRILLED print(get_standard_relation(隶属于)) # BELONGS_TO print(get_standard_relation(AI研发)) # DEVELOPED4.3 批量导入与索引用neo4j-admin import替代 Cypher10 倍提速对万级三元组逐条CREATE语句导入极慢。本项目采用neo4j-admin import工具需准备 CSV 文件nodes.csvid:ID,name,:LABEL,type,source_url,crawl_timerels.csv:START_ID,:END_ID,:TYPE,original_predicate,sentence# 生成 CSV 后执行导入需 Neo4j 停止运行 neo4j-admin import \ --nodesimport/nodes.csv \ --relationshipsimport/rels.csv \ --ignore-missing-nodestrue \ --ignore-duplicate-nodestrue \ --id-typeSTRING导入完成后立即创建索引// 为高频查询字段创建索引 CREATE INDEX entity_name_index ON :Entity(name); CREATE INDEX entity_type_index ON :Entity(type); CREATE INDEX entity_source_index ON :Entity(source_url);注意--ignore-duplicate-nodestrue确保同一实体多次爬取不产生重复节点--id-typeSTRING匹配 CSV 中的字符串 ID如 URL 的哈希值索引必须在导入后手动创建否则MATCH (e:Entity {name: XXX})查询将全表扫描。5. 问答验证技巧用 3 类 Cypher 查询模板快速检验语料库是否真正支撑自动问答语料库是否“可用”最终要落在具体问题能否被回答。本项目提供三类高频问答场景的 Cypher 模板无需额外开发直接在 Neo4j Browser 中运行即可验证数据质量。5.1 单跳关系查询验证“谁-做什么-什么”基础事实这是最基础的问答类型对应subject-predicate-object三元组。模板中?为待替换变量// 模板[主体] 的 [关系] 是什么 MATCH (s:Entity {name: 中石化勘探分公司})-[r:DRILLED]-(o:Entity) RETURN o.name AS object, r.original_predicate AS relation // 模板[关系] [客体] 的主体有哪些 MATCH (s:Entity)-[r:BELONGS_TO]-(o:Entity {name: 塔里木油田分公司}) RETURN s.name AS subject, r.original_predicate AS relation提示r.original_predicate保留原始谓词如“隶属于”便于核对 NLP 抽取准确性若返回空结果检查nodes.csv中中石化勘探分公司是否因空格/括号差异未匹配。5.2 多跳路径查询验证“间接关系”推理能力自动问答常需跨两步推理如“克深21井的钻探方的上级机构是”// 模板A 的 B 的 C 是 MATCH (a:Entity {name: 克深21井})-[:DRILLED]-(b:Entity)-[:BELONGS_TO]-(c:Entity) RETURN a.name AS well, b.name AS driller, c.name AS parent_org // 模板与 A 有相同 C 关系的所有 B MATCH (a:Entity {name: 塔里木盆地})-[:LOCATION]-(e:Entity)-[:DRILLED]-(w:Entity) RETURN DISTINCT w.name AS well, e.name AS event注意-[:LOCATION]-表示反向关系需确保rels.csv中已写入该方向DISTINCT避免同一口井因多个事件重复出现。5.3 属性约束查询验证时间、地点等限定条件是否生效真实问答常带条件如“2023年在塔里木盆地钻探的井有哪些”// 模板满足 [属性] 条件的 [关系] 结果 MATCH (e:Entity)-[r:DRILLED]-(w:Entity) WHERE e.type Event AND e.date STARTS WITH 2023 AND e.location CONTAINS 塔里木 RETURN w.name AS well, e.name AS event, e.date AS date // 模板按 [属性] 排序的 Top-N 结果 MATCH (o:Entity)-[r:APPLIED_IN]-(t:Entity) WHERE t.type Technology AND t.name CONTAINS 智能 RETURN o.name AS organization, t.name AS technology, count(*) AS freq ORDER BY freq DESC LIMIT 5提示STARTS WITH 2023比e.date 2023更健壮适配 2023-08-12 格式CONTAINS支持子串匹配比精确匹配更实用count(*)可统计某技术被多少机构应用体现热度。当这三类查询均能稳定返回合理结果且响应时间 500ms本地 SSD 环境即可确认该语料库已具备支撑自动问答系统的数据基础——它不再是“能跑通的 Demo”而是“可交付的语料资产”。本文还有配套的精品资源点击获取
返回列表