怎么把知乎里的好答案原封不动的保存下来?
后面给出了现成的程序,直接保存下来,就能用。
而且可以保存收藏夹。
核心思路:
1.让ai写一个把选中文本保存为md格式的程序。
2.再让ai写一个autohotkey脚本,来设置快捷键调用这个程序。
之后选中内容,按下快捷键,就自动保存了。
3.md格式可以用obsidian打开,里面可以装自动保存图片的插件。
(下面有程序说明,还有不懂的直接问ai就好)
说明:
1.这个方案的好处是,简单稳定。基本不依赖任何外在的插件程序了。
2.可以随意修改,包括标题格式,内容格式等等。 我目前的内容格式是,一部分是源代码,另一部分是处理后的简单的md格式。 另外甚至可以加ai自动总结放在最前面(这个我去掉了,如果需要可以让ai帮你写一个)。
代码1,python程序
import win32clipboard
import time
import os
from datetime import datetime
import html2text
from bs4 import BeautifulSoup
import re
from html.parser import HTMLParser
import sys
import inspect
ROOT_PATH = r"D:\剪藏2024-6-20\clipper"
class ContentProcessor:
"""Base class for content processing"""
def __init__(self, root_path):
self.root_path = root_path
def sanitize_filename(self, filename):
invalid_chars = r'[\\/*?:"<>|]'
return re.sub(invalid_chars, "_", filename)
def get_first_line(self, text, max_chars=70):
lines = text.split('\n')
first_line = ""
for line in lines:
stripped_line = line.strip().strip('```')
if not stripped_line:
continue
if "SourceURL" in line:
continue
if re.match(r'^[A-Za-z0-9-]+$', stripped_line):
continue
first_line = line.strip()
break
first_line = first_line.lstrip('#').strip()
match = re.search(r'^\[([^\]]+)\]', first_line)
if match:
first_line = match.group(1)
return first_line[:max_chars] if len(first_line) > max_chars else first_line
def remove_specific_lines(self, text):
keywords = ["Version:", "StartHTML:", "EndHTML:", "StartFragment:", "EndFragment:"]
lines = text.split('\n')
source_urls = [line for line in lines if "SourceURL:" in line]
filtered_lines = [line for line in lines if not any(keyword in line for keyword in keywords)]
return '\n'.join(filtered_lines), source_urls
def remove_unwanted_newlines(self, text):
pattern = r'(?<![\.\!\?。!?\s])\n\s*(?!\n)'
text=re.sub(pattern, ' ', text)
return text
# 通用。对每个文本都处理 2025-4-15 17:40:16
def remove_unwanted_newlines_normal(self, text):
pattern_newline_after_link=r'\)\n'
text=re.sub(pattern_newline_after_link, ')', text)
pattern_square_bracket_newline = r'\[\s*\n'
text = re.sub(pattern_square_bracket_newline, '[', text)
return text
def convert_html_to_md(self, html_content):
h = html2text.HTML2Text()
return self.remove_unwanted_newlines_normal(h.handle(html_content))
# return h.handle(html_content)
def determine_source_type(self, source_urls):
if not source_urls or not isinstance(source_urls, list) or not isinstance(source_urls[0], str):
# return None
return "other"
first_url = source_urls[0].lower()
source_mappings = {
"ai_chat": {"chatbox", "yuanbao.tencent.com", "deepseek.com", "qwenlm.ai", "moonshot.cn", "qwen.ai","aistudio.google.com","gemini.google.com","chatgpt.com"},
"zhihucollection": {"zhihu.com/collection"},
"zhihu": {"zhihu.com"},
"weibo": {"weibo.com"},
"xhs": {"xiaohongshu.com"},
"wx": {"weixin.qq.com"},
}
for source_type, keywords in source_mappings.items():
if any(re.search(r'\b' + re.escape(keyword) + r'\b', first_url) for keyword in keywords):
return source_type
return "other"
def process_content(self, html_content, need_summary=True):
html_data, source_urls = self.remove_specific_lines(html_content)
source_type = self.determine_source_type(source_urls)
md_data = self.convert_html_to_md(html_data)
summary = ''
if need_summary:
try:
# summary = summarize_message(md_data, source_type, self.ai_model_name)
summary =''
except:
pass
return {
'html_data': html_data,
'md_data': md_data,
'summary': summary,
'source_type': source_type,
'source_urls': source_urls
}
def save_to_file(self, processed_data, custom_path=None):
summary = processed_data['summary']
html_data = processed_data['html_data']
md_data = processed_data['md_data']
source_type = processed_data['source_type']
source_urls = processed_data['source_urls']
summary_first_line = self.sanitize_filename(self.get_first_line(summary))
first_line = self.sanitize_filename(self.get_first_line(md_data))
file_path, output_data = self.generate_file_path(
source_type, first_line, summary, html_data, md_data, summary_first_line, custom_path
)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
f.write(output_data)
print(f"成功保存到 {file_path}")
def generate_file_path(self, source_type, first_line, summary, html_data, md_data, summary_first_line, custom_path=None):
"""To be implemented by subclasses"""
raise NotImplementedError
class GeneralContentProcessor(ContentProcessor):
"""Processes general web content"""
def __init__(self, root_path=ROOT_PATH, ai_model_name=''):
super().__init__(root_path)
self.ai_model_name = ai_model_name
def generate_file_path(self, source_type, first_line, summary, html_data, md_data, summary_first_line, custom_path=None):
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
if source_type=='other':
output_data = summary + '\n\n' + html_data + '\n' + self.remove_unwanted_newlines(md_data)
else:
output_data = summary + '\n\n' + html_data + '\n' + md_data
if source_type == 'ai_chat':
filename = fr"【ai问答】{first_line}{timestamp}.md"
path = os.path.join(self.root_path, 'ai问答') if not custom_path else custom_path
elif source_type == 'other':
filename = fr"{first_line}{timestamp}.md"
path = self.root_path if not custom_path else custom_path
elif source_type == 'weibo':
# 微博来源
source_mark = 'weibo'
filename = fr"【{source_mark}】{summary_first_line}-{first_line}{timestamp}.md"
path = self.root_path
else:
if len(first_line) < 15:
filename = fr"【{source_type}】{first_line}-{summary_first_line}{timestamp}.md"
else:
filename = fr"【{source_type}】{first_line}{timestamp}.md"
path = self.root_path if not custom_path else custom_path
return os.path.join(path, filename), output_data
class ZhihuContentProcessor(ContentProcessor):
"""Processes Zhihu collection content"""
def __init__(self, root_path=ROOT_PATH,ai_model_name=''):
super().__init__(root_path)
self.ai_model_name = ai_model_name
def get_zhihu_title(self, html_content):
try:
if not html_content or not isinstance(html_content, str):
return ""
soup = BeautifulSoup(html_content, 'html.parser')
title_tag = soup.find(['h2', 'h1'])
return title_tag.text.strip() if title_tag else ""
except Exception as e:
print(f"Error parsing HTML: {e}")
return ""
def get_zhihu_author(self, html_content):
soup = BeautifulSoup(html_content, 'html.parser')
author_tag = soup.find('div', class_='AuthorInfo-head') or soup.find('a', class_='UserLink-link')
return author_tag.text.strip() if author_tag and author_tag.text.strip() else ""
def generate_file_path(self, source_type, first_line, summary, html_data, md_data, summary_first_line, custom_path=None):
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
output_data = summary + '\n\n' + html_data + '\n' + md_data
# 知乎来源
source_mark = '知乎'
title = self.get_zhihu_title(html_data) # 假设有一个函数 get_zhihu_title
author = self.get_zhihu_author(html_data) # 假设有一个函数 get_zhihu_author
if title:
filename = fr"【{source_mark}】{title}-{author}{timestamp}.md"
else:
filename = fr"【{source_mark}】{summary_first_line}-{author}{timestamp}.md"
filename=self.sanitize_filename(filename)
file_path = os.path.join(self.root_path, filename)
return file_path, output_data
class ZhihuCollectionContentProcessor(ZhihuContentProcessor):
"""Processes Zhihu collection content"""
def __init__(self, root_path=os.path.join(ROOT_PATH, '知乎收藏夹')):
super().__init__(root_path)
def get_zhihu_collection_divs(self, html_content):
soup = BeautifulSoup(html_content, 'html.parser')
specific_divs = soup.find_all('div', class_='jsNavigable CollectionDetailPageItem css-vurnku')
return specific_divs if specific_divs else soup.find_all('div', class_='List-item')
def process_collection(self, html_content):
collection_divs = self.get_zhihu_collection_divs(html_content)
need_summary = len(collection_divs) == 1
for div in collection_divs:
html_data = "SourceURL:https://www.zhihu.com/" + '\n' + str(div)
processed_data = self.process_content(html_data, need_summary)
self.save_to_file(processed_data)
return len(collection_divs)
class ClipboardManager:
"""Manages clipboard operations"""
@staticmethod
def get_clipboard_html():
try:
win32clipboard.OpenClipboard()
html_format = win32clipboard.RegisterClipboardFormat("HTML Format")
if win32clipboard.IsClipboardFormatAvailable(html_format):
return win32clipboard.GetClipboardData(html_format).decode('utf-8')
return None
except Exception as e:
print(f"发生错误: {str(e)}")
return None
finally:
try:
win32clipboard.CloseClipboard()
except:
pass
def get_all_processors():
processors = {}
# 获取当前模块中所有对象
for name, obj in globals().items():
# 检查是否是类,并且继承自 ContentProcessor
if inspect.isclass(obj) and issubclass(obj, ContentProcessor) and obj is not ContentProcessor:
# 获取类名,去掉 "ContentProcessor" 后缀
key = name.replace("ContentProcessor", "").lower()
processors[key] = obj() # 实例化类并加入字典
return processors
def main():
# 初始化所有处理器
processors = get_all_processors()
general_processor = GeneralContentProcessor(ROOT_PATH)
html_content = ClipboardManager.get_clipboard_html()
if not html_content:
print("剪贴板中没有HTML格式内容")
return
# 3. 分析内容来源类型,选择对应的处理器
_, source_urls = general_processor.remove_specific_lines(html_content)
source_type = general_processor.determine_source_type(source_urls)
# 根据source_type选择处理器
# "根据 source_type 从 processors 字典中获取对应的处理器,如果找不到匹配的处理器,就默认使用 general_processor"。
processor = processors.get(source_type, general_processor)
if source_type == 'zhihucollection':
# 1. 首先尝试处理为知乎收藏夹内容 processors['zhihucollection']
zhihu_count = processor.process_collection(html_content)
# if zhihu_count > 0:
# return # 如果是知乎收藏夹内容,处理完成后直接返回
# 处理内容并保存
processed_data = processor.process_content(html_content)
processor.save_to_file(processed_data)
if __name__ == "__main__":
main()
说明:
1.这个是你文件想保存的路径:ROOT_PATH = r"D:\剪藏2024-6-20\clipper"
保存收藏夹的说明:
2.这个程序,除了保存选中的单个回答,可以全选你的收藏夹页面,用同样的方式保存。
但是需要先展开收藏夹页面的回答: 可以用这个代码,保存为书签栏,点击一下自动展开
javascript:setInterval(function(){ let buttonElements2=document.querySelectorAll('.Button.ContentItem\\-more.Button\\-\\-plain');for(var element of buttonElements2){element.click()};},100);void(0);2.autohotkey程序
^q:: ; Ctrl+Q快捷键
Send, ^c ; 发送Ctrl+C进行复制
Sleep, 200 ; 等待100毫秒,确保复制操作完成
Run, pythonw.exe d:\auto_clipper_module.py ; 运行Python文件
return
说明:
- 这个改成你python文件保存的路径: d:\auto_clipper_module.py
AutoHotkey 脚本安装与使用说明
这个脚本的作用是将 Ctrl+Q 设置为快捷键,一键完成“复制内容 + 运行Python脚本处理”的自动化流程。以下是详细的安装和使用步骤:
一、 环境准备
- 安装 AutoHotkey 软件
- 如果尚未安装,请前往 AutoHotkey 官网 下载并安装 v1.1 或 v2.0 版本(你提供的代码语法兼容 v1.1,建议安装 v1.1 版本以保证完美兼容)。
- 安装完成后,电脑中
.ahk后缀的文件将显示为 AutoHotkey 图标。
- 准备 Python 脚本
- 确保你已经保存了上一段对话中的 Python 代码。
- 关键步骤:根据你提供的 AHK 代码,Python 脚本必须命名为
auto_clipper_module.py并且必须放在D:\盘根目录下(即D:\auto_clipper_module.py)。 - 如果你的 Python 脚本位置或名称不同,请修改下方 AHK 代码中的路径。
二、 脚本安装步骤
- 创建脚本文件
- 在桌面或任意文件夹空白处,右键 -> 新建 -> AutoHotkey Script (如果没有此选项,新建文本文档也可)。
- 将文件重命名为
QuickClipper.ahk(名字可自定义,后缀必须是.ahk)。
- 编辑代码
- 右键点击该文件,选择 Edit Script (或使用记事本打开)。
- 将以下代码复制并粘贴进去(已为你修正注释中的时间描述):
^q:: ; Ctrl+Q 快捷键 Send, ^c ; 发送 Ctrl+C 进行复制 Sleep, 200 ; 等待 200 毫秒,确保复制操作完成 Run, pythonw.exe d:\auto_clipper_module.py ; 静默运行 Python 文件(无黑框) return
- 保存并运行
- 保存文件并关闭编辑器。
- 双击
QuickClipper.ahk文件运行。 - 如果在任务栏右下角出现绿色的 “H” 图标,说明脚本已开始后台监听。
三、 使用方法
- 选中内容:在浏览器、PDF 或文档中选中你想要保存的文字或网页区域。
- 按下快捷键:按下键盘上的 Ctrl + Q。
- 自动执行:
- 脚本会自动模拟按下 Ctrl+C(复制)。
- 等待 0.2 秒。
- 后台运行 Python 脚本处理剪贴板内容并保存文件。
- 查看结果:前往
D:\剪藏2024-6-20\clipper目录查看生成的 Markdown 文件。
四、 进阶设置(开机自启)
如果你希望电脑开机后自动运行这个快捷键脚本: - 选中你的
QuickClipper.ahk文件。 - 按
Ctrl+C复制。 - 按
Win+R打开运行窗口,输入shell:startup并回车。 - 在打开的文件夹中按
Ctrl+V粘贴快捷方式即可。
五、 常见问题
- Q: 按下快捷键没反应?
- 检查任务栏是否有 AutoHotkey 图标。如果没有,说明脚本未启动。
- 检查 Python 脚本路径是否正确。代码中写的是
d:\auto_clipper_module.py,如果你的脚本在别的文件夹,请修改 AHK 代码中的路径。
- Q: 为什么用
pythonw.exe而不是python.exe? pythonw.exe运行时不会弹出黑色的命令行窗口(CMD),体验更流畅。如果需要调试错误,可以临时改回python.exe以查看报错信息。
详细使用说明(ai撰写)
功能概述
此脚本用于自动处理剪贴板中的HTML格式内容,根据内容来源(如知乎、微博、微信、AI对话等)进行分类处理,转换为Markdown格式并保存到本地文件。支持以下功能:
- 自动识别来源:通过URL关键词识别内容来源(如知乎、微博等)。
- 格式转换:将HTML内容转换为Markdown格式。
- 文件命名:根据内容来源、标题、时间戳等自动生成文件名。
- 分类存储:不同来源的内容保存到对应子目录(如AI问答内容保存到
ai问答目录)。 - 特殊处理:对知乎收藏夹内容进行批量处理。
运行环境要求
- 操作系统:Windows(依赖
win32clipboard模块)。 - Python版本:Python 3.x。
安装依赖
在运行前,需安装以下依赖库:
pip install pywin32 html2text beautifulsoup4使用步骤
- 复制内容:在浏览器或其他应用中复制需要保存的内容(需复制为HTML格式)。
- 按快捷键ctrl+q
- 查看结果:
- 处理后的文件将保存到
ROOT_PATH指定的目录(默认为D:\剪藏2024-6-20\clipper)。 - 不同来源的内容会保存到对应子目录(如
ai问答、知乎收藏夹)。
配置修改
如需修改默认存储路径,可编辑脚本中的ROOT_PATH变量:
ROOT_PATH = r"D:\你的自定义路径\clipper"支持的来源类型及保存规则
| 来源类型 | 识别关键词(URL中) | 保存路径/文件名格式 |
|---|---|---|
| AI对话 | chatbox, deepseek.com, http://chatgpt.com等 | ai问答/【ai问答】{标题}{时间戳}.md |
| 知乎收藏夹 | http://zhihu.com/collection | 知乎收藏夹/【知乎】{标题}-{作者}{时间戳}.md |
| 知乎 | http://zhihu.com | 【知乎】{标题}-{作者}{时间戳}.md |
| 微博 | http://weibo.com | 【weibo】{摘要首行}-{首行}{时间戳}.md |
| 微信 | http://weixin.qq.com | 【wx】{首行}{时间戳}.md |
| 其他 | 不匹配上述关键词 | {首行}{时间戳}.md |
注意事项
- 剪贴板格式:必须复制HTML格式内容。如果剪贴板无HTML格式,脚本会提示“剪贴板中没有HTML格式内容”。
- 知乎收藏夹:如果识别为知乎收藏夹内容,脚本会自动批量处理收藏夹中的每个条目。
- 文件名清理:文件名中的非法字符(如
\ / * ? : " < > |)会被替换为下划线_。 - 摘要功能:代码中摘要生成部分被注释,如需启用需自行实现
summarize_message函数。
示例 - 复制一篇知乎文章内容,运行脚本后,会在
D:\剪藏2024-6-20\clipper目录下生成类似【知乎】如何学习Python-张三2026-03-12_15-30-00.md的文件。 - 复制一段ChatGPT对话,运行后会在
D:\剪藏2024-6-20\clipper\ai问答目录下生成类似【ai问答】Python编程技巧2026-03-12_15-30-00.md的文件。 如有其他需求,可修改脚本中的source_mappings字典或自定义ContentProcessor子类进行扩展。
编辑于 2026-03-14 · 著作权归作者所有