需求
在整理笔记的过程中,许多人由于使用过多个笔记软件,常常会面临笔记重复或内容相似的问题。这些重复的笔记不仅难以查找,而且取舍起来也相当棘手。为了更高效地管理这些笔记,我计划定期抽出固定时间,对相似的笔记进行统一处理,剔除过时的内容,补充最新的信息。
然而,找出相似笔记本身就是一个繁琐的任务。为了解决这一问题,我开发了一个脚本,旨在自动识别并标记出内容相似的笔记,从而简化整理过程。通过这个工具,我希望能够更轻松地整合和优化我的笔记库,确保其始终保持整洁和更新。
步骤
在使用之前,你需要安装一些特定库:
pip install nltk scikit-learn
具体的代码如下:
import os
import re
import nltk
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 下载nltk数据包
nltk.download('punkt')
def read_md_files(folder_path, exclude_folders=None):
"""
递归读取文件夹及子文件夹中的所有Markdown文件内容(返回绝对路径)
:param folder_path: 根目录路径
:param exclude_folders: 需要排除的文件夹列表(相对路径或绝对路径)
:return: 字典,键为文件绝对路径,值为文件内容
"""
if exclude_folders is None:
exclude_folders = []
# 将排除文件夹转换为绝对路径
exclude_folders = [os.path.abspath(os.path.join(folder_path, f)) for f in exclude_folders]
md_contents = {}
for root, _, files in os.walk(folder_path):
# 检查当前目录是否在排除列表中
if any(exclude_folder in root for exclude_folder in exclude_folders):
print(f"跳过排除目录: {root}")
continue
for filename in files:
if filename.lower().endswith(".md"):
file_path = os.path.abspath(os.path.join(root, filename))
with open(file_path, 'r', encoding='utf-8') as file:
try:
content = preprocess(file.read())
md_contents[file_path] = content
except UnicodeDecodeError:
print(f"解码失败跳过文件: {file_path}")
return md_contents
def preprocess(text):
"""文本预处理函数(带Markdown清理)"""
# 移除Markdown语法元素
text = re.sub(r'[#*\-_`\[\]!]', '', text)
# 移除代码块
text = re.sub(r'```.*?```', '', text, flags=re.DOTALL)
# 移除HTML标签
text = re.sub(r'<.*?>', '', text)
# 转小写并去除多余空白
return text.lower().strip()
def batch_calculate_similarity(md_contents, threshold=0.8):
"""批量计算相似度并返回结果列表"""
file_paths = list(md_contents.keys())
contents = list(md_contents.values())
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(contents)
cos_sim = cosine_similarity(tfidf_matrix)
similar_pairs = []
for i in range(len(cos_sim)):
for j in range(i+1, len(cos_sim)):
if cos_sim[i][j] >= threshold:
similar_pairs.append((
file_paths[i],
file_paths[j],
round(cos_sim[i][j], 4)
))
return similar_pairs
def save_results(similar_pairs, output_file="similar_results.txt"):
"""保存结果到文本文件"""
with open(output_file, 'w', encoding='utf-8') as f:
f.write("file1,file2,similarity\n")
for pair in sorted(similar_pairs, key=lambda x: x[2], reverse=True):
f.write(f'"{pair[0]}","{pair[1]}",{pair[2]}\n')
print(f"\n结果已保存到 {os.path.abspath(output_file)}")
def main():
folder_path = "D:/库/笔记备份/" # 修改为你的实际路径
output_filename = "similar_notes.csv" # 输出文件名
# 需要排除的文件夹列表(相对路径或绝对路径)
exclude_folders = [
"archive", # 相对路径
"D:/库/笔记备份/旧笔记", # 绝对路径
]
print("正在扫描Markdown文件...")
md_contents = read_md_files(folder_path, exclude_folders)
if not md_contents:
print("未找到任何Markdown文件")
return
print(f"找到 {len(md_contents)} 个文件,开始计算相似度...")
similar_pairs = batch_calculate_similarity(md_contents)
if similar_pairs:
print(f"\n发现 {len(similar_pairs)} 对相似文档:")
for pair in sorted(similar_pairs, key=lambda x: x[2], reverse=True):
print(f"{pair[2]:.2%} | {os.path.basename(pair[0])} <-> {os.path.basename(pair[1])}")
save_results(similar_pairs, output_filename)
else:
print("没有找到相似度超过80%的文件对")
if __name__ == "__main__":
main()
使用说明
folder_path
变量用于需要查找的文件夹(子文件夹中的笔记也会进行遍历);- 为了避免一些特殊的文件和二进制文件,所以只遍历了md后缀的markdown文件(二进制文件对比起来非常耗时,其他文件可能包括了配置文件,于我个人没有实际意义;
- 当前设置的相似度为80%,如果需要修改,可以修改
def batch_calculate_similarity(md_contents, threshold=0.8)
中的0.8; - 结果输出在similar_notes.csv文件中,代码中变量为output_filename。
输出效果:
最后使用quicker动作 ever智识 快速打开对应笔记进行对照修改即可。