嗯,如题 是个蛋疼物
目前QQ的聊天记录导出功能很让人郁闷
三种聊天记录格式的导出
1  TXT   没图
2  BAK  只能再导入QQ使用
3  MHT 有图有字,缺点是一旦聊天记录很多,文件体积就会很大,几乎所有的工具都不能正常打开

单纯的把MHT转换成HTML也不行,因为HTML也很大,加上图片之类的资源 也会卡死
于是只能切开显示,处理思路很简单,就是超大的文本文件,按行顺序处理,把图片解码存入文件,然后分割HTML内容
代码如下 只支持单个QQ群导出记录

  1 import io;
2 import fsys.dlg;
3 import string;
4 import crypt.bin;
5 import fsys.path;
6 //将Base64的数据转换成图片
7 function base64images (str,path)
8 {
9 if(str)
10 {
11 var kzm = string.match(str,"Content-Type:image/(\S+)");
12 var wjm = string.match(str,"Content-Location\:(\S+.dat)");
13 startpos,endpos = string.find(str,"}.dat");
14 if(endpos)
15 {
16 var bindata = crypt.bin.decodeBase64(string.trim( string.sub(str,endpos+1)));
17 //io.print(string.trim( string.sub(str,endpos)));
18 //execute("pause") //按任意键继续
19 string.save(path ++ "\\" ++ wjm,bindata);
20 }
21
22 }
23 }
24 //切割记录文件
25 function split_html (file_path,path)
26 {
27 var html_head = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QQ Message</title><style type="text/css">body{font-size:12px; line-height:22px; margin:2px;}td{font-size:12px; line-height:22px;}</style></head><body><table width=100% cellspacing=0>';
28 var html_foot = '</body></html>';
29 var file = io.open( file_path, "rt" );
30 var line_text = file.read();
31 var line = 0;
32 var page = 0;
33 var tmp = '';
34 while(line_text)
35 {
36 line_text = string.match(line_text,"\<tr.*?\</tr\>");
37 if(line_text)
38 {
39 tmp = tmp ++ string.replace(line_text,'@<IMG src="{','<IMG src="data:images/{');
40 line++;
41 if( line>500 )
42 {
43 var f = io.open(path ++page++".html","w+");
44 f.write(html_head);
45 f.write(tmp);
46 f.write('</table>'++'<a href="'++ page+1 ++'.html">Next page</a>');
47 f.write(html_foot);
48 f.close();
49 page++;
50 line = 0;
51 tmp = '';
52 }
53 }
54 line_text = file.read();
55 }
56 var f = io.open(path ++ page ++ ".html","w+");
57 f.write(html_head);
58 f.write(tmp);
59 f.write('</table>');
60 f.write(html_foot);
61 f.close();
62 file.close();
63 }
64 //打开控制台
65 io.open();
66 //选择QQ聊天记录
67 var mhtPath = fsys.dlg.open("QQ聊天记录文件*.mht|*.mht||");
68 //开始处理文件
69 if(mhtPath)
70 {
71 var path = io.splitpath(mhtPath);
72 var img_dir = path.dir ++ path.name ++ '\\images';
73 var file = io.open( mhtPath, "rt" );
74 fsys.createDir( path.dir ++ path.name);
75 fsys.createDir( path.dir ++ path.name ++ '\\images');
76 var html = io.open( path.dir ++ path.name ++ "\\tmp.html","a+");
77 var line_text = file.read();
78 var i = 0; //行数
79 var is_html = false;
80 var tmp_image_data = '';
81 //判断是否是腾讯QQ聊天记录文件
82 if(string.indexAny(line_text,"Tencent"))
83 {
84
85 while( line_text )
86 {
87 //判断文本内容开始处
88 html_start,xmlns_end = string.find(line_text,"@<html xmlns");
89 if( 1 == html_start )
90 {
91 is_html = true;
92 }
93 //判断文本内容结束处
94 html_end1,html_end2 = string.find(line_text,"@</table></body></html>");
95 if( 1 == html_end1 )
96 {
97 //最后一行写入文件
98 html.write(line_text);
99 html.write('\r\n');
100 is_html = false;
101 //break;
102 }
103
104 if(is_html)
105 { //将聊天文本内容写入文件
106 html.write(line_text);
107 html.write('\r\n');
108 }
109
110 //切割图片base64数据
111 if(false == is_html && i>10 )
112 {
113 if(string.find(line_text,"@------=_NextPart")){
114 base64images(tmp_image_data,img_dir);
115 tmp_image_data = line_text ++ '\r\n';
116 }else{
117 tmp_image_data = tmp_image_data ++ line_text ++ '\r\n';
118 }
119 }
120
121 line_text = file.read();
122 i++;
123 io.print("已处理",i,'行数据');
124 }
125 base64images(tmp_image_data,img_dir);
126
127 }else {
128
129 io.print("您选择的文件可能不是QQ导出的mht聊天记录文件");
130
131 }
132 //关闭文件
133 file.close();
134 html.close();
135 //需要切割html
136 split_html( path.dir ++ path.name ++ "\\tmp.html",path.dir ++ path.name ++ "\\");
137 }
138 else
139 {
140 io.print("请正确的选择QQ导出的mht聊天记录文件");
141 }
142 execute("pause") //按任意键继续
143 io.close();//关闭控制台

