浏览器书签智能分类

news/2025/1/8 20:12:58/

浏览器书签智能分类工具

最近发现浏览器的书签越来越乱了,主要是因为自己太懒,其次之前建的分类太多又乱,重新手动整理确实比较烦。因此有了这个小项目。借助智谱AI的力量对书签进行重新分类

项目简介

本工具用于自动整理浏览器书签,通过AI智能分类技术,将杂乱的书签按照主题自动归类,并且重新生成结构化的书签文件。

功能特性

  • 自动提取书签信息
  • 智能分类书签内容
  • 分类智能生成
  • 生成HTML格式书签文件,可直接导入浏览器
  • 自动清理临时文件

使用说明

准备工作

  1. 获取智谱AI API密钥
  2. 准备浏览器导出的书签HTML文件

快速开始

  1. 下载脚本
  2. 修改CONFIG配置中的INPUT_FILEOUTPUT_FILE
  3. 修改CONFIG配置中的API_KEY
  4. 运行脚本:
    python 浏览器书签文件重新分类.py
    
  5. 查看生成的分类结果文件bookmarks.html

配置文件说明

在脚本开头的CONFIG字典中可配置以下参数:

  • API_KEY: 智谱AI API密钥(必填)
  • INPUT_FILE: 输入的书签HTML文件路径
  • EXTRACTED_FILE: 提取的临时文件路径
  • CLASSIFIED_FILE: 分类结果Markdown文件路径
  • OUTPUT_FILE: 最终输出的HTML书签文件路径
  • DEFAULT_CATEGORIES: 默认分类列表

代码结构

# 主要功能模块
1. 配置参数 (CONFIG)
2. AI客户端初始化 (zhipu_client)
3. 书签提取 (extract_bookmark_info)
4. 书签分类 (classify_bookmark)
5. 分类创建 (create_new_category)
6. 格式转换 (md_to_netscape)
7. 文件清理 (cleanup_temp_files)

脚本代码详细说明

浏览器书签文件重新分类脚本详细说明

1. 配置参数
CONFIG = {'API_KEY': "9e2458a8acf46274d1c1b9418eec500c.oBojpibVHt3IyXnz",  # 智谱API密钥,必填'INPUT_FILE': 'bookmarks_2025_1_3.html',  # 输入文件,必填'EXTRACTED_FILE': 'extracted_bookmarks.txt',  # 提取的临时文件'CLASSIFIED_FILE': 'classified_bookmarks.md',  # 分类结果文件'OUTPUT_FILE': 'bookmarks.html',  # 最终输出文件'DEFAULT_CATEGORIES': [  # 默认分类'编程语言','人工智能', '数据科学',]
}
  • API_KEY: 用于调用智谱AI API的密钥。
  • INPUT_FILE: 输入的HTML格式浏览器书签文件路径。
  • EXTRACTED_FILE: 提取的书签信息保存为文本文件的路径。
  • CLASSIFIED_FILE: 分类后的书签信息保存为Markdown文件的路径。
  • OUTPUT_FILE: 最终生成的HTML格式书签文件路径。
  • DEFAULT_CATEGORIES: 预定义的默认分类列表。
2. 初始化客户端
zhipu_client = ZhipuAI(api_key=CONFIG['API_KEY'])
  • 使用提供的API密钥初始化智谱AI客户端,用于后续与AI模型交互。
