精华内容
下载资源
问答
  • 更多相关内容
  • 电脑版微信聊天记录导出打印方法.pdf
  • 微信导出聊天记录_android系统.pdf
  • 微信聊天记录导出(2020新版)

    万次阅读 2020-08-16 09:53:43
    微信聊天记录导出(2020新版) 首先说明,坑的部分主要是数据库破解。 1. 本地备份提取聊天记录 这里主要讲小米手机,苹果手机参考https://www.zhihu.com/question/66251440,其他安卓手机可以用模拟器然后root提取。 ...

    微信聊天记录导出(2020新版)

    首先说明,坑的部分主要是数据库破解。

    项目地址:https://github.com/fly-dragon211/Wechat-message-analysis

    1. 本地备份提取聊天记录

    这里主要讲小米手机,苹果手机参考https://www.zhihu.com/question/66251440,其他安卓手机可以用模拟器然后root提取。

    要导出微信安卓客户端的聊天记录,首先得找到聊天记录的数据库。
    安卓客户端的聊天记录储存在私有目录 /data/data/com.tencent.mm/MicroMsg 下,这个目录需要root权限才能进去,但是,那样太太太麻烦了,好在我们MI6有本地备份的功能,利用这个功能。我们轻而易举就可以获得数据库。

    需要的工具

    此处下载

    1. 首先到手机:设置->更多设置->备份和重置->本地备份 里面点击新建备份,选择软件程序中的微信进行备份,注意只选择微信。

    2. 然后到文件管理 /内部储存设备/MIUI/backup/ALLBackup/ 下将备份的文件夹复制到电脑

    3. 然后用任意一种压缩包软件(我用的是7zip)打开这个com.tencent.mm.bak文件,并且将apps\com.tencent.mm\r\MicroMsg\systemInfo.cfgapps\com.tencent.mm\r\MicroMsg\CompatibleInfo.cfgapps\com.tencent.mm\r\MicroMsg\xxxx\EnMicroMsg.db三个文件解压到电脑上。这里xxxx是一串随机的字母,代表你的微信用户,每个人不一样,一般是最大的那个文件夹,我这里是下图所示文件夹:

    2. 破解数据库密码

    找到聊天数据库了,但是目前还不能得到聊天记录,因为这个数据库是sqlcipher加密数据库,需要密码才能打开。

    数据库密码有很多种生成方式:

    1. 手机IMEI+uin(微信用户id userinformation) 将拼接的字符串MD5加密取前7位

      IMEI123456uinabc,则拼接后的字符串为123456abc 将此字符串用MD5加密(32位)后

      df10ef8509dc176d733d59549e7dbfaf 那么前7位df10ef8 就是数据库的密码,由于有的手机是双卡,有多个IMEI,或者当手机获取不到IMEI时会用默认字符串1234567890ABCDEF来代替,由于种种原因,并不是所有人都能得出正确的密码,此时我们可以换一种方法。

    2. 反序列化CompatibleInfo.cfgsystemInfo.cfg

      不管是否有多个IMEI ,或者是微信客户端没有获取到IMEI,而使用默认字符串代替,微信客户端都会将使用的信息保存在MicroMsg文件夹下面的CompatibleInfo.cfgsystemInfo.cfg文件中,可以通过这两个文件来得到正确的密码,但是这两个文件需要处理才能看到信息。

    3. 使用hook方式得到数据库的密码,这个方法最有效参考

    4. 暴力破解

    我开始用反序列化:

    javac IMEI.java
    java IMEI systemInfo.cfg CompatibleInfo.cfg
    

    运行完成后就会得到密码
    参考链接

    但是出现了如下错误:

    错误: 找不到或无法加载主类 IMEI 原因: java.lang.ClassNotFoundException: IMEI

    于是我换了第一种方法,可是找不到uid

    寻找uid

    uid不是微信号,原来是保存在下面路径:

    /data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml,

    在该文件中,键值“auth_uin”即为该用户的uin。

    但是我发现根本没有这个路径,可能是我的手机没有root,于是我把备份的文件解压,在里面搜索这个,终于找到了。

    最终确定uid,然后MD5(IMEI少一位+uin)的输出字符串作为密码,取前7位小写,就可以破解数据库了。

    得到数据库之后可以分析一下你的聊天记录,顺便制作一个词云来给你的心上人看一下你们都聊了啥👀

    参考:

    https://zhuanlan.zhihu.com/p/77418711

    https://github.com/Heyxk/notes/issues/1

    https://www.sohu.com/a/355273307_704736

    数据分析

    下面就是最关键的数据分析。聊天记录包含了非常丰富的数据,这里我只做了两个比较简单的例子,一个是针对时间做聊天时间段分布的统计;一个是针对内容做字符匹配,统计一些高频词汇出现的次数,比如“早安”、“晚安”、“想你”、“爱”等等(你懂的)。

    详情见本项目。代码都有注释,我写了一个类把直方图,高频词汇统计,词云等结合起来了。

    在这里插入图片描述

    # -*- coding: utf-8 -*-
    """
    Created on Sun Aug 16 10:41:08 2020
    @author: fly
    """
    
    import numpy as np
    import pandas as pd
    import time
    import re
    import datetime
    import seaborn as sns
    import matplotlib.pyplot as plt
    from matplotlib.font_manager import *  # 如果想在图上显示中文,需导入这个包
    import xlwt
    import wordcloud  # 词云
    import imageio
    import jieba  # 中文分词
    
    
    class WechatAnalysis:
        def __init__(self, data_frame, name):
            self.data_frame = data_frame
            self.wechat_name = name
            self.chat_time, self.chat_content = self.get_time_and_content()
            self.font_path = r'C:\Windows\Fonts\MSYH.TTC'  # 微软雅黑
    
        def get_time_and_content(self):
            # 把聊天内容和时间取出
            chat = self.data_frame
            chat_time = []
            chat_content = []
            for i in range(len(chat) - 1):
                content = chat[i:i + 1]
                if content['talker'].values[0] == self.wechat_name:
                    t = content['createTime'].values[0] // 1000  # 除以1000用以剔除后三位0
                    c = content['content'].values[0]
                    chat_time.append(t)
                    chat_content.append(c)
            return chat_time, chat_content
    
        def get_time_hist(self, time_flag=0):
            """
            :param time_flag: 0 hour, 1 year day, 2 month
            :return:
            """
            # 画小时核密度分布图
            str_list = ['小时', '天', '月']
            data_list = [self.to_struct_time(t)[time_flag] for t in self.chat_time]  # 得到数据列表
            max_indx = np.argmax(np.bincount(data_list))  # 出现次数最多的数据
            max_num = np.bincount(data_list)[max_indx]  # 出现次数
            print('\n.......................\n开始画图\n.......................')
            myfont = FontProperties(fname=r'C:\Windows\Fonts\MSYH.TTC', size=22)  # 标题字体样式
            myfont2 = FontProperties(fname=r'C:\Windows\Fonts\MSYH.TTC', size=18)  # 横纵坐标字体样式
            myfont3 = FontProperties(fname=r'C:\Windows\Fonts\FZSTK.TTF', size=18)  # 标注字体
            sns.set_style('darkgrid')  # 设置图片为深色背景且有网格线
            if time_flag == 0:
                sns.distplot(data_list, 24, color='lightcoral')
                plt.xticks(np.arange(0, 25, 1.0), fontsize=15)
                plt.ylabel('聊天概率', fontproperties=myfont2)
            elif time_flag == 1:
                sns.distplot(data_list, 365, kde=False, color='lightcoral')
                plt.xticks(np.arange(1, 366, 30), fontsize=15)
                plt.ylabel('消息条数', fontproperties=myfont2)
                plt.annotate("这%s我们发了%d条消息!" % (str_list[time_flag], max_num),
                             xy=(max_indx,max_num), fontproperties=myfont3)
            elif time_flag == 2:
                sns.distplot(data_list, 12, kde=False, color='lightcoral')
                plt.xticks(np.arange(0, 12, 1.0), fontsize=15)
                plt.ylabel('消息条数', fontproperties=myfont2)
                plt.annotate("这%s我们发了%d条消息!" % (str_list[time_flag], max_num),
                             xy=(max_indx, max_num), fontproperties=myfont3)
    
            plt.yticks(fontsize=15)
            plt.title('聊天时间分布', fontproperties=myfont)
            plt.xlabel('时间段/' + str_list[time_flag], fontproperties=myfont2)
    
            fig = plt.gcf()
            fig.set_size_inches(15, 8)
            fig.savefig('chat_time.png', dpi=100)
            plt.show()
            print('\n.......................\n画图结束\n.......................')
    
        def get_hour_slice(self):
            hour_set = [self.to_struct_time(t)[0] for t in self.chat_time]
            print('\n.......................\n开始聊天时段统计\n.......................')
            time_slice = [0, 0, 0, 0, 0, 0]
            deep_night = []
            for i in range(len(hour_set)):
                if 0 <= hour_set[i] < 6:
                    time_slice[0] += 1
                    deep_night.append([self.chat_time[i], self.chat_content[i]])
                elif 6 <= hour_set[i] < 10:
                    time_slice[1] += 1
                elif 10 <= hour_set[i] < 14:
                    time_slice[2] += 1
                elif 14 <= hour_set[i] < 18:
                    time_slice[3] += 1
                elif 18 <= hour_set[i] < 22:
                    time_slice[4] += 1
                else:
                    time_slice[5] += 1
            labels = ['凌晨0点至6点', '6点至10点', '10点至14点',
                      '14点至18点', '18点至22点', '22点至次日凌晨0点']
            time_distribution = {
                labels[0]: time_slice[0],
                labels[1]: time_slice[1],
                labels[2]: time_slice[2],
                labels[3]: time_slice[3],
                labels[4]: time_slice[4],
                labels[5]: time_slice[5]
            }
            for label in labels:
                print("{ name: '%s': %d}, \n" % (label, time_distribution[label]))
    
            ''' 深夜聊天记录 '''
            wbk = xlwt.Workbook()
            sheet = wbk.add_sheet('late')
            for i in range(len(deep_night)):
                sheet.write(i, 0, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(deep_night[i][0])))
                sheet.write(i, 1, deep_night[i][1])
            wbk.save('聊得很晚.xls')
            print('\n.......................\n聊天时段统计结束\n.......................')
    
        def get_word_cloud(self, chinese_slice=False, stopwords=None, image_out_name=None):
            """
            :param chinese_slice:  Whether Use jieba to slice the sentences.
            :param stopwords: a set include some words to exclude.
            :return:
            """
            font_path = self.font_path
            if image_out_name is None:
                image_out_name = 'word-heart.png'
            if chinese_slice:
                text = ",".join(self.chat_content)
                text_list = jieba.lcut(text)
                text = " ".join(text_list)
                image_out_name = 'zh-'.__add__(image_out_name)
            else:
                text = " ".join(self.chat_content)
            mk = imageio.imread("heart.png")
    
            # 构建并配置词云对象w,注意要加scale参数,提高清晰度
            w = wordcloud.WordCloud(width=1000,
                                    height=700,
                                    background_color='white',
                                    font_path=font_path,
                                    mask=mk,
                                    scale=2,
                                    stopwords=stopwords,
                                    contour_width=1,
                                    contour_color='red')
            # 将string变量传入w的generate()方法,给词云输入文字
            w.generate(text)
            # 展示图片
            # 根据原始背景图片的色调进行上色
            image_colors = wordcloud.ImageColorGenerator(mk)
            plt.imshow(w.recolor(color_func=image_colors))
            # 根据原始黑白色调进行上色
            # plt.imshow(wc.recolor(color_func=grey_color_func, random_state=3), interpolation='bilinear') #生成黑白词云图
            # 根据函数原始设置进行上色
            # plt.imshow(wc)
    
            # 隐藏图像坐标轴
            plt.axis("off")
            plt.show()
    
            # 将词云图片导出到当前文件夹
            w.to_file(image_out_name)
    
        def get_word_statistic(self):
            ''' 字符统计 '''
            chat_content = self.chat_content
            chat_time = self.chat_time
    
            print('\n..........\n开始字符统计\n............\n')
            start = datetime.datetime.now()
            pattern_love = '.*?(爱).*?'
            pattern_morning = '.*?(早).*?'
            pattern_night = '.*?(晚安).*?'
            pattern_miss = '.*?(想你).*?'
            pattern_set = [pattern_love, pattern_morning, pattern_night,
                           pattern_miss, '.*?(在干嘛).*?', '.*?(嘻嘻).*?']
            statistic = list(np.zeros(len(pattern_set), dtype=np.int))
            for i in range(len(chat_content)):
                for j in range(len(pattern_set)):
                    length = len(re.findall(pattern_set[j], str(chat_content[i])))
                    statistic[j] += length
    
            print("在%d个日日夜夜里" % ((max(chat_time) - min(chat_time)) // (3600*24)))
            for i, pattern in enumerate(pattern_set):
                print("我们说了%d次 %s" % (statistic[i], pattern.strip('.*?()')))
    
            end = datetime.datetime.now()
            print('\n..........\n字符统计结束,用时: {}\n............\n'.format(end - start))
    
    
        @staticmethod
        def to_struct_time(t):
            struct_time = time.localtime(t)  # 将时间戳转换为struct_time元组
            hour = round((struct_time[3] + struct_time[4] / 60), 2)
            month = struct_time.tm_mon
            yday = struct_time.tm_yday
            return hour, yday, month
    
    
    if __name__ == '__main__':
        myGirl = '微信UID !!!'
        csv_path = '你导出的csv文件'
    
        chat = pd.read_csv(csv_path, sep=',', encoding='utf-8', usecols=[6,7,8])
        # createTime  talker    content‘ dtype: float, string,string
    
        Wechat = WechatAnalysis(chat, myGirl)
        # Get the hour distribution
        Wechat.get_time_hist()
        Wechat.get_hour_slice()
    
        # Get word cloud
        plt.figure(2)
        Wechat.get_word_cloud()  # Not slice the chinese words
        plt.figure(3)
        Wechat.get_word_cloud(stopwords={'嘻嘻', '嗯嗯', '嗯呢', '这样子', '这样子呀', '捂脸'})
        Wechat.get_word_statistic()
    

    参考:

    1. 爱情大数据https://blog.csdn.net/iphilo/article/details/79052325
    2. 词云可视化https://www.cnblogs.com/wkfvawl/p/11585986.html
    展开全文
  • Android 通过AccessibilityService实现微信聊天记录导出

    千次阅读 热门讨论 2018-11-30 15:32:33
    接上Android 微信聊天记录、联系人备份并导出为表格继续讲 不太了解AccessibilityService可以看看这篇文章 基本原理: 首先打开 DDMS 捕捉界面元素 拿到resourceid,调用方法 List&lt;AccessibilityNodeInfo...

    接上Android 微信聊天记录、联系人备份并导出为表格继续讲

    不太了解AccessibilityService可以看看这篇文章

    基本原理:

    首先打开 DDMS 捕捉界面元素

    拿到resourceid,调用方法

    List<AccessibilityNodeInfo> mListView = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a-c");

    能拿到一个 listview 的 list, 事实上 mListView 的长度只有1再通过

    myListView.get(0)

    就可以准确的拿到 listview了,然后可以调用滚动的方法

    /* 滚动 */
    listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) //向上滚动
    listNode.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) //向下滚动
    

    每滚动一次,根据 resourceId 获取当前屏幕的textview 集合,再遍历集合,调用 textview.getText() 方法,就能拿到聊天记录了; 

    下面直接贴代码,讲的很详细了,1:1的注释

    package com.cxk.wechatlog;
    
    import android.accessibilityservice.AccessibilityService;
    import android.content.Intent;
    import android.graphics.Rect;
    import android.os.Environment;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.accessibility.AccessibilityEvent;
    import android.view.accessibility.AccessibilityNodeInfo;
    import android.widget.Toast;
    
    import java.util.List;
    
    /**
     * Created by 曾轲
     * <p>
     * 获取即时微信聊天记录服务类,该功能在微信6.5.8上可正常使用
     */
    
    public class WeChatLogService extends AccessibilityService {
    
        /**
         * 聊天对象
         */
        private String ChatName;
    
        /**
         * 小视频的秒数,格式为00:00
         */
        private String VideoSecond;
    
    
        private String ChatRecord;
    
        /**
         *  根据聊天列表两次滚动后最底部的人是不是同一个,来判断聊天列表是否到了底部
         */
        private boolean listIsBottom = false;
        private String bottomName = "";
    
        /**
         *  根据聊天消息滚动前后三条是否重复,用来判断消息界面是否滚动到了最顶部
         */
        private boolean chatMessageIsTop = false;
        private String chatMessage1 = "";
        private String chatMessage2 = "";
        private String chatMessage3 = "";
        String topMessage1= "";
        String topMessage2= "";
        String topMessage3= "";
        /**
         * 聊天人的姓名
         */
        String chatName= "";
    
        /**
         * 聊天信息保存目录
         */
        private String mCurrApkPath =  Environment.getExternalStorageDirectory().getPath() + "/";
    
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            int eventType = event.getEventType();
            switch (eventType) {
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
                    String currentActivity = event.getClassName().toString();
                    //如果在微信界面
                    if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_LAUNCHUI)) {
                        Log.e("界面","微信主页");
                        AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
                        //获取聊天列表的 listview
                        List<AccessibilityNodeInfo> listview = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/bny");
                        if (listview!=null&&listview.size() != 0) {
                            //如果 listview 没有滑动到最底部,就一直无限循环遍历聊天记录
                            while (!listIsBottom) {
                                //获取当前页面聊天 list的单个条目和聊天人名字
                                List<AccessibilityNodeInfo> ChatList = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/agu");
                                List<AccessibilityNodeInfo> NameList = rootNodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/agw");
                                //如果没有聊天信息,直接跳出,并终止循环
                                if(NameList.size()==0||NameList==null){
                                    listIsBottom=true;
                                   return;
                                }
                                //获取当前list 最后一个人员的名字,与上一次滑动比较,如果相同说明到了最底部
                                String lastName = NameList.get(NameList.size() - 1).getText().toString();
                                if (bottomName.equals(lastName)) {
                                    listIsBottom = true;
                                } else {
                                    bottomName = lastName;
                                    listIsBottom = false;
                                    //遍历循环列表,进入聊天详情页进行备份
                                    if (ChatList!=null&ChatList.size() != 0) {
                                        for (int j = 0; j < ChatList.size(); j++) {
                                            //页面停顿防止过快无法找到目标
                                            try {
                                                Thread.sleep(500);
                                            } catch (InterruptedException e) {
                                                e.printStackTrace();
                                            }
                                            //进入聊天界面
                                            ChatList.get(j).performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                            //延迟防止页面还没完全载入,拿不到根节点信息
                                            try {
                                                Thread.sleep(500);
                                            } catch (InterruptedException e) {
                                                e.printStackTrace();
                                            }
                                            AccessibilityNodeInfo chatRootNodeInfo = getRootInActiveWindow();
                                            //遍历下一个人的聊天记录,需要重置定置状态
                                            chatMessageIsTop=false;
                                            getWeChatLog(chatRootNodeInfo);
                                        }
                                    }
                                    //循环遍历完当前页面的列表后滑动遍历下一个页面
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                    listview.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                                    try {
                                        Thread.sleep(1000);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
    
                               }
                            }else{
    
                        }
    
                        } else if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_CONTACTINFOUI)) {
                            Log.e("界面","联系人界面");
                        } else if (currentActivity.equals(WeChatTextWrapper.WechatClass.WECHAT_CLASS_CHATUI)) {
                            Log.e("界面","聊天界面");
                        }
                    }
                    break;
                }
    
    
        }
    
        /**
         * 遍历获取聊天消息
         * @param rootNode
         */
        private void getWeChatLog(AccessibilityNodeInfo rootNode) {
            if (rootNode != null) {
                //获取聊天人的姓名
                List<AccessibilityNodeInfo> listName = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/gp");
                if(listName!=null&&listName.size()!=0){
                   chatName = listName.get(0).getText().toString();
                    Log.e("name",chatName);
                }
    
                //获取聊天详情页的listview
                List<AccessibilityNodeInfo> listChatRecord = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/a3e");
                //如果没有聊天记录,直接返回
                if(listChatRecord!=null&&listChatRecord.size()==0){
                    Log.e("消息类型","当前没有聊天记录");
                    performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                    return;
                }
                //利用姓名创建文件
    
    
                //当消息详情不在最顶部时无限循环遍历消息记录
                while(!chatMessageIsTop){
                //有聊天记录,开始遍历循环
                //获取聊天头像list
                List<AccessibilityNodeInfo> imageName = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ik");
                //获取聊天信息list
                List<AccessibilityNodeInfo> record = rootNode.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/im");
    
                //有聊天头像说明有聊天记录(但是不一定有文字信息,可能是图片,分享等等)
                if (imageName!=null&&imageName.size() != 0) {
                    //如果record的 size 为零说明当前截取到的 list 没有文字
                    if (record.size() == 0) {
                        Log.e("消息类型","当前没有文字消息");
                        //判断当前这条消息是不是和上一条一样,防止重复
                            //获取聊天对象
                            ChatName = imageName.get(0).getContentDescription().toString().replace("头像", "");
                            Log.e("AAAA", ChatName + ":" + "对方发的是图片或者表情");
    
                            try {
                                Thread.sleep(800);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            //当前 listview 显示的都是非文字消息,乡下滚动一次后再次遍历循环
                            listChatRecord.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
                            return;
                    //有文本消息,开始辨析备份文本消息
                    }else {
                        //获取当前页面顶部的三条文字,与上一次滚动的三条文字对比,判断是否滚动到最顶部了
                        switch (record.size()) { //判断 size 防止索引越界
                            case 1:
                                 topMessage1 = record.get(record.size() - 1).getText().toString();
                                 topMessage2="";
                                 chatMessage2="";
                                 topMessage3="";
                                 chatMessage3="";
                                break;
    
                            case 2:
                                 topMessage1 = record.get(record.size() - 1).getText().toString();
                                 topMessage2 = record.get(record.size() - 2).getText().toString();
    
                                topMessage3="";
                                chatMessage3="";
                                break;
                            case 3:
                                 topMessage1 = record.get(record.size() - 1).getText().toString();
                                 topMessage2 = record.get(record.size() - 2).getText().toString();
                                 topMessage3 = record.get(record.size() - 3).getText().toString();
    
                                break;
    
                                default:
                                    topMessage1 = record.get(record.size() - 1).getText().toString();
                                    topMessage2 = record.get(record.size() - 2).getText().toString();
                                    topMessage3 = record.get(record.size() - 3).getText().toString();
    
                                    break;
                        }
                            //如果三条消息都重复证明已经滚动到了最顶部,返回聊天列表
                            if(chatMessage1.equals(topMessage1)&&chatMessage2.equals(topMessage2)&&chatMessage3.equals(topMessage3)){
                                chatMessageIsTop=true;
                                performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                            }else{
                                //不是第一条,重新赋值最后三条消息记录,遍历保存文字信息
                                chatMessage1=topMessage1;
                                chatMessage2=topMessage2;
                                chatMessage3=topMessage3;
                                chatMessageIsTop=false;
                                //遍历获取文字,并写入文件
                                for (int i = record.size()-1; i >= 0; i--) {
                                    //保存消息
                                    //Log.e("文字消息",record.get(i).getText().toString());
                                    //Log.e("index",record.get(i).toString());
                                    //获取文本消息的位置,根据位置来判断但前消息是谁发的
                                    Rect outBounds = new Rect();
                                    record.get(i).getBoundsInScreen(outBounds);
                                    //因为对方发送的消息的左侧 left 坐标是永远不会变的,自己发送的消息会被换行折叠,因此可以用此来区分
                                    String leftIndex = outBounds.left+"";
                                    //不同手机这个值不一样,需要自己适配
                                    if(leftIndex.equals("156")){
                                        //对方发送的消息
                                        saveMassage(chatName,record.get(i).getText().toString());
                                    }else{
                                        //自己发送的消息
                                        saveMassage("我",record.get(i).getText().toString());
                                    }
    
                                }
                                //遍历完毕后滚动下一屏聊天记录开始,并停止.8秒等待聊天记录加载
                                listChatRecord.get(0).performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
                                try {
                                    Thread.sleep(800);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
    
                        //有聊天列表但是没有聊天头像和文字消息.类似于招行信用卡的公众号,不作处理直接返回聊天列表遍历下一个
                        }else{
                            chatMessageIsTop=true;
                            performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                             }
                        }
                    }
                }
            }
    
        private void saveMassage(String chatName, String message) {
    
    
        }
    
    
        /**
         * 遍历所有控件,找到头像Imagview,里面有对联系人的描述
         */
        private void GetChatName(AccessibilityNodeInfo node) {
            for (int i = 0; i < node.getChildCount(); i++) {
                AccessibilityNodeInfo node1 = node.getChild(i);
                if ("android.widget.ImageView".equals(node1.getClassName()) && node1.isClickable()) {
                    //获取聊天对象,这里两个if是为了确定找到的这个ImageView是头像的
                    if (!TextUtils.isEmpty(node1.getContentDescription())) {
                        ChatName = node1.getContentDescription().toString();
                        if (ChatName.contains("头像")) {
                            ChatName = ChatName.replace("头像", "");
                        }
                    }
    
                }
                GetChatName(node1);
            }
        }
    
    
        /**
         * 必须重写的方法:系统要中断此service返回的响应时会调用。在整个生命周期会被调用多次。
         */
        @Override
        public void onInterrupt() {
            Toast.makeText(this, "我快被终结了啊-----", Toast.LENGTH_SHORT).show();
        }
    
        /**
         * 服务开始连接
         */
        @Override
        protected void onServiceConnected() {
            Toast.makeText(this, "服务已开启", Toast.LENGTH_SHORT).show();
            super.onServiceConnected();
        }
    
        /**
         * 服务断开
         *
         * @param intent
         * @return
         */
        @Override
        public boolean onUnbind(Intent intent) {
            Toast.makeText(this, "服务已被关闭", Toast.LENGTH_SHORT).show();
            return super.onUnbind(intent);
        }
    
        /**
         * 遍历所有控件:这里分四种情况
         * 文字聊天: 一个TextView,并且他的父布局是android.widget.RelativeLayout
         * 语音的秒数: 一个TextView,并且他的父布局是android.widget.RelativeLayout,但是他的格式是0"的格式,所以可以通过这个来区分
         * 图片:一个ImageView,并且他的父布局是android.widget.FrameLayout,描述中包含“图片”字样(发过去的图片),发回来的图片现在还无法监听
         * 表情:也是一个ImageView,并且他的父布局是android.widget.LinearLayout
         * 小视频的秒数:一个TextView,并且他的父布局是android.widget.FrameLayout,但是他的格式是00:00"的格式,所以可以通过这个来区分
         *
         * @param node
         */
        public void GetChatRecord(AccessibilityNodeInfo node) {
            for (int i = 0; i < node.getChildCount(); i++) {
                AccessibilityNodeInfo nodeChild = node.getChild(i);
    
                //聊天内容是:文字聊天(包含语音秒数)
                if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.RelativeLayout".equals(nodeChild.getParent().getClassName().toString())) {
                    if (!TextUtils.isEmpty(nodeChild.getText())) {
                        String RecordText = nodeChild.getText().toString();
                        //这里加个if是为了防止多次触发TYPE_VIEW_SCROLLED而打印重复的信息
    
                        if (!RecordText.equals(ChatRecord)) {
                            ChatRecord = RecordText;
                            //判断是语音秒数还是正常的文字聊天,语音的话秒数格式为5"
                            if (ChatRecord.contains("\"")) {
                                Toast.makeText(this, ChatName + "发了一条" + ChatRecord + "的语音", Toast.LENGTH_SHORT).show();
    
                                Log.e("WeChatLog",ChatName + "发了一条" + ChatRecord + "的语音");
                            } else {
                                //这里在加多一层过滤条件,确保得到的是聊天信息,因为有可能是其他TextView的干扰,例如名片等
                                if (nodeChild.isLongClickable()) {
                                    Toast.makeText(this, ChatName + ":" + ChatRecord, Toast.LENGTH_SHORT).show();
    
                                    Log.e("WeChatLog",ChatName + ":" + ChatRecord);
                                }
    
                            }
                            return;
                        }
                    }
                }
    
                //聊天内容是:表情
                if ("android.widget.ImageView".equals(nodeChild.getClassName()) && "android.widget.LinearLayout".equals(nodeChild.getParent().getClassName().toString())) {
                    Toast.makeText(this, ChatName+"发的是表情", Toast.LENGTH_SHORT).show();
    
                    Log.e("WeChatLog",ChatName+"发的是表情");
    
                    return;
                }
    
                //聊天内容是:图片
                if ("android.widget.ImageView".equals(nodeChild.getClassName())) {
                    //安装软件的这一方发的图片(另一方发的暂时没实现)
                    if("android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())){
                        if(!TextUtils.isEmpty(nodeChild.getContentDescription())){
                            if(nodeChild.getContentDescription().toString().contains("图片")){
                                Toast.makeText(this, ChatName+"发的是图片", Toast.LENGTH_SHORT).show();
    
                                Log.e("WeChatLog",ChatName+"发的是图片");
                            }
                        }
                    }
                }
    
                //聊天内容是:小视频秒数,格式为00:00
                if ("android.widget.TextView".equals(nodeChild.getClassName()) && "android.widget.FrameLayout".equals(nodeChild.getParent().getClassName().toString())) {
                    if (!TextUtils.isEmpty(nodeChild.getText())) {
                        String second = nodeChild.getText().toString().replace(":", "");
                        //正则表达式,确定是不是纯数字,并且做重复判断
                        if (second.matches("[0-9]+") && !second.equals(VideoSecond)) {
                            VideoSecond = second;
                            Toast.makeText(this, ChatName + "发了一段" + nodeChild.getText().toString() + "的小视频", Toast.LENGTH_SHORT).show();
    
                            Log.e("WeChatLog","发了一段" + nodeChild.getText().toString() + "的小视频");
                        }
                    }
    
                }
    
                GetChatRecord(nodeChild);
            }
        }
    
    
    }
    package com.cxk.wechatlog;
    
    public class WeChatTextWrapper {
        public static final String WECAHT_PACKAGENAME = "com.tencent.mm";
    
    
        public static class WechatClass{
            //微信首页
            public static final String WECHAT_CLASS_LAUNCHUI = "com.tencent.mm.ui.LauncherUI";
            //微信联系人页面
            public static final String WECHAT_CLASS_CONTACTINFOUI = "com.tencent.mm.plugin.profile.ui.ContactInfoUI";
            //微信聊天页面
            public static final String WECHAT_CLASS_CHATUI = "com.tencent.mm.ui.chatting.ChattingUI";
        }
    
    
        public static class WechatId{
            /**
             * 通讯录界面
             */
            public static final String WECHATID_CONTACTUI_LISTVIEW_ID = "com.tencent.mm:id/ih";
            public static final String WECHATID_CONTACTUI_ITEM_ID = "com.tencent.mm:id/iy";
            public static final String WECHATID_CONTACTUI_NAME_ID = "com.tencent.mm:id/j1";
    
            /**
             * 聊天界面
             */
            public static final String WECHATID_CHATUI_EDITTEXT_ID = "com.tencent.mm:id/a_z";
            public static final String WECHATID_CHATUI_USERNAME_ID = "com.tencent.mm:id/ha";
            public static final String WECHATID_CHATUI_BACK_ID = "com.tencent.mm:id/h9";
            public static final String WECHATID_CHATUI_SWITCH_ID = "com.tencent.mm:id/a_x";
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.cxk.wechatlog">
    
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <service
                android:name=".WeChatLogService"
                android:enabled="true"
                android:exported="true"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService" />
                </intent-filter>
                <meta-data
                    android:name="android.accessibilityservice"
                    android:resource="@xml/wechatlog_service_config"></meta-data>
            </service>
        </application>
    
    </manifest>

    源码地址 :

    https://github.com/KeZengOo/AccessibilityServiceWeChatHelper

    注意事项 :

    1.微信每一次更新都会导致 resourceId 变化,所以尽量不要更新或者及时更改代码中的 resourceId

    2.旧版本的微信每一个文本框都是用的 textview,可以直接调用 getText()获取聊天文字,新版本为了防止这个操作,变成自定义的 View 了,目前我还没有找到解决方案....希望有大神能点拨点拨...

    3.我的代码在微信6.5.8上运行完美,高于该版本的都不行(参考第二条)

    4.目前只是第一个 demo 版本,有很多不足的地方,遍历到公众号,等非聊天窗口时,可能会有异常

    5.安装后必须在设置里打开辅助功能里的对应开关,再切换到微信,即可正常运行

    6.如果觉得还不错,请给我点个赞吧

    展开全文
  • 微信聊天记录导出工具WeChatExporter开源啦!

    万次阅读 多人点赞 2018-08-07 17:57:53
    微信聊天记录导出工具。无需越狱手机,即可导出备份微信聊天记录。目前支持文字、语音、图片、视频的查看。 项目基于nodejs实现,框架采用angularjs 目前支持导出iOS系统导出,软件运行仅限MacOS系统。(其实安卓...

    【2019年08月21日更新】
    距离第一次发布软件已经有了许多新功能和稳定性上的提升,本文的一些内容已经过时,欢迎直接到GitHub上看ReadMe:https://github.com/tsycnh/WeChatExporter


    之前曾经写过一个导出微信聊天记录的工具,偶尔自己用一下,现在免费开源出来,希望大家喜欢。

    WeChatExporter

    微信聊天记录导出工具。无需越狱手机,即可导出备份微信聊天记录。目前支持文字、语音、图片、视频的查看。
    项目基于nodejs实现,框架采用angularjs

    目前支持导出iOS系统导出,软件运行仅限MacOS系统。(其实安卓和Windows系统也能用,只是现在懒得适配多平台)
    项目地址:https://github.com/tsycnh/WeChatExporter
    使用方法:

    一、准备工作

    Step1:数据导出:
    首先需要将微信聊天数据进行导出。目前只支持iOS系统,如果你用的是安卓机,可以尝试将聊天记录迁移到iPad上,再导出。

    按照下图使用iTunes备份整机数据,注意不要选择给iPhone备份加密
    这里写图片描述

    使用第三方软件导出微信备份数据,这里使用的是iMazing,需要导出的是Documents文件夹。
    这里写图片描述

    Step2:安装nwjs(0.23.1版本) 官网:https://nwjs.io

    二、运行软件

    Step1:下载项目 git clone https://github.com/tsycnh/WeChatExporter

    Step2: cd path/to/WeChatExporter

    Step3: cd development

    Step4: 运行nwjs /path/to/nw/nwjs.app/Contents/MacOS/nwjs .

    即可运行导出工具。

    三、使用软件

    目前工具由三部分组成:

    soft1: 用来查看并确定要导出的聊天对象

    soft2:用来导出并转换数据

    soft3:直接查看聊天内容


    Step1: 点击soft1进入分析模式,输入导出的Documents文件夹路径,然后进入分析模式

    Step2: 左上角显示的是在当前手机上登陆过的微信帐号,点击任意一个将在左下角显示和你聊过天的朋友,默认只显示聊天消息总数超过100的朋友(或群聊)。

    Step3:点击左下角任意一聊天对象,会在右侧显示10条最近的聊天记录,以做确认之用。

    Step4:这时右上角会显示两串红色的字符,分别是你的微信账户和聊天对象(均经过MD5加密)。将这两个数值复制下来。
    这里写图片描述

    Step5:点击左上角微信备份按钮跳转到主页,点击Soft2 进入解析多媒体模式。

    Step6:按要求填写表单,日期区间可以控制导出聊天记录的时间范围,默认不填表示全部导出。然后点击开始生成数据。生成结束后会得到一个文件夹,即path/to/output 里面存放了所有需要的信息。至此Documents目录已经没有用了,可以删除。
    这里写图片描述

    Step7:回到主页进入Soft3 页面,输入刚到导出的output目录,即可开始查看导出的聊天记录了。

    之后再查看直接进入Soft3页面即可。
    这里写图片描述

    PS:目前有些流程还是有些累赘和繁琐,有待改进
    欢迎有能力同学来对这个项目做贡献!
    项目地址:https://github.com/tsycnh/WeChatExporter


    待添加功能

    • soft1和soft2合并
    • 为微信用户添加头像
    • 为微信用户添加昵称
    • 导出html功能
    • 聊天查看页面增加图像点击放大
    展开全文
  • 微信聊天记录导出到电脑的方法.doc
  • 微信聊天记录导出(iOS) [2019.7.24]

    千次阅读 2019-07-24 14:05:44
    博客地址:https://www.busby.com.cn/2019/07/24/微信聊天记录导出(iOS)[2019.7.24]/ 最前 前不久很久,我的小傻瓜女票误删了我们俩的微信聊天记录,也没有iOS系统或PC端微信聊天记录的备份。无奈微信聊天记录在...
  • 本文的最终目的是将手机微信聊天记录导出到电脑里,变成txt文本文件,然后对其进行分析。 网上有一些工具也可以完成这个功能,但是基本都是付费的。手动操作的话,找了很多的博客,基本没有完全有效的。最终找到...
  • android微信聊天记录导出到电脑【微信安卓版技巧】  微信,对它又爱又恨!爱的是微信能替代很多手机通话短信,恨的是有些较早前的手机不能友好支持,比如ytkah之前用的i8000,挺上手的,就是没办法装...
  • 微信聊天记录导出

    2018-01-03 02:32:58
    关注过我的人大概都知道,2016年我就实现了一版基于iOS的聊天记录导出功能(https://zhuanlan.zhihu.com/p/23034838),但因为现在绝大多数用户的iPhone都没越狱,我就只能发布非官方的微信版本,可这样就算是违背...
  • 本文记载了将微信聊天记录导出到电脑成html文件到过程
  • 微信里积累了数年的聊天记录,连iPhone都吃不消了,可惜你依旧不能删掉它们。把重要的聊天记录导...第二部 聊天记录导出 根据选择的账号和联系人导出聊天记录,瞬间即可导出选中的聊天记录。支持增量导出,即有新...
  • 最近想做一个可以无聊的时候和微信好友对话的功能,用到些许...首先是导出微信聊天记录到txt: https://blog.csdn.net/swinfans/article/details/88712593 此处假设大家已经完成了聊天记录导出到txt的任务... ...
  • 如何导出微信聊天记录

    千次阅读 2021-04-22 15:09:39
    有时候聊天记录里有些碎片化的记录想要整理出来单独看,所以去调研了一下有什么好的办法 太长不看版 最便捷的方式是多选,然后邮件发送,然后稍微处理下格式就行了! 想自己捣鼓一下的: ios的朋友可以试
  • 导出 Mac 版微信聊天记录

    千次阅读 2021-03-29 12:46:21
    macOS 微信的“备份与恢复”功能只能从手机微信导出到 Mac, 但是微信其实又在本地存了加密的 sqlite3 数据库; 本地数据库的是一系列 *.db 文件,可以用如下命令查看, ls -alh ~/Library/Containers/...
  • fetchall返回筛选结果 data=open(“聊天记录.txt”,‘w+’,encoding=‘utf-8’) for i in value: data.write(i[0]+’\n’) 将筛选结果写入 聊天记录.txt data.close() cursor.close() conn.close() 关闭连接 记得把...
  • 之前给大家详细讲解过如何用小米手机导出微信聊天记录:godweiyang:微信聊天记录导出为电脑txt文件教程今天再给大家讲解一下如何直导出mac版本微信的聊天记录,当然如果你没有mac,那可以直接关闭这篇文章了。...
  • 对于如何导出手机上的微信聊天记录,网上绝大部分教程提到的“楼月微信聊天记录导出恢复助手”和“手机博士微信聊天记录查看”等软件都是收费的,免费版本只能查看很少的几条聊天记录并且不能导出。在这里提供一种...
  • 1.导出Mac版微信聊天记录 Mac版微信在本地存放了聊天记录的数据库,数据库使用的是开源的 sqlcipher加密了里面的数据。在终端输入下面命令,可以查看这些数据库的路径。 ls -alh ~/Library/Containers/...
  • Mac导出微信聊天记录到world

    千次阅读 2020-05-02 00:02:43
    很多人都知道PC版微信软件可以对手机微信聊天记录进行备份,于是很多人产生了一个疑问,怎么在电脑上打开这些微信聊天记录进行查看呢?如何把这些记录保存到Word文档或txt文本中呢?如何导出里面的语音消息,小视频...
  • 微信聊天数据导出

    2021-04-15 10:00:04
    1、使用iPhone手机,聊天记录都已经在手机上; 2、使用Mac电脑上的iTunes连接iPhone,请注意,不要选择加密iPhone备份内容,然后把手机上的资料库整个同步到Mac电脑; 3、去http://wxbackup.imxfd.com点下载软件...
  • 以Timemachine备份为例,如果没有timemachine没有备份,也可以参照下面方法在旧Mac电脑上操作,核心方式就是找到旧数据用替换的方式迁移旧的微信数据,以保证数据不丢失。  搜索输入:com.tencent.xinWeChat ...
  • 获取微信聊天记录导出为Excel

    千次阅读 2019-07-31 18:15:33
    获取微信聊天记录导出为Excel (ios端) 工具 iTunes 楼月免费iTunes备份管理器 DB Browser for SQLite python 步骤 通过iTunes备份ipone中的数据到电脑上, 打开楼月免费iTunes备份管理器选择备份的记录 导出...
  •  现在大部分人都在用微信进行通信,微信官方宣称服务器不会存储用户的聊天信息,那好,微信聊天记录存储在哪里?以什么方式进行存储的?  网上查证,微信聊天记录是在/data/data/com.tecent.mm/MicroMsg目录下...
  • 安卓手机导出微信聊天记录 [ios导出文末有文章,但没试过]大致流程!!第一步:安卓手机获取root权限(必须获取)一、备份聊天记录到电脑第二步:电脑安装手机模拟器第三步:获取聊天记录数据库 EnMicroMsg.db第四步:...
  • 【安卓wechat微信导出聊天记录

    千次阅读 2021-12-27 10:09:02
    5、将电脑微信聊天记录再次备份到模拟器微信里。如果你的电脑微信被退出了,可以登录模拟器微信,点击扫码登录哦。 6、打开右侧小图标,点击打开安卓文件夹 7、左侧点击根目录,进入/data/data/com.tencent.m
  • 对于微信聊天记录,可以通过采集聊天记录,通过聊天数据生成词云两步实现 一、聊天记录数据采集 整个任务中最困难的即为聊天数据采集。由于QQ和微信的聊天记录提取难度不同,对于QQ聊天数据,简单的导出即可。对于...
  • 用9008把userdata分区导出, 参考:https://zhuanlan.zhihu.com/p/35422254 可能会失败 windows api readfile failed your device is probably not on this port 01:47:56: ERROR: function: sahara_rx_data:194 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,556
精华内容 1,022
关键字:

微信聊天记录导出