二进制版本

PHP版本的(可能有点小BUG 不确定 效率提升不少)

 1 <?php
2 if(!file_exists($argv[1])){
3 echo 'There isn\'t have this file.';
4 exit;
5 }
6 ini_set('pcre.backtrack_limit',100000000);
7 #ini_set('pcre.recursion_limit',100000000);
8 define("BASEDIR",dirname($argv[1]).'/');
9 $is_table_end = false;
10 $page = 0;
11 mk_imgdir();
12 $handle = fopen("input.mht", "rb");
13 $contents = '';
14 while (!feof($handle)) {
15 $contents .=fread($handle,204800);
16 $contents = process($contents);
17 }
18 process($contents);
19
20 #主体数据处理
21 function process($contents){
22 $html_head = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QQ Message</title><style type="text/css">body{font-size:12px; line-height:22px; margin:2px;}td{font-size:12px; line-height:22px;}</style></head><body><table width=100% cellspacing=0>';
23 $html_foot = '</table></body></html>';
24 Global $is_table_end;
25 Global $page;
26 if(false === $is_table_end){
27 if( false !== strpos($contents,$html_foot) ){
28 $is_table_end = true;//判断消息部分是否完毕 只处理图片部分
29 }
30 $r = preg_match_all ('|<tr.*?\</tr\>|ims', $contents , $matches ,PREG_OFFSET_CAPTURE);
31 if($r){
32 $matches = array_chunk($matches[0],200);
33 foreach($matches as $key=>$val){
34 $arr = array_column($val,0);
35 array_walk( $arr , function(&$v, $k) { $v = preg_replace('|<IMG src="{(\S)(\S)(\S+).dat|ims','<IMG src="../images/$1/$2/{$1$2$3.dat',$v);});
36 file_put_contents('messages/'.sprintf("%08d", $page+$key).'.html' ,$html_head . implode('',$arr).'<td><H1><a href="./'.sprintf("%08d", $page+$key+1).'.html">Next page</a></h1></td>'.$html_foot);
37 }
38 $page += $key;
39 $pos = end($val);
40 $contents = substr($contents,bcadd($pos[1],strlen($pos[0]),0));
41 }else{
42 $contents = '';
43 }
44 }
45 if(true === $is_table_end){
46 //图片部分
47 $r = preg_match_all ('|Content-Type:image.*?:base64.*?Content-Location:(.*?)\.dat(.*?)(?:------=_)|ims', $contents , $matches ,PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
48 if($r){
49 //$matches = array_chunk($matches[0],200);
50 $result = array();
51 foreach($matches as $key=>$val){
52 $result[] = array('name'=>$val[1][0],'contents'=>$val[2][0]);
53 if(count($result) >=150){
54 put_images($result);
55 $result = array();
56 }
57 }
58 put_images($result);
59 $result = array();
60 $contents = substr($contents,$val[0][1]); ##There is no str len
61 }else{
62 $contents = '';
63 }
64 }
65 return $contents;
66 }
67
68
69 #写入图片表情文件到硬盘
70 function put_images($data){
71 foreach($data as $val){
72 $dir = './images/'. substr($val['name'],1,1) .'/' . substr($val['name'],2,1) .'/'.$val['name'] . '.dat';
73 echo $dir."\r\n";
74 file_put_contents($dir,base64_decode(trim($val['contents'])));
75 }
76 }
77
78 #建立图片保存目录
79 function mk_imgdir(){
80 if(file_exists(BASEDIR.'images')){
81 rename(BASEDIR.'images',BASEDIR.'images_old');
82 }
83 $tmp = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
84 foreach($tmp as $v1){
85 mkdir(BASEDIR.'images'.'/'.$v1,0777,true);
86 foreach($tmp as $v2){
87 mkdir(BASEDIR.'images'.'/'.$v1.'/'.$v2,0777,true);
88 }
89 }
90 if(file_exists(BASEDIR.'messages')){
91 rename(BASEDIR.'messages',BASEDIR.'messages_old');
92 }
93 mkdir(BASEDIR.'messages'.'/',0777,true);
94 return 0;
95 }
<?php
ini_set('pcre.backtrack_limit',1000000);
$is_table_end = false ; $page = 0 ;$contents = ''; $output_zip = './test.zip';
#判断输入文件
if(!file_exists($argv[1]))
{
echo 'There isn\'t have this file.';
exit;
}
#建立归档文件 默认为
make_output_target($output_zip);
$handle = fopen($argv[1], "rb");
#分段处理
do
{
$contents .=fread($handle,124416);#wtf
$contents = mht_process($contents,$output_zip);
}
while (!feof($handle)); #主体数据处理
function mht_process($contents,$output_zip){
Global $is_table_end;
Global $page;
$zip = new ZipArchive;
if ($zip->open($output_zip,ZIPARCHIVE::CREATE) !== TRUE)
{
echo 'create image failed';
exit;
}
#判断非图片消息部分是否处理完毕
if(false === $is_table_end)
{
$html_head = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QQ Message</title><style type="text/css">body{font-size:12px; line-height:22px; margin:2px;}td{font-size:12px; line-height:22px;}</style></head><body><table width=100% cellspacing=0>';
$html_foot = '</table></body></html>';
#判断消息部分是否完毕 只处理图片部分
if(false !== strpos($contents,$html_foot))
{
$is_table_end = true;
}
$r = preg_match_all ('|<tr.*?\</tr\>|ims', $contents , $matches ,PREG_OFFSET_CAPTURE);
if($r)
{
$matches = array_chunk($matches[0],200);
foreach($matches as $key=>$val)
{
$arr = array_column($val,0);
array_walk( $arr , function(&$v, $k) use ($zip)
{
$v = preg_replace('|<IMG src="{(\S)(\S)(\S+).dat|ims','<IMG src="../images/$1/$2/{$1$2$3.dat',$v);
});
$zip->addFromString('messages/'.sprintf("%08d", $page+$key).'.html' ,$html_head . implode('',$arr).'<td> <H1><a href="./'.sprintf("%08d", $page+$key-1).'.html">Prev page</a></h1> <H1><a href="./'.sprintf("%08d", $page+$key+1).'.html">Next page</a></h1></td>'.$html_foot);
}
$page += $key;
$pos = end($val);
$contents = substr($contents,bcadd($pos[1],strlen($pos[0]),0));
}
else
{
$contents = '';
}
}
#处理图片部分
if(true === $is_table_end)
{
#图片数据匹配
$r = preg_match_all ('|Content-Type:image.*?:base64.*?Content-Location:(.*?)\.dat(.*?)(?:------=_)|ims', $contents , $matches ,PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
if($r)
{
//$matches = array_chunk($matches[0],200);
$result = array();
foreach($matches as $key=>$val){
$result = array('name'=>$val[1][0],'contents'=>$val[2][0]);
$dir = 'images/'. substr($result['name'],1,1) .'/' . substr($result['name'],2,1) .'/'.$result['name'] . '.dat';
#写入图片表情文件到硬盘
$zip->addFromString($dir,base64_decode(trim($result['contents'])));
$result = array();
}
$result = array();
$contents = substr($contents,$val[0][1]);
}
else
{
$contents = '';
}
}
$zip->close();
#剩余部分返回 下一次处理拼接数据
return $contents;
} #建立保存目标
function make_output_target($output='./test.zip')
{
$zip = new ZipArchive;
if ($zip->open($output,ZIPARCHIVE::CREATE) !== TRUE)
{
echo 'create images directory failed';
exit;
}
$zip->addEmptyDir('images');
$tmp = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
array_walk($tmp,function($val,$key) use ($tmp,$zip)
{
$base_path = 'images/'.$val;
$zip->addEmptyDir($base_path);
array_walk($tmp,function($val,$key) use ($tmp,$zip,$base_path)
{
$zip->addEmptyDir($base_path.'/'.$val);
});
});
$zip->addEmptyDir('messages');
$zip->close();
return 0;
}

使用方法

 "php.exe" cli.php input.mht > log.txt
pause

生成ZIP之后 可以用winmount来挂载查看 无需解压很方便

QQ群聊天记录文件分割的更多相关文章

  1. 使用 R 语言挖掘 QQ 群聊天记录

    1.获取数据 从 QQ 消息管理器中导出消息记录,保存的文本类型选择 txt 文件.这里获取的是某群从 2016-04-18 到 2016-05-07 期间的聊天记录,记录样本如下所示. 消息记录(此 ...

  2. 特定用户QQ群聊天记录导出的实现

    一.把QQ群的聊天记录txt格式导出 消息管理器 -> 选择要导出的群 -> 右击.导出   这里要注意 : 导出之后的 文本是 unicode 编码的,需要转换 ==|| 之前不知道,搞 ...

  3. 一梦江湖费六年——QQ群聊天分析

    本文结构: 一.那些年我们加过的QQ群 二.数据读入和整理(一)--来自蓝翔的挖掘机 二.数据读入和整理(二)--你不知道的事 三.聊天宏观(1)--寤寐思服 三.聊天宏观(2)日月篇 三.聊天宏观( ...

  4. QQ聊天记录备份助手 v1.0——搜索、备份、恢复QQ聊天记录文件,重装系统必备

    项目地址 https://github.com/guoyaohua/QQBackUp 开发环境 Netbean JAVA 功能描述 自动搜索硬盘中的QQ聊天记录文件 可选择的备份QQ数据记录 还原QQ ...

  5. (获取qq群成员信息,并下载头像,每个群保存一个文件夹)

    # 1.获取到自己qq里面所有的群,并且保存每个群里面的群成员信息到mongodb里面# 下载每个群的群成员的头像# 1.抓包,抓到获取自己所有qq群的接口 requests模块 https://qu ...

  6. 我们是怎么管理QQ群的

    文章背景:腾讯平台上的qq群数以千万百万计,但99%的在吹水扯蛋,从早上的问好开始,到晚上的晚安,无一不浪费青春之时间,看之痛心,无力改变,只好自己建了一个,希望能以此来改变群内交流的氛围或环境. 以 ...

  7. 前端资料QQ群交流

    转:https://github.com/jsfront/src/blob/master/qq.md 这本来是我QQ群内部的一份公共约定的日常交流规则,后来得到大伙的一致认可,并用实际行动来捍卫它,使 ...

  8. 腾讯QQ的聊天记录中的图片记录造假

    前不久和朋友在群里聊天时,突然出现了一个BUG,就是一个群友发了A图片,但在我这边显示得却是B图片.当时就猜测,腾讯为了节省流量或者手机资源的原因,给每一张图片弄了个唯一ID,遇到相同ID的就直接从本 ...

  9. 一行实现QQ群组头像,微信群组,圆角等效果. 并支持url直接加载图片

    说点题外话. Coding中我们总是经历着这么几个过程. 学会使用: 不管是API也好, 开源库也好. 总是在最开始的学会去用. 了解实现原理: 可能会因为一些不兼容, 代码的异常状态的处理不够完美等 ...

随机推荐

  1. 如何通过 jQuery text() 和 html()

    text() - 设置或返回所选元素的文本内容 html() - 设置或返回所选元素的内容(包括 HTML 标记) val() - 设置或返回表单字段的值 $("#btn1").c ...

  2. BZOJ 2228 礼物(gift)(最大子长方体)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2228 题意:给出一个只含有NP两种字母的长方体.从中找出只含有字母N的长方体,造型为a* ...

  3. [HIHO1082]然而沼跃鱼早就看穿了一切(字符串水题)

    题目链接:http://hihocoder.com/problemset/problem/1082 数据范围小,胡搞. /* ━━━━━┒ギリギリ♂ eye! ┓┏┓┏┓┃キリキリ♂ mind! ┛┗ ...

  4. 径向基函数(RBF)神经网络

    RBF网络能够逼近任意的非线性函数,可以处理系统内的难以解析的规律性,具有良好的泛化能力,并有很快的学习收敛速度,已成功应用于非线性函数逼近.时间序列分析.数据分类.模式识别.信息处理.图像处理.系统 ...

  5. 警惕rapidxml的陷阱:添加节点时,请保证变量的生命周期

    http://www.cnblogs.com/chutianyao/p/3246592.html 项目中要使用xml打包.解析协议,HQ指定了使用rapidxml--号称是最快的xml解析器. 功能很 ...

  6. 红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

    在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间 ...

  7. orm 通用方法——GetOneModel 条件查询一个对象

    数据连接层的方法封装成通用方法是很有必要,节省不必要的重复写代码. Golang的orm.xorm框架没有封装这些操作. 这里是一个查询单个对象的方法. 此处抛砖引玉,大家继续完善. 通用方法定义代码 ...

  8. 关于android软键盘enter键的替换与事件监听

    android软键盘事件监听enter键  软件盘的界面替换只有一个属性android:imeOptions,这个属性的可以取的值有 normal,actionUnspecified,actionNo ...

  9. 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化

    上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...

  10. 生成并返回 json 结果文件

    #region 生成并返回 json 结果文件 /// <summary> /// 生成并返回 json 结果文件 /// </summary> /// <param n ...