3. 获取AI响应
def get_llm_response(sysPrompt, questionPrompt):response = zhipu_client.chat.completions.create(model="GLM-4-Plus",  # 填写需要调用的模型编码messages=[{"role": "user", "content": sysPrompt},{"role": "assistant", "content": "你好,请告诉我你需要分类的书签?"},{"role": "user", "content": questionPrompt},],)return response.choices[0].message.content
  • 定义一个函数get_llm_response,用于向AI模型发送系统提示和用户问题,并返回模型的响应内容。
4. 读取并解析HTML文件
with open(CONFIG['INPUT_FILE'], 'r', encoding='utf-8') as file:html_content = file.read()soup = BeautifulSoup(html_content, 'html.parser')
bookmarks = soup.find_all('a')bookmark_info = []
for bookmark in bookmarks:href = bookmark.get('href')add_date = bookmark.get('add_date')icon = bookmark.get('icon')text = bookmark.text.strip().replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')bookmark_info.append({'URL': href,'Text': text})with open(CONFIG['EXTRACTED_FILE'], 'w', encoding='utf-8') as f:for info in bookmark_info:f.write(f"Name: {info['Text']}, URL: {info['URL']}\n")
  • 读取输入的HTML书签文件内容。
  • 使用BeautifulSoup解析HTML,提取所有的<a>标签作为书签。
  • 将每个书签的URL和名称保存到bookmark_info列表中。
  • 将提取的书签信息保存到临时文件EXTRACTED_FILE中。
5. 分类书签
def extract_bookmark_info(line):name_match = re.search(r'Name: ([^,]+)', line)url_match = re.search(r'URL: (https?://[^\s]+)', line)if not name_match or not url_match:return None, Nonereturn name_match.group(1), url_match.group(1)def classify_bookmark(bookmark_name, bookmark_url, categories):system_prompt = ("你是一个智能助手,任务是根据提供的书签名称和URL从给定的分类列表中选择最合适的分类返回给用户。仅返回分类名称。\n""请根据书签的内容和性质选择最适合的分类。如果没有合适的分类,请回答'无法分类'。\n""给定分类: {categories_str}\n""请选择最合适的分类。\n""示例:""user: 书签名称:Python官方文档\n""书签URL:https://docs.python.org/3/\n""assistant: 编程语言\n").format(categories_str=", ".join(categories))user_prompt = ("书签名称: {bookmark_name}\n""书签URL: {bookmark_url}\n").format(bookmark_name=bookmark_name, bookmark_url=bookmark_url)try:response = get_llm_response(system_prompt, user_prompt)except Exception as e:response = "其他"if response == "无法分类":return Nonereturn responsedef create_new_category(name, url):system_prompt = ("你是一个智能助手,任务是根据提供的书签名称和URL给定一个合适的书签分类。仅返回分类名称。\n""请根据书签的内容和性质给定分类。\n""示例:""user: 书签名称:Python官方文档\n""书签URL:https://docs.python.org/3/\n""assistant: 编程语言\n")user_prompt = ("书签名称: {name}\n""书签URL: {url}\n").format(name=name, url=url)response = get_llm_response(system_prompt, user_prompt)return response
  • extract_bookmark_info: 从临时文件中每行提取书签名称和URL。
  • classify_bookmark: 根据书签名称和URL,使用AI模型从预定义分类中选择最合适的分类。如果无法分类,则返回None
  • create_new_category: 如果书签无法归入现有分类,则创建新分类
6. 处理书签分类
categories = CONFIG['DEFAULT_CATEGORIES']with open(CONFIG['EXTRACTED_FILE'], 'r', encoding='utf-8') as f:bookmarks = f.readlines()classified = defaultdict(list)
for line in bookmarks:if not line.strip():continuename, url = extract_bookmark_info(line)if not name or not url:continuecategory = classify_bookmark(name, url, categories)if not category:category = create_new_category(name, url)categories.append(category)print(f'分类书签: {name} -> {category}')classified[category].append(line)with open(CONFIG['CLASSIFIED_FILE'], 'w', encoding='utf-8') as f:f.write('# 书签\n\n')for category, items in classified.items():f.write(f'## {category}\n')for item in items:name, url = extract_bookmark_info(item)if name and url:f.write(f'- [{name}]({url})\n')f.write('\n')
  • 从临时文件中读取所有书签信息。
  • 对每个书签进行分类,如果无法归入现有分类则创建新分类
  • 分类结果保存到Markdown文件CLASSIFIED_FILE中。
7. 分类结果转换为HTML书签文件
def md_to_netscape(md_content):html = '''<!DOCTYPE NETSCAPE-Bookmark-file-1><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"><TITLE>Bookmarks</TITLE><H1>Bookmarks</H1><DL><p>'''lines = md_content.split('\n')stack = []current_level = 0for line in lines:if line.startswith('#'):level = line.count('#')title = line.lstrip('#').strip()while stack and stack[-1] >= level:html += '</DL><p>\n'stack.pop()html += f'    <DT><H3 ADD_DATE="{int(datetime.now().timestamp())}" LAST_MODIFIED="{int(datetime.now().timestamp())}">{title}</H3>\n'html += '    <DL><p>\n'stack.append(level)current_level = levelelif line.startswith('-'):match = re.match(r'-\s*\[(.*?)\]\((.*?)\)', line)if match:name, url = match.groups()html += f'        <DT><A HREF="{url}" ADD_DATE="{int(datetime.now().timestamp())}">{name}</A>\n'while stack:html += '</DL><p>\n'stack.pop()return htmlwith open(CONFIG['CLASSIFIED_FILE'], 'r', encoding='utf-8') as f:md_content = f.read()html_content = md_to_netscape(md_content)with open(CONFIG['OUTPUT_FILE'], 'w', encoding='utf-8') as f:f.write(html_content)
  • 定义md_to_netscape函数,将Markdown格式的分类结果转换为Netscape Bookmark HTML格式。
  • 读取分类结果文件CLASSIFIED_FILE,将其转换为HTML格式并保存到OUTPUT_FILE
8. 清理临时文件
def cleanup_temp_files():temp_files = [CONFIG['EXTRACTED_FILE'],CONFIG['CLASSIFIED_FILE']]for file in temp_files:if os.path.exists(file):os.remove(file)print(f"已删除临时文件: {file}")if __name__ == '__main__':try:# 主逻辑代码...cleanup_temp_files()print("脚本执行完成,临时文件已清理")except Exception as e:print(f"脚本执行出错: {str(e)}")
  • 定义cleanup_temp_files函数,清理脚本运行过程中产生的临时文件。
  • 在主程序执行完成后调用此函数,确保临时文件被删除。

总结

该脚本通过以下步骤实现了对浏览器书签文件的重新分类

  1. 读取和解析:从HTML文件中提取书签信息。
  2. 分类:使用AI模型对书签进行分类,必要时创建新分类
  3. 保存结果:将分类结果保存为Markdown文件。
  4. 转换格式:将Markdown格式的分类结果转换为HTML格式。
  5. 清理:删除临时文件以保持工作环境整洁。

这个过程不仅简化了书签管理,还利用AI技术提高了分类的准确性和效率。

注意事项

  1. 确保API密钥有效
  2. 输入文件格式需为标准Netscape书签格式
  3. 分类结果可能受AI模型影响,建议人工复核
  4. 脚本运行后会清理临时文件,请及时保存结果

完整代码

# 浏览器书签文件重新分类.pyfrom zhipuai import ZhipuAI
from bs4 import BeautifulSoup
import re
import os
from collections import defaultdict
from datetime import datetime# 配置参数
CONFIG = {'API_KEY': "my-secret-key",  # 智谱API密钥,必填'INPUT_FILE': 'bookmarks_2025_1_3.html',  # 输入文件,必填'EXTRACTED_FILE': 'extracted_bookmarks.txt',  # 提取的临时文件'CLASSIFIED_FILE': 'classified_bookmarks.md',  # 分类结果文件'OUTPUT_FILE': 'bookmarks.html',  # 最终输出文件'DEFAULT_CATEGORIES': [  # 默认分类'编程语言',#这个被用到给AI举例,不能删除'人工智能', '数据科学',]
}# 初始化客户端
zhipu_client = ZhipuAI(api_key=CONFIG['API_KEY'])def get_llm_response(sysPrompt, questionPrompt):response = zhipu_client.chat.completions.create(model="GLM-4-Plus",  # 填写需要调用的模型编码messages = [{"role": "user", "content": sysPrompt},{"role": "assistant", "content": "你好,请告诉我你需要分类的书签?"},{"role": "user", "content": questionPrompt},],)return response.choices[0].message.content# 读取HTML文件内容
with open(CONFIG['INPUT_FILE'], 'r', encoding='utf-8') as file:html_content = file.read()# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')# 查找所有的<A>标签
bookmarks = soup.find_all('a')# 提取书签信息
bookmark_info = []
for bookmark in bookmarks:href = bookmark.get('href')add_date = bookmark.get('add_date')icon = bookmark.get('icon')text = bookmark.text.strip().replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')bookmark_info.append({'URL': href,'Text': text})# 打印提取的书签信息
for info in bookmark_info:print(info)# 保存提取的书签信息with open(CONFIG['EXTRACTED_FILE'], 'w', encoding='utf-8') as f:for info in bookmark_info:f.write(f"Name: {info['Text']}, URL: {info['URL']}\n")def extract_bookmark_info(line):"""从书签行中提取名称和URL"""name_match = re.search(r'Name: ([^,]+)', line)url_match = re.search(r'URL: (https?://[^\s]+)', line)if not name_match or not url_match:return None, Nonereturn name_match.group(1), url_match.group(1)def classify_bookmark(bookmark_name, bookmark_url, categories):system_prompt = ("你是一个智能助手,任务是根据提供的书签名称和URL从给定的分类列表中选择最合适的分类返回给用户。仅返回分类名称。\n""请根据书签的内容和性质选择最适合的分类。如果没有合适的分类,请回答'无法分类'。\n""给定分类: {categories_str}\n""请选择最合适的分类。\n""示例:""user: 书签名称:Python官方文档\n""书签URL:https://docs.python.org/3/\n""assistant: 编程语言\n").format(categories_str=", ".join(categories))user_prompt = ("书签名称: {bookmark_name}\n""书签URL: {bookmark_url}\n").format(bookmark_name=bookmark_name, bookmark_url=bookmark_url)try:response = get_llm_response(system_prompt, user_prompt)except Exception as e:response = "其他"if response == "无法分类":return Nonereturn responsedef create_new_category(name, url):"""创建新分类:param name: 书签名称:param url: 书签URL:return: 新分类名"""system_prompt = ("你是一个智能助手,任务是根据提供的书签名称和URL给定一个合适的书签分类。仅返回分类名称。\n""请根据书签的内容和性质给定分类。\n""示例:""user: 书签名称:Python官方文档\n""书签URL:https://docs.python.org/3/\n""assistant: 编程语言\n")user_prompt = ("书签名称: {name}\n""书签URL: {url}\n").format(name=name, url=url)response = get_llm_response(system_prompt, user_prompt)return response# 使用配置中的分类规则
categories = CONFIG['DEFAULT_CATEGORIES']with open(CONFIG['EXTRACTED_FILE'], 'r', encoding='utf-8') as f:bookmarks = f.readlines()classified = defaultdict(list)
for line in bookmarks:if not line.strip():continuename, url = extract_bookmark_info(line)if not name or not url:continue# 分类书签category = classify_bookmark(name, url, categories)if not category:# 创建新分类category = create_new_category(name, url)categories.append(category)print(f'分类书签: {name} -> {category}')classified[category].append(line)# 将分类结果保存为文件
with open(CONFIG['CLASSIFIED_FILE'], 'w', encoding='utf-8') as f:f.write('# 书签\n\n')for category, items in classified.items():f.write(f'## {category}\n')for item in items:name, url = extract_bookmark_info(item)if name and url:f.write(f'- [{name}]({url})\n')f.write('\n')from datetime import datetime
# 将分类结果重新转为HTML书签文件
def md_to_netscape(md_content):# 初始化HTML结构html = '''<!DOCTYPE NETSCAPE-Bookmark-file-1><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"><TITLE>Bookmarks</TITLE><H1>Bookmarks</H1><DL><p>'''# 解析MD内容lines = md_content.split('\n')stack = []current_level = 0for line in lines:# 处理标题if line.startswith('#'):level = line.count('#')title = line.lstrip('#').strip()# 关闭之前的DLwhile stack and stack[-1] >= level:html += '</DL><p>\n'stack.pop()# 添加H3标签html += f'    <DT><H3 ADD_DATE="{int(datetime.now().timestamp())}" LAST_MODIFIED="{int(datetime.now().timestamp())}">{title}</H3>\n'html += '    <DL><p>\n'stack.append(level)current_level = level# 处理链接elif line.startswith('-'):match = re.match(r'-\s*\[(.*?)\]\((.*?)\)', line)if match:name, url = match.groups()html += f'        <DT><A HREF="{url}" ADD_DATE="{int(datetime.now().timestamp())}">{name}</A>\n'# 关闭所有打开的DLwhile stack:html += '</DL><p>\n'stack.pop()return htmlwith open(CONFIG['CLASSIFIED_FILE'], 'r', encoding='utf-8') as f:md_content = f.read()html_content = md_to_netscape(md_content)with open(CONFIG['OUTPUT_FILE'], 'w', encoding='utf-8') as f:f.write(html_content)# 清理临时文件
def cleanup_temp_files():"""清理脚本运行过程中产生的临时文件"""temp_files = [CONFIG['EXTRACTED_FILE'],CONFIG['CLASSIFIED_FILE']]for file in temp_files:if os.path.exists(file):os.remove(file)print(f"已删除临时文件: {file}")# 主程序执行
if __name__ == '__main__':try:# 原有主逻辑代码...# 脚本执行完成后清理临时文件cleanup_temp_files()print("脚本执行完成,临时文件已清理")except Exception as e:print(f"脚本执行出错: {str(e)}")

http://www.ppmy.cn/news/1560630.html

相关文章

【ShuQiHere】算法的开枝散叶:从机器学习到深度学习的模型总结

【ShuQiHere】&#x1f680; 人工智能&#xff08;AI&#xff09;在过去十几年里经历了跨越式发展&#xff1a;从最初的计算机象征式推理到如今大行其道的深度学习&#xff0c;AI正逐步渗透到我们生活和工作的各个领域。然而&#xff0c;AI到底是如何一步步走到今天的&#xf…

AI数据标注师理论部分考试题库 - 500题

1.题目:职业道德是职场和行业内的特殊规范,这些规范是()在职业领域的具体实践。 A. 家庭道德 B. 社会道德 C. 仁义道德 D. 伦理道德 答案:B 解析:职业道德基于社会道德在职业场景的体现,与家庭、仁义、伦理道德概念不同,答案为 B。 2.题目:为了实现职业的健康发展,以…

python3中的字典推导式

一. 简介 前面简单学习了 python中的列表推导式&#xff0c;本文来简单学习一下 python中的字典推导式。 二. 字典推导式 python 中的字典推导式是 Python中创建字典的一种简洁方式。它允许你用一行代码来代替多行的 for循环和条件语句&#xff0c;从而快速地生成字典。 字典…

【LLM之评测】opencompass使用自定义接口与自定义数据集进行评测

&#x1f6e0;️ 安装指南 版本&#xff1a;0.3.7 下面提供了快速安装和数据集准备的步骤。 &#x1f4bb; 环境搭建 强烈建议使用 conda 来管理您的 Python 环境。 创建虚拟环境 conda create --name opencompass python3.10 -y conda activate opencompass通过pip安装Ope…

React-Router 一站式攻略:从入门到精通,掌握路由搭建与权限管控

文章目录 一、前言二、安装使用 npm 安装&#xff08;推荐&#xff09;使用 yarn 安装 三、基础使用设置路由基础结构定义路由和组件关联直接在组件中定义路由定义单独一个路由表 创建导航链接 四、核心组件和功能BrowserRouter 和 HashRouterRoute 组件Link 组件Switch 组件 五…

《大话设计模式》解读09-建造者模式

上篇文章,介绍了《大话设计模式》的第12章——外观模式。 本篇,来介绍《大话设计模式》的第13章——建造者模式。并通过python代码实现示例代码的功能。 1 建造者模式 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。…

Next.js 多语言 (1) | 中间件(Middleware)的设置与应用

当我们开发一个支持多语言的 Next.js 网站时&#xff0c;常常需要解决以下问题&#xff1a; 用户首次访问时&#xff0c;应该显示哪个语言版本&#xff1f; &#x1f914; 比如&#xff0c;用户访问 / 时&#xff0c;是展示 /en 还是 /de&#xff1f; SEO 是否能够抓取所有语言…

【JavaScript】变量-常量-数据类型-类型转换

目录 一、JavaScript 介绍 1. JavaScript &#xff08;是什么&#xff1f;&#xff09; 2. 作用&#xff08;做什么&#xff1f;&#xff09; 3. JavaScript的组成&#xff08;有什么&#xff1f;&#xff09; 3.1 ECMAScript: 3.2 Web APIs : 总结&#xff1a; 4. Jav…