本指南将使用开源工具栈构建一个轻量级、可复现的档案知识挖掘环境。所有工具均可免费获取。
在Ubuntu 20.04 LTS或CentOS 8服务器上执行以下命令安装基础环境。我们选择Python作为主要开发语言。
更新系统并安装Python 3.8+及pip:
``` sudo apt-get update && sudo apt-get upgrade -y sudo apt-get install -y python3.8 python3-pip python3.8-venv git ```创建并激活一个独立的Python虚拟环境,避免包冲突:
``` python3.8 -m venv archive_mining_env source archive_mining_env/bin/activate ```在虚拟环境中,使用以下命令一次性安装所有必需的Python库。我们将依赖库分为数据处理、文本挖掘和可视化三类。
创建一个名为requirements.txt的文件,内容如下:
``` 数据处理 pandas==1.4.2 numpy==1.22.3 openpyxl==3.0.10 用于处理Excel档案目录 python-docx==0.8.11 用于处理DOCX格式档案 PyPDF2==2.10.0 用于处理PDF格式档案 文本挖掘与NLP jieba==0.42.1 中文分词 snownlp==0.12.3 中文情感分析等 gensim==4.2.0 主题模型LDA scikit-learn==1.1.1 机器学习与文本向量化 transformers==4.21.0 预训练模型,如BERT torch==1.12.0 transformers的依赖 知识图谱与可视化 networkx==2.8.5 py2neo==2021.2.3 Neo4j图数据库操作 matplotlib==3.5.2 seaborn==0.11.2 ```执行安装命令:
``` pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ```原始档案数据通常为结构化目录与非结构化原文的混合体。预处理的目标是将其转化为机器可读的、干净的文本数据。
编写一个Python脚本extract_text.py,实现从PDF、DOCX、TXT等常见格式中提取纯文本。以下是核心函数:
``` import os from PyPDF2 import PdfReader from docx import Document def extract_text_from_file(file_path): """根据文件后缀名,调用不同方法提取文本""" text = "" _, ext = os.path.splitext(file_path) ext = ext.lower() try: if ext == '.pdf': reader = PdfReader(file_path) for page in reader.pages: text += page.extract_text() + "\n" elif ext == '.docx': doc = Document(file_path) for para in doc.paragraphs: text += para.text + "\n" elif ext == '.txt': with open(file_path, 'r', encoding='utf-8') as f: text = f.read() else: print(f"Unsupported file type: {ext}") except Exception as e: print(f"Error reading {file_path}: {e}") return text.strip() 批量处理示例 def batch_extract(archive_dir, output_file='corpus.txt'): """遍历目录,提取所有支持格式文件的文本,并合并输出""" all_texts = [] for root, dirs, files in os.walk(archive_dir): for file in files: if file.lower().endswith(('.pdf', '.docx', '.txt')): full_path = os.path.join(root, file) print(f"Processing: {full_path}") text = extract_text_from_file(full_path) if text: 在文本前加入文件名作为元数据 all_texts.append(f"FILENAME: {file}\n{text}\n{'='50}\n") 将所有文本写入一个文件,便于后续处理 with open(output_file, 'w', encoding='utf-8') as f: f.writelines(all_texts) print(f"All texts saved to {output_file}") ```运行脚本,指定你的档案文件夹路径:
``` python extract_text.py 在脚本内调用:batch_extract('/path/to/your/archive_folder') ```
提取的文本包含大量噪音(页眉、页脚、无关符号)。创建clean_and_segment.py进行清洗和中文分词。
``` import jieba import re def clean_text(text): """清洗单条文本""" 移除换行符和多余空格,合并为一个空格 text = re.sub(r'\s+', ' ', text) 移除电子邮件、URL等无关信息 text = re.sub(r'\S@\S\s?', '', text) text = re.sub(r'http\S+', '', text) 移除非中文字符、数字、标点(根据需求可保留数字和英文) 此处示例保留中文、数字、英文字母 text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text) return text.strip() def segment_text(text, use_stopwords=True): """对清洗后的文本进行分词""" 加载停用词表(需提前准备,可从https://github.com/goto456/stopwords下载) stopwords = set() if use_stopwords: try: with open('stopwords.txt', 'r', encoding='utf-8') as f: for line in f: stopwords.add(line.strip()) except FileNotFoundError: print("Stopwords file not found, proceeding without it.") 使用jieba进行精确模式分词 words = jieba.lcut(text) 过滤停用词和单字(可根据需要调整) filtered_words = [w for w in words if w not in stopwords and len(w) > 1] return filtered_words 主处理流程 def process_corpus(input_file='corpus.txt', output_file='segmented_data.json'): """处理整个语料库,输出结构化的分词结果""" import json processed_docs = [] with open(input_file, 'r', encoding='utf-8') as f: content = f.read() 根据之前插入的分隔符分割文档 raw_docs = content.split('='50) for doc in raw_docs: if not doc.strip(): continue 提取文件名和内容 lines = doc.strip().split('\n') filename = lines[0].replace('FILENAME: ', '') if lines[0].startswith('FILENAME') else 'Unknown' raw_text = '\n'.join(lines[1:]) cleaned = clean_text(raw_text) if cleaned: 只处理非空文本 words = segment_text(cleaned) processed_docs.append({ 'filename': filename, 'raw_text': raw_text[:200], 保存前200字符供参考 'cleaned_text': cleaned, 'segmented': words }) 保存为JSON格式,便于后续分析 with open(output_file, 'w', encoding='utf-8') as f: json.dump(processed_docs, f, ensure_ascii=False, indent=2) print(f"Processed {len(processed_docs)} documents. Saved to {output_file}.") ```运行清洗与分词脚本:
``` python clean_and_segment.py ```预处理完成后,我们进入知识挖掘阶段,主要实现关键词提取、主题发现和实体关系构建。
关键词是档案知识检索的基石。创建keyword_extraction.py,结合统计与图算法提取关键词。
``` from sklearn.feature_extraction.text import TfidfVectorizer import jieba.analyse def extract_keywords_tfidf(documents, top_k=10): """ 使用TF-IDF提取整个语料库的关键词。 documents: 列表,每个元素是一个文档的分词结果(字符串,词用空格连接) """ 将分词列表转换为空格连接的字符串 text_for_tfidf = [' '.join(doc) for doc in documents] vectorizer = TfidfVectorizer(max_features=1000) tfidf_matrix = vectorizer.fit_transform(text_for_tfidf) 获取特征词(词汇表) feature_names = vectorizer.get_feature_names_out() 计算所有文档中每个词的TF-IDF平均权重 avg_tfidf = tfidf_matrix.mean(axis=0).A1 建立词到权重的映射并排序 word_score = list(zip(feature_names, avg_tfidf)) word_score_sorted = sorted(word_score, key=lambda x: x[1], reverse=True) 返回top K个关键词 return [word for word, score in word_score_sorted[:top_k]] def extract_keywords_textrank(single_doc_text, top_k=5): """ 使用TextRank算法为单个文档提取关键词。 single_doc_text: 单个文档的原始文本字符串(清洗前或后均可) """ 使用jieba内置的TextRank实现 keywords = jieba.analyse.textrank(single_doc_text, topK=top_k, withWeight=False, allowPOS=('n','nr','ns','v','vn')) return keywords 应用示例 import json with open('segmented_data.json', 'r', encoding='utf-8') as f: data = json.load(f) 准备TF-IDF所需数据 all_segmented_docs = [item['segmented'] for item in data] 提取语料库级关键词 corpus_keywords = extract_keywords_tfidf(all_segmented_docs, top_k=15) print("语料库核心关键词(TF-IDF):", corpus_keywords) 为第一个文档提取TextRank关键词 if data: doc_text = data[0]['raw_text'] doc_keywords = extract_keywords_textrank(doc_text, top_k=5) print(f"文档 '{data[0]['filename']}' 的关键词(TextRank):", doc_keywords) ```主题模型能发现档案中隐含的议题。创建topic_modeling.py,使用Gensim库实现LDA。
``` from gensim import corpora, models import json def train_lda_model(segmented_data_file='segmented_data.json', num_topics=5): """训练LDA主题模型""" 加载分词数据 with open(segmented_data_file, 'r', encoding='utf-8') as f: data = json.load(f) documents = [item['segmented'] for item in data] 创建词典和文档-词袋表示 dictionary = corpora.Dictionary(documents) 过滤极端值,保留至少出现在2个文档中,且不超过50%文档的词 dictionary.filter_extremes(no_below=2, no_above=0.5) corpus = [dictionary.doc2bow(doc) for doc in documents] 训练LDA模型 lda_model = models.LdaModel(corpus=corpus, id2word=dictionary, num_topics=num_topics, random_state=42, passes=10, 迭代次数 alpha='auto') 自动学习主题分布 打印每个主题的前10个关键词 print("LDA发现的主题:") for idx, topic in lda_model.print_topics(-1, num_words=10): print(f"主题 {idx}: {topic}") 将主题分配给每个文档 for i, doc in enumerate(documents): doc_bow = dictionary.doc2bow(doc) topic_dist = lda_model.get_document_topics(doc_bow) 获取概率最高的主题 main_topic = max(topic_dist, key=lambda x: x[1]) if topic_dist else (0, 0) data[i]['dominant_topic'] = main_topic[0] data[i]['topic_prob'] = round(main_topic[1], 3) 保存带主题标签的数据 output_file = 'data_with_topics.json' with open(output_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"主题建模完成,结果已保存至 {output_file}") return lda_model, dictionary, corpus 运行训练 model, dict, corp = train_lda_model(num_topics=5) ```知识图谱能直观展示实体关系。我们使用Neo4j图数据库。安装并启动Neo4j社区版。
在Ubuntu上安装:
``` wget -O - https://debian.neo4j.com/neotechnology.gpg.key | sudo apt-key add - echo 'deb https://debian.neo4j.com stable latest' | sudo tee /etc/apt/sources.list.d/neo4j.list sudo apt-get update sudo apt-get install neo4j=1:4.4.11 sudo systemctl enable neo4j sudo systemctl start neo4j ```安装后,通过浏览器访问 http://localhost:7474,默认用户名和密码均为 neo4j,首次登录后会要求修改密码。
接下来,编写Python脚本build_knowledge_graph.py,将我们提取的实体和关系导入Neo4j。
``` from py2neo import Graph, Node, Relationship import json 连接到本地Neo4j数据库,替换为你修改后的密码 graph = Graph("bolt://localhost:7687", auth=("neo4j", "your_new_password")) def extract_simple_entities(text): """ 一个简单的基于规则的实体抽取示例。 在实际项目中,应使用NER模型(如BERT+CRF)。 此处假设档案中的人名包含“同志”、“先生”等后缀,机构名包含“局”、“委员会”等。 """ entities = {'PERSON': [], 'ORG': []} words = text.split() for i, word in enumerate(words): if word.endswith(('同志', '先生', '女士')): 将前面的词作为人名 if i > 0: entities['PERSON'].append(words[i-1] + word) elif word.endswith(('局', '委员会', '办公室', '部门')): entities['ORG'].append(word) return entities def build_graph_from_data(data_file='data_with_topics.json'): """从处理好的数据中构建知识图谱""" with open(data_file, 'r', encoding='utf-8') as f: data = json.load(f) 清空现有图(谨慎操作,仅用于示例) graph.delete_all() for doc in data[:50]: 先处理前50个文档作为演示 filename = doc['filename'] text_snippet = doc['raw_text'][:100] 取前100字符用于实体抽取 topic = doc.get('dominant_topic', -1) 创建“档案文档”节点 doc_node = Node("ArchiveDocument", filename=filename, topic=f"Topic_{topic}") graph.create(doc_node) 简单实体抽取 entities = extract_simple_entities(text_snippet) 创建实体节点并建立关系 for person in entities['PERSON']: person_node = Node("Person", name=person) graph.merge(person_node, "Person", "name") 合并同名节点 rel = Relationship(doc_node, "MENTIONS_PERSON", person_node) graph.create(rel) for org in entities['ORG']: org_node = Node("Organization", name=org) graph.merge(org_node, "Organization", "name") rel = Relationship(doc_node, "MENTIONS_ORG", org_node) graph.create(rel) 如果有关键词,也作为节点加入 if 'keywords' in doc: for kw in doc['keywords'][:3]: 取前3个关键词 kw_node = Node("Keyword", term=kw) graph.merge(kw_node, "Keyword", "term") rel = Relationship(doc_node, "HAS_KEYWORD", kw_node) graph.create(rel) print(f"知识图谱构建完成,已导入 {len(data[:50])} 个文档的实体信息。") 执行构建 build_graph_from_data() ```运行后,可在Neo4j浏览器中执行Cypher查询,例如:
``` MATCH (d:ArchiveDocument)-[:MENTIONS_PERSON]->(p:Person) RETURN d.filename, p.name LIMIT 10 ```将分析结果直观呈现,是知识挖掘价值传递的关键。