因为工作原因,这几年短信量激增,短信量已经增加到14万多条。手机是华为的mate 40 pro,没有升级鸿蒙,仍然是EMUI系统,最后一个版本。
我的需求很简单,短信太多了,系统自带的短信应用,已经不能搜索了,输入后就搜索不出来了。在网上找了一些方案,使用华为套件来看看。
使用华为套件,点击到通讯录是可以正常工作的,也能导出内容,但是当点击到短信的时候,就卡死了,不知道是程序哪里的bug,打华为客服,前后沟通了一个多星期,也是无果,后来就没管了。
使用华为备份产生的文件,需要解密,然后再查找短信,但网上找到的解密渠道也失效了,无法解密。
简单的需求
手机短信太多了,无法搜索,想将手机短信存档,对于历史短信,有需要时能快速搜索即可。
近期的方案
- SMS Backup & Restore 备份短信,备份出来的xml文件。去google play商店或者apkure商店下载即可。
- 文件解析并导入到mongodb,使用compass进行查询。
短信导出
推荐方法
- 打开,sms backup & Restore,以英文界面为例, settings – Backup location ->Local backup folder 选择一个你熟悉的目录,最好是短目录,不然后面使用adb 的时候,不好拉出文件,当然你也可以通过其他方法把导出的文件备份出来。
- 这里我是将文件存储到Downloads目录中,后面的文件名,你看一下填写对应备份出来的文件即可。adb是连接了电脑,你可以把这个文件复制到指定的目录。
adb pull /storage/emulated/0/Download/sms-20241204152305.xml ./sms-backup-20241204.xml
注意点:注意导出设置的时候,允许读取通讯录,这样你可以看到有一个字段,contact_name,短信的易读性会好很多。
另一种导出的办法
使用 Content Provider 导出短信 Android 提供了一种标准方法,通过 content://
URI 来访问短信数据:
adb shell content query --uri content://sms > sms_data.txt
这个会在当前目录下生成一个sms_data.txt的目录。
两种方法比较
Content Provider导出:导出的是广本行,没有解析,以=分隔,如果数据中有=号会造成后续分隔困难。
Sms Backup & Restore: 导出的xml格式,可以直接解析,方便导入到json中。
解析xml数据并导入到mongodb中
解析思路
- 有唯一主键,这样后续再导入的时候,因为备份都是全量备份,后续随便导入,但有主键后,重复的将数据将不会被再插入到数据库中。使用了address,date,type,body,sub_id进行组合生成md5的hash值作为数据库的_id。
- 解析字段完成后,利用python的list完成插入。
相关代码
import xml.etree.ElementTree as ET
import json
from pymongo import MongoClient
import hashlib
def generate_unique_id(sms):
unique_string = f"{sms['address']}_{sms['date']}_{sms['type']}_{sms.get('body', '')}_{sms.get('sub_id', '')}"
return hashlib.md5(unique_string.encode()).hexdigest()
# 解析 XML
tree = ET.parse('data/sms-backup-20241204.xml')
root = tree.getroot()
sms_list = []
for sms in root.findall('sms'):
sms_list.append({
'address': sms.get('address'),
'date': sms.get('date'),
'type': sms.get('type'),
'contact_name' : sms.get('contact_name'),
'body': sms.get('body'),
'sub_id': sms.get('sub_id'),
'read': sms.get('read'),
'status':sms.get('status'),
'date_sent': sms.get('date_sent')
})
# 保存为 JSON 文件
with open('data/sms_data.json', 'w', encoding='utf-8') as f:
json.dump(sms_list, f, ensure_ascii=False, indent=4)
## add primary key
for sms in sms_list:
sms["_id"] = generate_unique_id(sms)
# 导入到 MongoDB
USERNAME = "admin"
PASSWORD = "test1234"
HOST = "10.0.1.21" # 或远程服务器地址
PORT = 27017 # MongoDB 默认端口
DATABASE = "sms"
COLLECTION = "sms_collection"
# 连接 MongoDB
client = MongoClient(f"mongodb://{USERNAME}:{PASSWORD}@{HOST}:{PORT}/")
db = client[DATABASE]
collection = db[COLLECTION]
try:
collection.insert_many(sms_list, ordered=False) # 跳过重复记录
print("短信数据已成功导入 MongoDB!")
except Exception as e:
print(f"导入时出现错误:{e}")
xml导出的字段说明
- protocol 协议类型,通常为 0,表示标准 SMS 协议
- address 短信的发送方或接收方号码
- date 短信发送或接收的时间戳(1970 年 1 月 1 日以来的毫秒数)
- type 短信类型:1 表示接收,2 表示发送
- subject 短信主题,通常为 null,适用于 MMS 或其他多媒体消息
- body 短信内容
- toa 接收方号码地址,通常为 null
- sc_toa 短信服务中心地址,通常为 null
- service_center 短信的服务中心信息,通常为 null
- read 短信是否已读:1 表示已读,0 表示未读
- status 短信状态:-1 表示无特殊状态;其他值可能表示发送失败等状态
- locked 短信是否被锁定:1 表示锁定,0 表示未锁定
- date_sent 短信的实际发送时间(1970 年 1 月 1 日以来的毫秒数)
- sub_id 订阅卡 ID(双卡手机时标记 SIM 卡)
- readable_date 人类可读的时间格式(如 Oct 28, 2015 19:57:25)
- contact_name 联系人名称,若未知则为 (Unknown)
查询示例
mongodb操作
创建索引
db.sms_collection.createIndex({body: "text"});
查找特定文字
db.sms_collection.findOne({body: /游多多/i});
查询截图
这还能查到好多年前的短信,15年的。