Python脚本实现QQ聊天记录TXT文件合并与排序

📅 2026/8/13 23:47:49
Python脚本实现QQ聊天记录TXT文件合并与排序
1. 项目缘起一个看似“无用”却高频的痛点你有没有遇到过这样的场景想把和某个朋友、家人或者工作伙伴的QQ聊天记录完整地导出来做个纪念或者留个证据。一番操作后你发现QQ自带的“消息管理器”确实提供了导出功能但导出的结果却是一堆按日期分割的、零零散散的TXT文件。想完整回顾一段跨越数月的对话你得在几十个甚至上百个文件里来回切换体验极其割裂。更别提如果你想对这些聊天记录进行文本分析、关键词搜索或者只是单纯地想按时间顺序通读一遍这种碎片化的格式简直就是灾难。这就是“无用小程序”要解决的问题。说它“无用”是因为它不创造新内容不解决宏大命题只做一件极其具体的小事把多个按日期导出的QQ聊天记录TXT文件合并成一个按时间顺序排列的、连贯的单一文件。但正是这种“小”让它具备了极高的实用价值。对于需要整理情感记录的情侣、复盘项目沟通的职场人、或是想分析社群聊天模式的研究者来说这个需求真实且迫切。网络上相关的讨论和工具五花八门从手动复制粘贴到使用Windows的copy命令再到寻找各种“合并神器”。但手动操作低效易错命令行对小白不友好第三方工具又可能涉及隐私泄露或捆绑垃圾软件。作为一个有十多年经验的开发者我的第一反应是用Python写个脚本自己动手丰衣足食。它安全、透明、可定制并且一次编写终身受用。接下来我就带你从零开始手把手实现这个“无用”却实用的小工具并深入探讨其中的技术细节和避坑指南。2. 解剖麻雀理解QQ聊天记录TXT的格式与合并逻辑在动手写代码之前我们必须先搞清楚我们要处理的对象——QQ导出的TXT文件到底长什么样。这是所有后续操作的基础理解格式才能精准解析。2.1 QQ聊天记录TXT文件格式深度解析当你从QQ消息管理器中导出一段时间的聊天记录为TXT时通常会得到一个以日期命名的文件例如2024-05-01.txt。用记事本打开其内容结构大致如下消息记录此消息记录为文本格式不支持重新导入 消息分组:我的好友 消息对象:张三 2024-05-01 10:23:15 我 吃午饭了吗 2024-05-01 10:24:01 张三 还没呢刚开完会。 2024-05-01 10:25:30 我 那一起食堂新开了个窗口。 2024-05-01 11:05:44 张三 图片[pic].jpg 图片已过期或已被清理 2024-05-01 11:06:12 张三 [动画表情]我们来拆解一下这个结构的关键部分文件头信息开头的几行是固定的提示和分隔线包含了“消息记录”、“消息分组”、“消息对象”等元数据。这部分在每个文件中都是重复的。消息主体这是核心内容。每条消息的基本格式是YYYY-MM-DD HH:MM:SS [发送者][消息内容]注意时间和发送者在同一行消息内容在下一行。这是一个非常规整的、按行组织的结构。特殊内容处理对于图片、表情、撤回消息等QQ会以特定的文本格式如图片[pic].jpg、[动画表情]、“xxx”撤回了一条消息进行占位。我们的合并程序需要原样保留这些内容。时间戳YYYY-MM-DD HH:MM:SS格式是标准的、可排序的。这为我们按时间顺序合并提供了天然的依据。2.2 合并的核心逻辑与挑战合并多个这样的文件目标很明确生成一个全新的TXT文件它包含所有输入文件中的聊天消息并且这些消息严格按照时间戳升序排列。听起来很简单但有几个关键挑战需要解决去重文件头每个文件开头都有相同的元信息消息分组、对象等。在合并后的文件中我们只需要保留一份文件头否则会显得非常冗余。全局时间排序我们不能简单地把文件A的内容全部接在文件B的后面。因为聊天是连续的很可能文件A5月1日的最后一条消息是晚上11点而文件B5月2日的第一条消息是早上9点。我们必须提取所有消息行基于它们的时间戳进行全局的重新排序。处理跨文件的消息连续性要确保合并后对话的上下文是连贯的。编码问题确保程序能正确读取和写入中文避免乱码。文件遍历与筛选如何让程序自动识别并读取指定文件夹下所有相关的TXT文件。基于以上分析我们的技术方案就清晰了编写一个Python脚本读取指定目录下所有符合条件的TXT文件解析出每条消息识别时间戳行然后在一个统一的列表中进行排序最后将排序后的消息连同唯一的一份文件头写入到一个新的文件中。3. 手把手实现从环境搭建到代码逐行解读理论清晰了现在开始实战。我将假设你是一个有一定Python基础但可能不熟悉文件处理和排序的读者我会尽量详细地解释每一步。3.1 环境准备与工具选型Python环境你需要安装Python。推荐使用Python 3.6及以上版本。去Python官网下载安装包安装时务必勾选“Add Python to PATH”。安装后在命令行输入python --version检查是否成功。代码编辑器任何文本编辑器都可以但推荐使用VSCode、PyCharm或Sublime Text它们有语法高亮和调试功能体验更好。核心Python库我们主要使用Python标准库无需额外安装任何第三方包。这保证了脚本的纯净和可移植性。主要用到的库有os: 用于遍历文件夹和文件。re: 正则表达式库用于精准匹配和提取消息行。datetime: 用于解析和比较时间戳虽然本例中字符串排序也可行但用datetime更规范。3.2 核心代码实现与分步讲解下面是我编写的一个健壮性较高的合并脚本。我会将代码分块并详细解释每一部分的作用和设计考量。#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re from datetime import datetime def merge_qq_chat_logs(input_dir, output_filemerged_chat_log.txt): 合并指定目录下所有QQ聊天记录TXT文件。 参数: input_dir (str): 包含QQ聊天记录TXT文件的目录路径。 output_file (str): 合并后输出的文件路径默认为当前目录下的merged_chat_log.txt。 # 1. 定义匹配消息行的正则表达式 # 匹配格式2024-05-01 10:23:15 发送者昵称 message_pattern re.compile(r^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (.)$) all_messages [] # 用于存储所有解析出的消息元组时间戳字符串 整行内容 消息内容 header_lines [] # 用于存储第一个文件的文件头 header_captured False # 标志位确保只捕获一次文件头 # 2. 遍历输入目录下的所有文件 print(f正在扫描目录: {input_dir}) file_list [] for filename in os.listdir(input_dir): if filename.lower().endswith(.txt): file_path os.path.join(input_dir, filename) file_list.append(file_path) print(f找到 {len(file_list)} 个TXT文件。) if not file_list: print(未找到任何TXT文件程序退出。) return # 对文件按文件名通常是日期排序这可以作为初步的预排序提高效率 file_list.sort() # 3. 逐个文件读取并解析 for file_path in file_list: print(f正在处理: {os.path.basename(file_path)}) try: with open(file_path, r, encodingutf-8) as f: lines f.readlines() except UnicodeDecodeError: # 如果utf-8失败尝试用gbk编码一些旧系统导出的文件可能是gbk try: with open(file_path, r, encodinggbk) as f: lines f.readlines() except Exception as e: print(f 错误无法读取文件 {file_path} 跳过。错误信息: {e}) continue except Exception as e: print(f 错误打开文件 {file_path} 失败 跳过。错误信息: {e}) continue current_message_block None for line in lines: line line.rstrip(\n) # 去掉行尾换行符 # 4. 捕获文件头仅第一次 if not header_captured: header_lines.append(line) # 简单的启发式判断如果遇到包含“消息对象”的行认为文件头部分即将结束 # 更严谨的做法是判断遇到第一个符合消息格式的行但QQ格式固定此方法有效 if 消息对象: in line: # 不是立即结束再读一行看看是否是分隔线“” pass if line.startswith() and len(header_lines) 5: # 遇到分隔线且已有一定行数认为头捕获完成 header_captured True # 确保文件头以两个换行结尾与消息主体分开 if not header_lines[-1] : header_lines.append() continue # 这一行是分隔线不进入消息解析 # 如果文件头已捕获开始解析消息 if header_captured: match message_pattern.match(line) if match: # 5. 如果匹配到新消息行保存上一条消息如果有并开始新消息 if current_message_block is not None: all_messages.append(current_message_block) timestamp_str, sender match.groups() # 将时间戳字符串转换为datetime对象便于精确排序 try: timestamp datetime.strptime(timestamp_str, %Y-%m-%d %H:%M:%S) except ValueError: # 如果时间格式意外错误降级使用原始字符串 timestamp timestamp_str current_message_block (timestamp, line, ) # 第三个元素存消息内容 else: # 6. 当前行不是新消息行则它是上一条消息的延续内容、续行或空行 if current_message_block is not None: # 将当前行内容追加到当前消息块的内容部分 ts, first_line, content current_message_block if content: # 如果已有内容加个换行符再拼接保留原格式 new_content content \n line else: new_content line current_message_block (ts, first_line, new_content) # 7. 一个文件处理完后别忘了添加最后一个消息块 if current_message_block is not None: all_messages.append(current_message_block) current_message_block None print(f共解析出 {len(all_messages)} 条消息。) if not all_messages: print(未解析到任何消息可能文件格式不符。) return # 8. 按时间戳排序所有消息 print(正在按时间排序消息...) # 排序键使用datetime对象或字符串。datetime对象排序更准确。 all_messages.sort(keylambda x: x[0] if isinstance(x[0], datetime) else x[0]) # 9. 将排序后的消息写入新文件 print(f正在写入合并文件: {output_file}) try: with open(output_file, w, encodingutf-8) as f: # 写入文件头 f.write(\n.join(header_lines)) f.write(\n\n) # 文件头与消息主体之间空两行 # 写入排序后的消息 for timestamp, first_line, content in all_messages: f.write(first_line \n) if content: # 如果消息有内容行 f.write(content \n) # 每条消息后空一行保持可读性 f.write(\n) print(f合并完成文件已保存至: {output_file}) except Exception as e: print(f写入输出文件时出错: {e}) if __name__ __main__: # 使用示例 # 将你的QQ聊天记录TXT文件放在一个文件夹例如 D:/QQ_Chats input_directory input(请输入包含QQ聊天记录TXT文件的文件夹路径: ).strip().strip() # 或者直接指定路径 # input_directory rD:\QQ_Chats output_filename merged_qq_chat_log.txt merge_qq_chat_logs(input_directory, output_filename)关键代码逻辑解读正则表达式message_pattern这是解析器的核心。^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (.)$匹配以日期时间开头后跟发送者的行。^和$确保匹配整行避免部分匹配。双编码尝试在try块中我们优先使用utf-8编码打开文件这是现代软件的标准。如果失败抛出UnicodeDecodeError则尝试用gbk编码。这个细节处理了不同环境下导出文件编码不一致的问题极大地增强了脚本的兼容性。文件头捕获逻辑我们用一个标志位header_captured控制只从第一个文件捕获一次文件头。通过判断行是否以开头且已读取了一定行数来判定文件头结束。这里采用了一种启发式方法在实践中对于标准QQ导出格式非常可靠。消息块的组装这是处理多行消息内容的关键。current_message_block变量是一个元组临时存储正在解析的这条消息的(时间戳, 首行, 内容)。当遇到新的时间戳行时就把上一个消息块存入总列表并开始新的一个。如果不是时间戳行就将其追加到当前消息块的内容部分用\n连接以保留原始换行。排序all_messages.sort(keylambda x: x[0])根据元组的第一个元素时间戳进行排序。由于我们存储的是datetime对象排序是精确到秒的。写入文件写入时先写入唯一的文件头然后遍历排序后的消息列表依次写入每条消息的“时间戳发送者”行和其内容行并在每条消息之间插入一个空行使合并后的文件清晰易读。3.3 如何使用这个脚本将上面的代码完整复制保存为一个文件例如merge_qq_chat.py。将所有从QQ导出的TXT文件如2024-05-01.txt,2024-05-02.txt放到一个单独的文件夹里比如我的聊天记录。打开命令行终端导航到保存了merge_qq_chat.py脚本的目录。运行命令python merge_qq_chat.py。根据提示输入你存放TXT文件的文件夹的完整路径例如C:\Users\你的名字\Desktop\我的聊天记录。你也可以直接修改脚本中input_directory的值为你的路径然后运行。脚本运行结束后会在当前目录下生成一个名为merged_qq_chat_log.txt的文件这就是合并并排序后的完整聊天记录。4. 避坑指南与进阶优化一个能跑起来的脚本只是开始要让它在各种实际场景下都稳定可靠还需要考虑很多边界情况。下面是我在多次使用和迭代中总结的经验。4.1 常见问题与排查思路问题一运行脚本后提示“未找到任何TXT文件”或“未解析到任何消息”。排查首先检查你输入的文件夹路径是否正确以及文件夹内是否有.txt后缀的文件。其次检查文件编码。脚本虽然尝试了UTF-8和GBK但如果你的文件是其他罕见编码如UTF-8 with BOM可能会失败。你可以用记事本打开一个TXT文件点击“另存为”在对话框底部查看“编码”一项。如果是“UTF-8 BOM”可以先用记事本另存为“UTF-8”格式再试。解决可以在代码的编码尝试部分增加对utf-8-sig(UTF-8 with BOM) 的支持。问题二合并后的文件时间顺序是乱的。排查这通常是因为时间戳格式不标准导致正则表达式没有正确匹配或者时间戳被当作字符串排序时出现了问题例如月份和日期位置颠倒。检查原始TXT文件确认时间格式是否为YYYY-MM-DD HH:MM:SS。解决确保正则表达式message_pattern与你的文件格式完全匹配。如果日期格式是DD/MM/YYYY之类的需要修改正则表达式和datetime.strptime中的格式字符串。问题三消息内容出现错位或丢失。排查这种情况通常发生在一条消息内容包含多行且其中某一行“碰巧”符合时间戳格式虽然概率极低。或者原始文件中存在不规则的空行或分隔符。解决我们的脚本逻辑是将非时间戳行都视为上一条消息的延续这在绝大多数情况下是安全的。为了更稳健可以增加一个判断如果当前行是空行且上一条消息的内容已经结束可以将其视为消息间的分隔空行而不是上一条消息的一部分。这需要对current_message_block的处理逻辑进行微调。问题四处理大量文件时速度慢。排查如果聊天记录文件多达数百个每个文件都先全部读入内存f.readlines()可能会消耗较多内存。排序操作在数据量极大时也可能成为瓶颈。解决对于内存可以改为流式读取一次读一行。对于排序Python的list.sort()是高效的万条级别的消息瞬间完成。如果达到十万条以上才需要考虑更优化的算法但QQ导出的文本记录通常不会到这个量级。4.2 脚本的进阶优化方向基础的合并功能已经实现但我们可以让它更强大、更易用增加命令行参数使用argparse库让用户可以直接通过命令行指定输入文件夹和输出文件名无需修改代码或手动输入。例如python merge_qq_chat.py -i ./chats -o ./total.txt。支持更灵活的文件筛选除了按.txt后缀还可以支持按文件名模式筛选例如只合并与张三_*.txt这样的文件。增加去重功能如果导出的时间段有重叠可能会导致重复消息。可以在排序后增加一个步骤对比相邻消息的时间戳和内容哈希去除完全相同的重复消息。输出格式美化将合并后的文件输出为HTML格式用不同的颜色区分“我”和对方的消息甚至还原简单的气泡样式可读性会大大提升。这需要将每条消息的发送者信息利用起来。图形化界面GUI对于非技术用户一个简单的图形界面使用Tkinter或PyQt会更友好。用户只需点击选择文件夹和输出位置即可。打包成可执行文件使用PyInstaller将脚本打包成.exe文件这样没有安装Python的用户也可以直接双击运行。4.3 一个重要的安全与隐私提醒注意聊天记录是个人隐私数据。请务必在可信的、离线环境下运行此脚本。不要将包含个人敏感信息的聊天记录文件上传到不明网站或使用来历不明的在线合并工具。自己编写的脚本处理数据是最安全的方式。处理完后也请妥善保管合并后的文件并及时清理临时文件。5. 举一反三文本处理脚本的通用设计思维完成这个“无用”的小程序后我们不妨跳出这个具体问题看看它背后蕴含的、可复用的文本处理脚本设计模式。掌握这个思维你可以轻松应对很多类似的自动化需求。通用流程可以抽象为输入 - 解析 - 处理 - 输出。输入模块负责获取原始数据。在我们的例子里是遍历文件夹读取TXT文件。在其他场景可能是读取CSV、JSON、数据库甚至是抓取网页。关键点是健壮性要考虑文件不存在、编码错误、网络超时等情况使用try...except进行妥善处理。解析模块将原始数据转换为程序内部易于处理的结构如列表、字典。我们使用了正则表达式来识别消息行。解析的核心是精确匹配和结构化。你需要仔细研究原始数据的格式规律分隔符、固定模式、缩进等选择最合适的工具字符串方法、split、re、json.loads等。处理模块对解析后的数据进行核心业务逻辑操作。这里是排序。在其他场景可能是过滤筛选特定关键词、统计词频分析、转换格式转换、计算数据聚合等。这一部分是脚本的“大脑”体现了你要解决的具体问题。输出模块将处理后的结果持久化。我们是将排序后的列表写回一个新的TXT文件。输出要考虑格式友好易读和格式正确编码。同样写入文件也可能失败磁盘满、无权限需要异常处理。当你面对一个新的文本处理任务时先别急着写代码。按照这四个步骤去分析你会发现自己思路清晰很多。比如如果你想分析合并后的聊天记录里最常用的词那么输入读取我们刚刚生成的merged_qq_chat_log.txt。解析可能需要过滤掉时间戳和发送者行只提取纯消息内容。处理使用jieba中文分词库进行分词然后用collections.Counter统计词频。输出将词频结果输出到一个CSV文件或直接打印出来。你看底层逻辑是相通的。这个“无用”的小程序不仅解决了一个具体问题更是一次绝佳的、关于如何用编程思维自动化处理日常繁琐任务的实践。它教会你的远比“合并TXT文件”这件事本身要多得多。