首页 > 其他分享 >微信聊天记录报告制作教程

微信聊天记录报告制作教程

时间:2023-09-04 10:55:12浏览次数:51  
标签:教程 const 微信 聊天记录 item isSend type 模拟器

前言

马上要到我们家小宝的生日了,思来想去没想好送什么东西,在上班摸鱼的时候无意间看到了三年前很火的《微信聊天记录统计报告》,这个东西很好玩,所以我决定搞一下。但是大体浏览了一下,发现事情没有想象的那么简单,所以借这次机会讲一整个过程记录下来,也是一次学习的机会啦。

参考文章:[https://zhuanlan.zhihu.com/p/589718049]、[https://www.itcode1024.com/196633/]

本教程使用的工具,请务必在开始前准备好:
逍遥模拟器[https://www.xyaz.cn/]
sqlcipher(加密数据库破解)[https://url21.ctfile.com/f/27727221-930256389-b825f4?p=3889](访问密码: 3889)

微信聊天记录导出EXCEL

这一步在参开文章说的比较全面,基本搬抄,总要将他导入导出的。

将聊天记录导入到电脑模拟器

将手机聊天记录迁移到电脑上模拟器里的微信,再将聊天记录文件从模拟器传输到电脑中,最终获得EnMicroMsg.db文件即可。

  1. 打开逍遥模拟器,将设置内中打开root权限。
    微信截图_20230824142324.png
  2. 在模拟器上安装微信,模拟器分辨率设置为手机形式的窄长型,注意不必着急登录,否则会把你正常手机上的微信踢下线。
    QQ截图20230824142504.png
  3. 进入你平时正常使用的手机上的微信,点击设置->聊天->聊天记录备份与迁移->迁移->迁移到手机/平板微信。
    Screenshot_2023-08-24-14-22-14-744_com.tencent.mm.jpgScreenshot_2023-08-24-14-22-18-197_com.tencent.mm.jpgScreenshot_2023-08-24-14-22-20-240_com.tencent.mm.jpgScreenshot_2023-08-24-14-22-26-068_com.tencent.mm.jpgScreenshot_2023-08-24-14-22-28-361_com.tencent.mm.jpg
  4. 选择聊天记录的时间和内容。内容强烈建议选择”不含图片/视频/文件”,否则迁移过程可能会非常慢。时间以你想统计的年月日跨度为准,可以导入几年或者几个月的。尽量选取你认为有必要统计的好友的私聊记录不要点击全选,否则会很慢很慢。
    Screenshot_2023-08-24-14-22-39-571_com.tencent.mm.jpg
  5. 选择完成后点击迁移聊天记录,会出现如下的二维码,此时在电脑的模拟器上登录微信,用笔记本电脑自带的摄像头扫描该二维码即可开始同步。如果电脑没有摄像头,可以将该二维码截图后通过QQ、百度网盘、蓝牙等方式传输到电脑,之后在安卓模拟器里的微信点击扫描时选择实时截取屏幕的方式(如下图)扫取电脑上打开的二维码图片即可。
    Screenshot_2023-08-24-14-22-52-191_com.tencent.mm.jpgScreenshot_2023-08-24-13-54-56-106_com.tencent.mm.jpg
    在这一步可能会遇到模拟器扫描二维码闪退问题,解决方法:[官网教程],我在这里用简单的方法说一下:
    在桌面系统管理安装"谷歌安装器",在 设置->设备,将摄像头改为虚拟,就可以扫描电脑上的画面。
    image.png
    image.png
    image.png
    image.png
    在这一步提示手机和电脑不在同一个wifi,可以在设置->网络->WIFI热点 改成与你手机连接的wifi同一个名字,前提是必须要模拟器的电脑和手机连接的是同一个网络
    微信截图_20230824142524.png
  6. 同步完成后,打开模拟器里的文件管理器,在其 根目录/data/data/com.tencent.mm/MicroMsg/(一个32位字符串命名的文件夹中)中找到EnMicroMsg.db文件。这里的(32位字符串命名的文件夹)如果你只在模拟器上登陆过一个微信的话就只有一个,如果有两个这样命名的文件夹的话(如下图),那就每个都打开看看哪个文件夹中能找到EnMicroMsg.db。找到后将该db文件拷贝到电脑上。
    关于如何从模拟器中复制文件到电脑文件夹中请参考:1.将文件选中,返回内部共享空间。2.打开Download文件夹,该文件夹是与电脑共享。3.点击左下角点,粘贴所选项,就可以就将内容粘贴在此。
    image.pngimage.pngimage.pngimage.png
    image.png
    参考教程:

    如果找不到该文件或路径,请按照以下方法:1.点击左下角设置。2.点击常规设置3.将访问模式改成"超级用户访问模式",获取最高权限,再点击根目录就能找到上面路径。
    image.pngimage.pngimage.pngimage.pngimage.png

聊天记录破解

这一步是对1.1中获得的EnMicroMsg.db文件进行破解,并且生成csv文件。聊天记录破解需要用到破解软件sqlcipher,和破解密码。
破解密码是手机的IMEI码和你微信的uin码直接拼接相连后,换算成32位小写的MD5的前七位。
手机IMEI码获取方式:在模拟器输入*#06#后自动出现,但现在的最新版本 IMEI (手机序列号)为固定值为1234567890ABCDEF,可以都试一下。模拟器的是861009457165503、手机的是1234567890ABCDEF,如果密码不正确就都试试。
微信的uin码获取方式:雷电模拟器中/data/data/com.tencent.mm/shared_prefs/ 找到文件auth_info_key_prefs.xml,再传输到电脑中用记事本打开,找到auth_uin,其中value后面跟着的就是微信uin码。
image.pngimage.pngimage.png
然后将手机IMEI码和微信uin码直接相连后,用换算工具换算成小写32位md5值,其前7位就是破解密码。
image.png
打开工具sqlcipher,打开我们导出的db文件,输入密码,就可以看到好多列表,依次点击File->Export->Table as CSV file,选择message表导出,一定要自己加上后缀.csv。导出就是excel。
image.pngimage.pngimage.png
image.png
image.png
到这一步就搞到了excel的数据了,自由发挥了,没有IT基础的,可以参考[参考教程]里的方法,如果有IT基础,java,js,python等都可以解析excel数据。可以自己DIY展示。

聊天记录数据分析(JavaScript)

我这里使用前端技术实现分析,做一个可视化的解析工具(python确实不太熟练 ==,如果你会使用python我还是推荐这个)。以下内容是给有代码基础的朋友阅读,如果是不懂代码的朋友碰巧看到了这篇文章,也可以直接下载我生成的exe文件,在电脑上安装使用,链接:[https://github.com/Aolcycle/wx-crecords]。
我把以下demo放到了github[https://github.com/Aolcycle/wx-crecords],需要的可以直接拉取。

开发思路

写其他语言的朋友可以看这里的思路,写法虽然不一样,但是思路是一样的。

  1. 上传CSV带客户端进行解析,获得解析后的数据(比如json)。
  2. 得到数据后筛选名称,比如我想统计李华的聊天记录,那我先把我跟他的聊天记录进行筛选。
  3. 筛选完成后获取可以统计内容,比如,我们聊天当中发的什么内容最多,我们最晚的一次聊天是到几点,我们说话聊天最多的一天是哪天之类的。
  4. 统计完成后生成模板数据,展示到模板上或者其他随便什么地方。

这里提供了两个表格,可以按照表内内容进行检索。

列名 内容
msgId 按所有消息时间顺序的唯一编号
type 聊天内容类型
isSend 标识消息是自己发送还是对方发送,1表示自己,0表示对方
createTime 聊天时间
talker 单聊的wxid或群聊编号"XXXX@chatroom"
content 聊天内容,单聊直接显示内容,群聊格式为“wxid:\n”内容
type值 表示内容
1 文本内容
2 位置信息
3 图片及视频
34 语音消息
42 名片(公众号名片)
43 图片及视频
47 表情包
48 定位信息
49 小程序链接
10000 撤回消息提醒(XXXX撤回了一条消息)
1048625 照片
16777265 链接
285212721 文件
419430449 微信转账
436207665 微信红包
469762097 微信红包
·11879048186 位置共享
(还有未知type信息,待补充)

使用的技术栈:

  • electron
  • nodejs
  • TypeScript
  • Vue3
  • vite
  • antdv
  • xlsx-0.20.0

创建系统框架

这里就不造轮子了,快速构建框架,使用的是脚手架,本项目是基于nodejs框架搭建的,如果电脑没有nodejs请先到官网下载安装[https://nodejs.org/en]

-> nodejs -v //18.16.0
-> yarn create @quick-start/electron
name: wx-crecords
-> cd wx-crecords
-> yarn 
-> yarn dev

UI框架方面,只要有个文件上传组件的框架都可以,比如element-ui等都可以,可以根据自己的熟练程度自由使用,由于之前我写过类似的,所以这里选择了antdv4.x。
antdv4.x文档地址

-> yarn add [email protected]
// 官网有按需引入和全局引入代码示例,我为了项目大小使用了按需引入,详细请见官网
// https://next.antdv.com/docs/vue/getting-started-cn
import { createApp } from 'vue';
import Antd from 'ant-design-vue';
import App from './App';
import 'ant-design-vue/dist/reset.css';

const app = createApp(App);

app.use(Antd).mount('#app');

还有用到的其他npm库,在这里一起引入了。

-> yarn add https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz //处理excel表格

编写导入CSV代码

将代码引入完成后,在App.vue页面开始编写代码(可以按照自己的代码规范来,我方便起见直接写在了App页面了),先实现上传excel文件进行解析。

<template>
  <div>
    <Upload
      :file-list="fileList"
      :max-count="1"
      :custom-request="handleImport"
      accept=".xls, .xlsx, .csv"
      @remove="handleRemove"
    >
      <AButton> 选择文件 </AButton>
    </Upload>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { Upload, Button } from 'ant-design-vue' // 按需导入

export default defineComponent({
  name: 'App',
  components: {
    Upload,
    AButton: Button
  }
})
</script>

<script setup lang="ts">
import { ref } from 'vue'
import type { UploadProps } from 'ant-design-vue'
import { read, utils } from 'xlsx'

// 导入的文件列表
const fileList = ref<UploadProps['fileList']>([])

// 导入后文件的处理
const handleImport = (file): void => {
  fileList.value = [...(fileList.value || []), file.file]
  const reader: FileReader = new FileReader()
  reader.readAsBinaryString(file.file)
  reader.onload = (ev): void => {
    if (ev.target) {
      const res = ev.target.result
      const worker = read(res, { type: 'binary' })
      // 将返回的数据转换为json对象的数据
      const jsonData = utils.sheet_to_json(worker.Sheets[worker.SheetNames[0]])

      console.log(jsonData)
    }
  }
}

// 删除文件
const handleRemove: UploadProps['onRemove'] = (file) => {
  if (fileList.value) {
    const index = fileList.value.indexOf(file)
    const newFileList = fileList.value.slice()
    newFileList.splice(index, 1)
    fileList.value = newFileList
  }
}
</script>

<style lang="less"></style>

log日志打印一下,导入了excel中所有的数据,我这里是23万条数据,大约10秒中的样子。
image.png
在这里可能会遇到乱码问题,只需要使用excel软件打开csv,另存为xlsx格式,再打开上传就没问题了。
image.png

列表的展示和筛选

创建Table列表接受展示xlsx里的内容,后期好处理。

<ATable :data-source="dataSource" :columns="columns" :scroll="{ y: 550 }">
  <template #emptyText> 暂无数据 </template>
</ATable>
// table列表数据
const dataSource = ref<[]>([])

interface ColumnsType {
  title: string
  key: string
  align: string
  ellipsis?: boolean
  width?: number
  dataIndex?: string
}
// 表格header
const columns = ref<ColumnsType[]>([
  {
    title: '聊天顺序编号',
    key: 'msgId',
    dataIndex: 'msgId',
    width: 120,
    align: 'center'
  },
  {
    title: '聊天用户Id',
    dataIndex: 'talker',
    key: 'talker',
    width: 120,
    align: 'center'
  },
  {
    title: '聊天时间',
    dataIndex: 'createTime',
    key: 'createTime',
    width: 90,
    align: 'center'
  },
  {
    title: '聊天内容类型',
    dataIndex: 'type',
    key: 'type',
    width: 90,
    align: 'center'
  },
  {
    title: '标识消息发送者(0自己1对方)',
    dataIndex: 'isSend',
    key: 'isSend',
    width: 90,
    align: 'center'
  },
  {
    title: '聊天内容',
    dataIndex: 'content',
    key: 'content',
    ellipsis: true,
    width: 150,
    align: 'left'
  }
])

const handleImport = (file): void => {
  //省略...
	dataSource.value = jsonData
  //省略...
})

image.png

编写统计数据代码和导出数据

我这里直接上代码了,因为统计代码这一块,大家可以自己编写,我这里就直接上我的demo源码了,也没有优化就只简单地罗列代码,我的建议还是有基础的同学自己优化。我的gitee上也会随时更新。

	let closestTime = 0
  let closestDate = ''
  let closesContent = ''
  let smallestDifference = 24 * 60 * 60 * 1000 // 初始化为一天的毫秒数
  const targetTime = '04:00:00'
  let isSend_1 = 0
  let isSend_0 = 0
  let type_10000 = 0
  let wenzi = 0
  let yuyin = 0
  let biaoqing = 0

  let earliestTimestamp = Number.MAX_SAFE_INTEGER
  let earliestDate = ''
  let earliestContent = ''

  dataSource.value.forEach((item) => {
    if (item.talker === 'wxid_cjsjytzw4wms22') {
      dataList.push({
        msgId: item.msgId,
        talker: item.talker,
        createTime: item.createTime,
        type: item.type,
        isSend: item.isSend,
        content: item.content
      })

      if (item.__createTime__ < earliestTimestamp) {
        earliestTimestamp = item.__createTime__
        earliestContent = item.content
        earliestDate = item.createTime
      }

      if (item.isSend == '0') {
        isSend_0++
      } else {
        isSend_1++
      }
      if (item.type == '10000') {
        type_10000++
      }
      if (item.type == '1') {
        try {
          wenzi = wenzi + (item.content.length ? item.content.length : 0)
        } catch (e) {}

        dataList_type_1.push({
          msgId: item.msgId,
          talker: item.talker,
          createTime: item.createTime,
          type: item.type,
          isSend: item.isSend,
          content: item.content
        })
      }
      if (item.type == '34') {
        yuyin++
      }
      if (item.type == '47') {
        biaoqing++
      }

      const time = item.createTime.split(' ')[1] // 提取时间部分
      const timeObj = dayjs()
        .hour(parseInt(time.split(':')[0]))
        .minute(parseInt(time.split(':')[1]))
        .second(parseInt(time.split(':')[2]))
      const targetTimeObj = dayjs()
        .hour(parseInt(targetTime.split(':')[0]))
        .minute(parseInt(targetTime.split(':')[1]))
        .second(parseInt(targetTime.split(':')[2]))

      let difference = Math.abs(timeObj.diff(targetTimeObj, 'millisecond'))

      // 如果时间跨越午夜(00:00:00),需要考虑这一点
      if (difference > 12 * 60 * 60 * 1000) {
        difference = 24 * 60 * 60 * 1000 - difference
      }

      if (difference < smallestDifference) {
        smallestDifference = difference
        closestTime = time
        closestDate = item.createTime
        closesContent = item.content
      }
    }
  })
  console.log(
    '我们一共聊了:',
    dataList.length,
    '条, 其中我发的消息有',
    isSend_1,
    '条, 你发的消息有',
    isSend_0,
    '条, 我发消息的占比是',
    (isSend_1 / (isSend_1 + isSend_0)) * 100
  )
  console.log('撤回消息了:', type_10000, '条')
  console.log('联系人最早一条信息的时间是:', earliestTimestamp, earliestDate)
  console.log('联系人最早一条信息的内容是:', earliestContent)
  const date1 = dayjs(dataList[0].createTime).format('YYYY-MM-DD')
  console.log(
    '最晚的聊天的时间是:',
    closestTime,
    '那天是:' + closestDate,
    '内容是:',
    closesContent
  )
  console.log('只看文字消息,我们一共聊了:' + wenzi + '字')
  console.log('我们一共发了:' + yuyin + '条语音消息,表情包发了' + biaoqing + '条')

导出的话使用了方法:

const handleExport = (): void => {
  const lists = ref<DataType[]>(dataList_type_1)
  const titleArr = ['msgId', 'talker', 'createTime', 'type', 'isSend', 'content']
  exportExcel(lists.value as [], '聊天记录', titleArr, 'sheet1')
}
import * as XLSX from 'xlsx'

/**
 * 导出
 * @param json 数据
 * @param name 文件名
 * @param titleArr 标题
 * @param sheetName 表名
 */
export const exportExcel = (
  json: [],
  name: string,
  titleArr: string[],
  sheetName: string
): void => {
  /* convert state to workbook */
  const data: Array<string[]> = []
  const keyArray: string[] = []
  const getLength = (obj): number => {
    let count = 1
    for (const i in obj) {
      // eslint-disable-next-line no-prototype-builtins
      if (obj.hasOwnProperty(i)) {
        count++
      }
    }
    return count
  }

  if (!Array.isArray(titleArr)) {
    titleArr = []
  }

  for (const key1 in json) {
    // eslint-disable-next-line no-prototype-builtins
    if (json.hasOwnProperty(key1)) {
      const element: object = json[key1]
      const rowDataArray: string[] = []
      for (const key2 in element) {
        // eslint-disable-next-line no-prototype-builtins
        if (element.hasOwnProperty(key2)) {
          const element2 = element[key2]
          rowDataArray.push(element2)
          if (keyArray.length < getLength(element)) {
            keyArray.push(key2)
          }
        }
      }
      data.push(rowDataArray)
    }
  }
  // keyArray为英文字段表头
  data.splice(0, 0, titleArr)
  const ws = XLSX.utils.aoa_to_sheet(data)
  const wb = XLSX.utils.book_new()
  // 此处隐藏英文字段表头
  // var wsrows = [{ hidden: true }];
  // ws['!rows'] = wsrows; // ws - worksheet
  XLSX.utils.book_append_sheet(wb, ws, sheetName)
  /* generate file and send to client */
  XLSX.writeFile(wb, name + '.xlsx')
}

这里的导出可以自由编写导出内容,如果要使用以下的ROSTCM6,只需要导出“content”列就可以。

使用ROSTCM6汇总词汇

说实话我对这个东西的运用也不是很熟悉,网上有很多教程,大家可以直接baidu.com搜一下,我这里只做简单的使用。
1.我这里使用的是 功能性分析->词频分析,将我们导出的xlsx文件另存为txt文件,导入到待处理文件中,点击确定后就会生成字频统计了。不过我没搞懂排序应该怎么来,但是又没有太多时间,也就用肉眼统计了。如果大家又更好办法欢迎留言或提交到issues,一起讨论进步ROST+系列人文社科研究大数据计算工具.zip: [https://url21.ctfile.com/f/27727221-930256395-b892cf?p=3889] (访问密码: 3889)

image.png
image.png
2.使用词频,也是同理的,在功能性分析->词频分析。把“只输出排名前”,改为15000,获取的更准确,(我自己试倒是没什么差距,看个人使用吧)
image.png

制作H5页面

我这里使用的是 MAKA设计 当然,其他的一些H5设计软件或者网站都可以,根据自己喜好来。作为一个臭写代码的确实审美不在线(==),我做的效果确实不咋地,这里放出两个互联网上做的很好的H5,放在这里大家可以参考:
image.pngimage.pngimage.png
https://maka.im/mk-viewer-7/pcviewer/603283060/MPOA0SYBW603283060?platform_type=web
https://maka.im/pcviewer/603314886/QQI1RQLQW603314886
https://u603912349.viewer.maka.im/k/0HAFA3YUW603912349
制作过程就是拖拉拽,没什么好讲的,可以参考着来。

标签:教程,const,微信,聊天记录,item,isSend,type,模拟器
From: https://www.cnblogs.com/aoley/p/17676337.html

相关文章

  • 微信小程序自定义头部导航栏
    微信小程序自定义头部导航栏虽然小程序自带的顶部导航栏比较好用,但是扩展性比较差。在实际开发中我们经常需要针对导航栏进行一些功能性操作。比如点击返回按钮返回到固定的页面,设置其他背景颜色字体颜色以及增加一些按钮等等。下面我们一起来看看导航栏的定制;先看下demo的效果:......
  • 微信小程序开发基础知识一
    小程序和普通前端网页开发的区别1、运行环境:微信小程序是在微信内部运行的,而普通前端网页是在浏览器中运行的。这意味着微信小程序必须依赖微信提供的运行时环境,而普通前端网页可以在不同的浏览器上运行。因此,微信小程序开发需要专门的开发工具和技术栈。2、开发语言:微信小程序主......
  • 全开源风车im源码(前端uniapp可发布H5及app/后端java含视频搭建教程)
    互联网彻底改变了我们的沟通方式,电子邮件是迄今为止采用最快的通信形式。不到二十年前,还没有多少人听说过它。现在,我们中的许多人都用电子邮件而不是写信,甚至打电话给别人,世界各地的人们每天发送数十亿封电子邮件。源码:ms.jstxym.top但有时甚至电子邮件也不够快。您可能不知道您......
  • 基于微信小程序的图书馆座位预约系统设计与实现-计算机毕业设计源码+LW文档
    选题意义: 该系统可以监测到图书馆座位的使用情况,便于学生查询图书馆的分布、座位多少、是否空闲等基本数据。学生可以通过手机或者计算机等终端进行座位预约,方便快捷。对于占座现象,学生可以通过系统进行反馈,方便图书馆管理人员及时处理。基于微信小程序的图书馆座位预约系统的使......
  • 这可能是Github上最全面的Flutter教程,带你玩转Flutter
    Flutter是什么来头?Flutter是一款开源UI工具包,可利用单一代码库构建本地编译的移动、Web和桌面应用程序。Flutter由谷歌牵头开发,允许开发者构建出具有良好表现力、灵活设计、样式美观且运行迅速的应用程序。Flutter的核心语言为Dart,这是一种现代多范式语言,能够面向多个平台......
  • 无涯教程-JavaScript - TTEST函数
    TTEST函数取代了Excel2010中的T.TEST函数。描述该函数返回与学生t检验相关的概率。使用TTEST来确定两个样本是否可能来自均值相同的相同两个基础总体。语法TTEST(array1,array2,tails,type)争论Argument描述Required/OptionalArray1Thefirstdataset.Required......
  • 无涯教程-JavaScript - TINV函数
    TINV函数取代了Excel2010中的T.INV.2T函数。描述该函数返回学生t分布的两尾逆。语法TINV(probability,deg_freedom)争论Argument描述Required/OptionalProbabilityTheprobabilityassociatedwiththetwo-tailedStudent'st-distribution.RequiredDeg_freedom......
  • vivado 教程笔记 -创建工程 - 编译 - 布局布线 - 生成bit - 下板验证
    1、创建工程工程就算创建完了。2、 创建源文件双击打开后,就可以敲入代码 3、语法编译、布局布线、IO配置约束输入完一个完整代码后,先对语法进行综合分析,可直接跳过RTLANALYSIS,直接点击SYNTHESIS(综合)进行布局布线布局布线完后,IO管脚配置约束有时......
  • Lnton羚通算法算力云平台【PyTorch】教程:torch.nn.Hardtanh
    torch.nn.Hardtanh原型CLASStorch.nn.Hardtanh(min_val=-1.0,max_val=1.0,inplace=False,min_value=None,max_value=None)参数min_val ([float])–线性区域的最小值,默认为-1max_val ([float])–线性区域的最大值,默认为1inplace ([bool])–默认为Falsetorch.nn.Ha......
  • 微信小程序开发部署发布流程
    微信小程序开发部署发布流程最近因为有比赛,所以在进行敏捷小程序开发,由于我比较菜,不会JS原生,所以选择了符合技术栈的技术路线。MPFlutter框架+dart语言的开发。这样就可以符合“同时产出小程序与APP”的需求。1.微信小程序申请微信公众平台(qq.com)进行申请,注册,认证。按照......