【Python实用教程】手机通讯录vcf转excel再转vcf-手机通讯录转换

本文内容为网友分享,存在未知的风险性,查阅使用过程请务必认真辨别!

图片[1]-【Python实用教程】手机通讯录vcf转excel再转vcf-手机通讯录转换-云村集邮社

前言

前几天突然要想批量导入一些电话到手机,发现大部分手机还需要下载相应的手机助手,麻烦好多,因此AI了下这个程序。思路:现把vcf转换成excel,编辑好excel后转成vcf再导入手机

vcf转excel表格(oppo为例)

import vobject
import pandas as pd
import quopri
from datetime import datetime


def decode_quoted_printable(s):
    """解码quoted-printable编码的字符串"""
    try:
        return quopri.decodestring(s).decode('utf-8')
    except:
        return s  # 解码失败时返回原始字符串


def parse_vcard(vcard_str):
    """解析单个vCard"""
    try:
        vcard = vobject.readOne(vcard_str)
        contact = {
            '姓名': '',
            '姓氏': '',
            '名字': '',
            '电话号码': [],
            '邮箱': '',
            '分组': '未分组',
            '国家代码': 'CN',
            '最后联系时间': '',
            '备注': ''
        }

        # 处理姓名
        if hasattr(vcard, 'fn'):
            contact['姓名'] = decode_quoted_printable(vcard.fn.value)

        # 处理N字段(结构化姓名)
        if hasattr(vcard, 'n'):
            name_parts = vcard.n.value
            contact['姓氏'] = decode_quoted_printable(name_parts.family or "")
            contact['名字'] = decode_quoted_printable(name_parts.given or "")

        # 处理电话号码(优先排序)
        pref_numbers = []
        other_numbers = []
        if hasattr(vcard, 'tel_list'):
            for tel in vcard.tel_list:
                num = tel.value.strip()
                if 'PREF' in tel.params.get('TYPE', []):
                    pref_numbers.append(num)
                else:
                    other_numbers.append(num)
        contact['电话号码'] = ';'.join(pref_numbers + other_numbers) or ''

        # 处理邮箱
        if hasattr(vcard, 'email'):
            contact['邮箱'] = vcard.email.value

        # 处理分组  X-OPPO-GROUP可修改或者直接不要分组也可以
        for x in vcard.getChildren():
            if x.name == 'X-OPPO-GROUP':
                contact['分组'] = decode_quoted_printable(x.value)

        # 处理时间戳
        if hasattr(vcard, 'latestdate'):
            dt_str = vcard.latestdate.value
            try:
                dt = datetime.strptime(dt_str, "%Y%m%d%H%M")
                contact['最后联系时间'] = dt.strftime("%Y-%m-%d %H:%M")
            except:
                contact['最后联系时间'] = dt_str

        return contact
    except Exception as e:
        print(f"解析vCard失败: {str(e)}")
        return None


def vcf_to_excel(input_path, output_path):
    """主转换函数"""
    contacts = []
    with open(input_path, 'r', encoding='utf-8') as f:
        vcard_str = ""
        for line in f:
            vcard_str += line
            if line.startswith("END:VCARD"):
                contact = parse_vcard(vcard_str)
                if contact: contacts.append(contact)
                vcard_str = ""

    # 生成DataFrame
    df = pd.DataFrame(contacts)

    # 列排序
    columns = ['姓名', '姓氏', '名字', '电话号码', '邮箱', '分组', '国家代码', '最后联系时间', '备注']
    df = df[columns]

    # 保存Excel
    df.to_excel(output_path, index=False)
    print(f"成功转换 {len(contacts)} 个联系人至 {output_path}")


if __name__ == "__main__":
    # 使用方法
    vcf_to_excel(
        input_path="Contacts-2025-04-03(1).vcf",  # 输入文件路径
        output_path="contacts1.xlsx"  # 输出文件路径
    )

excel转vcf

import pandas as pd
import quopri
from datetime import datetime
import re


def excel_to_vcard(input_excel, output_vcf):
    # 读取时强制所有列为字符串类型
    df = pd.read_excel(
        input_excel,
        dtype=str,
        keep_default_na=False  # 禁用自动NaN检测
    )

    with open(output_vcf, 'w', encoding='utf-8') as f:
        for _, row in df.iterrows():
            # ========== 电话号码处理 ==========
            tel_str = str(row.get('电话号码', '')).strip()

            # 阶段1:多号码分割(支持分号、逗号、斜线)
            tels = re.split(r'[;,/]', tel_str) if tel_str else []

            # 阶段2:清洗和格式化
            cleaned_tels = []
            for tel in tels:
                tel = tel.strip()
                # 处理科学计数法 (如 1.38e+10 → 13800000000)
                if 'e+' in tel.lower():
                    tel = f"{float(tel):.0f}"
                # 去除尾随的 .0
                tel = tel.rstrip('.0')
                if tel:
                    cleaned_tels.append(tel)

            # ========== 基础字段构建 ==========
            vcard = [
                "BEGIN:VCARD",
                "VERSION:2.1"
            ]

            # ========== 编码处理优化 ==========
            def qp_encode(s):
                """处理空值并添加折行符"""
                encoded = quopri.encodestring(s.encode('utf-8')).decode('utf-8')
                # 每72字符折行(vCard规范要求)
                return '\r\n '.join([encoded[i:i + 72] for i in range(0, len(encoded), 72)])

            # 姓名组件
            last_name = qp_encode(row.get('姓氏', ''))
            first_name = qp_encode(row.get('名字', ''))
            vcard.append(f"N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{last_name};{first_name};;;")

            # 显示名
            full_name = qp_encode(row.get('姓名', ''))
            vcard.append(f"FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{full_name}")

            # ========== 电话号码处理 ==========
            for i, tel in enumerate(cleaned_tels):
                # 标记第一个号码为PREF
                params = "CELL" + (",PREF" if i == 0 else "")
                vcard.append(f"TEL;{params}:{tel}")

            # ========== 邮箱处理 ==========
            if email := row.get('邮箱', ''):
                vcard.append(f"EMAIL;PREF;WORK:{email}")

            # ========== 时间处理优化 ==========
            if dt_str := row.get('最后联系时间', ''):
                try:
                    # 支持多种时间格式解析
                    for fmt in ('%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M', '%Y%m%d%H%M'):
                        try:
                            dt = datetime.strptime(dt_str, fmt)
                            break
                        except ValueError:
                            continue
                    vcard.append(f"LATESTDATE:{dt.strftime('%Y%m%d%H%M')}")
                except:
                    pass  # 忽略无效时间格式

            # ========== 其他字段处理 ==========
            if country := row.get('国家代码', ''):
                vcard.append(f"COUNTRYISO:{country}")

            if group := row.get('分组', ''):
                encoded_group = qp_encode(group)
                vcard.append(f"X-OPPO-GROUP;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{encoded_group}")

            # 备注处理
            remark = row.get('备注', 'NULL').replace('"', "'")  # 防止引号冲突
            vcard.append(f"OPPO_RECENT_CALL:{remark}")

            # ========== 结束标记 ==========
            vcard.append("END:VCARD")

            # 写入时使用CRLF换行符
            f.write('\r\n'.join(vcard) + '\r\n')


# 使用示例
excel_to_vcard('contacts1.xlsx', 'restored_contacts2.vcf')
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享