下面是一个完整的 Python 程序,可以从手机通讯录(假设有通讯录文件或通过手机备份获取)中提取联系人信息并导出到 Excel 文件中。
由于直接从手机获取通讯录通常需要特殊权限,我将提供两种方案:
从 VCF 文件导入(大多数手机可导出 VCF 格式通讯录) 从 CSV 文件导入(通用格式)import pandas as pd
import re
import os
from pathlib import Path
import sys
from datetime import datetime
class ContactsExporter:
def __init__(self):
self.contacts = []
def parse_vcf(self, vcf_path):
"""解析 VCF (vCard) 文件"""
print(f"正在解析 VCF 文件: {vcf_path}")
with open(vcf_path, 'r', encoding='utf-8') as file:
content = file.read()
# 分割每个 vCard
vcards = content.split('BEGIN:VCARD')
contacts = []
for vcard in vcards:
if not vcard.strip():
continue
contact = {
'姓名': '',
'手机': [],
'电话': [],
'邮箱': [],
'公司': '',
'职位': '',
'地址': '',
'备注': ''
}
lines = vcard.split('\n')
for line in lines:
line = line.strip()
# 解析姓名
if line.startswith('FN:'):
contact['姓名'] = line[3:].strip()
elif line.startswith('N:'):
# 格式: N:Last;First;Middle;Prefix;Suffix
name_parts = line[2:].split(';')
if len(name_parts) >= 2 and name_parts[1]:
contact['姓名'] = name_parts[1] + ' ' + name_parts[0]
elif name_parts[0]:
contact['姓名'] = name_parts[0]
# 解析电话
elif line.startswith('TEL;') or line.startswith('TEL:'):
tel_match = re.search(r'TEL[^:]*:([\d\+\-\s\(\)]+)', line)
if tel_match:
phone = tel_match.group(1).strip()
# 判断是手机还是电话
if re.match(r'^1[3-9]\d{9}$', re.sub(r'\D', '', phone)):
contact['手机'].append(phone)
else:
contact['电话'].append(phone)
# 解析邮箱
elif line.startswith('EMAIL;') or line.startswith('EMAIL:'):
email_match = re.search(r'EMAIL[^:]*:([^\s;]+)', line)
if email_match:
contact['邮箱'].append(email_match.group(1).strip())
# 解析公司
elif line.startswith('ORG:'):
contact['公司'] = line[4:].strip()
# 解析职位
elif line.startswith('TITLE:'):
contact['职位'] = line[6:].strip()
# 解析地址
elif line.startswith('ADR;') or line.startswith('ADR:'):
addr_match = re.search(r'ADR[^:]*:(.+)', line)
if addr_match:
contact['地址'] = addr_match.group(1).strip().replace(';', ' ')
# 解析备注
elif line.startswith('NOTE:'):
contact['备注'] = line[5:].strip()
# 将列表转换为字符串以便存储
if contact['手机']:
contact['手机'] = '; '.join(contact['手机'])
else:
contact['手机'] = ''
if contact['电话']:
contact['电话'] = '; '.join(contact['电话'])
else:
contact['电话'] = ''
if contact['邮箱']:
contact['邮箱'] = '; '.join(contact['邮箱'])
else:
contact['邮箱'] = ''
# 添加到联系人列表
contacts.append(contact)
print(f"成功解析 {len(contacts)} 个联系人")
return contacts
def parse_csv(self, csv_path):
"""解析 CSV 文件"""
print(f"正在解析 CSV 文件: {csv_path}")
try:
# 尝试不同的编码
try:
df = pd.read_csv(csv_path, encoding='utf-8')
except:
df = pd.read_csv(csv_path, encoding='gbk')
contacts = []
required_columns = []
# 尝试识别列名
column_mapping = {}
for col in df.columns:
col_lower = col.lower()
if 'name' in col_lower or '姓名' in col or '名称' in col:
column_mapping['姓名'] = col
elif 'mobile' in col_lower or '手机' in col or '电话' in col:
column_mapping['手机'] = col
elif 'phone' in col_lower or 'tel' in col_lower:
column_mapping['电话'] = col
elif 'email' in col_lower or '邮箱' in col or '邮件' in col:
column_mapping['邮箱'] = col
elif 'company' in col_lower or '公司' in col:
column_mapping['公司'] = col
elif 'title' in col_lower or '职位' in col or '职务' in col:
column_mapping['职位'] = col
elif 'address' in col_lower or '地址' in col:
column_mapping['地址'] = col
elif 'note' in col_lower or '备注' in col:
column_mapping['备注'] = col
# 如果找不到姓名列,使用第一列作为姓名
if '姓名' not in column_mapping and len(df.columns) > 0:
column_mapping['姓名'] = df.columns[0]
# 转换数据
for _, row in df.iterrows():
contact = {
'姓名': str(row[column_mapping.get('姓名', df.columns[0])]) if '姓名' in column_mapping else '',
'手机': str(row[column_mapping['手机']]) if '手机' in column_mapping else '',
'电话': str(row[column_mapping['电话']]) if '电话' in column_mapping else '',
'邮箱': str(row[column_mapping['邮箱']]) if '邮箱' in column_mapping else '',
'公司': str(row[column_mapping['公司']]) if '公司' in column_mapping else '',
'职位': str(row[column_mapping['职位']]) if '职位' in column_mapping else '',
'地址': str(row[column_mapping['地址']]) if '地址' in column_mapping else '',
'备注': str(row[column_mapping['备注']]) if '备注' in column_mapping else '',
}
# 清理 NaN 值
for key in contact:
if str(contact[key]) == 'nan':
contact[key] = ''
contacts.append(contact)
print(f"成功解析 {len(contacts)} 个联系人")
return contacts
except Exception as e:
print(f"解析 CSV 文件时出错: {e}")
return []
def export_to_excel(self, contacts, output_path):
"""导出联系人到 Excel 文件"""
if not contacts:
print("没有联系人数据可导出")
return False
try:
# 转换为 DataFrame
df = pd.DataFrame(contacts)
# 重新排列列顺序
column_order = ['姓名', '手机', '电话', '邮箱', '公司', '职位', '地址', '备注']
existing_columns = [col for col in column_order if col in df.columns]
df = df[existing_columns]
# 导出到 Excel
df.to_excel(output_path, index=False)
print(f"成功导出 {len(contacts)} 个联系人到: {output_path}")
return True
except Exception as e:
print(f"导出到 Excel 时出错: {e}")
return False
def run(self):
"""主运行函数"""
print("=" * 50)
print("手机通讯录导出工具")
print("=" * 50)
# 获取文件路径
while True:
file_path = input("请输入通讯录文件路径 (VCF 或 CSV 格式): ").strip('"').strip()
if not os.path.exists(file_path):
print("文件不存在,请重新输入")
continue
file_ext = os.path.splitext(file_path)[1].lower()
if file_ext == '.vcf':
contacts = self.parse_vcf(file_path)
break
elif file_ext == '.csv':
contacts = self.parse_csv(file_path)
break
else:
print("不支持的文件格式,请提供 .vcf 或 .csv 文件")
continue
if not contacts:
print("未找到联系人数据")
return
# 生成输出文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
default_output = f"contacts_export_{timestamp}.xlsx"
# 获取输出路径
output_path = input(f"请输入输出 Excel 文件路径 (默认: {default_output}): ").strip('"').strip()
if not output_path:
output_path = default_output
# 确保文件扩展名为 .xlsx
if not output_path.lower().endswith('.xlsx'):
output_path += '.xlsx'
# 导出到 Excel
self.export_to_excel(contacts, output_path)
# 显示前几个联系人预览
print("\n前5个联系人预览:")
print("-" * 80)
for i, contact in enumerate(contacts[:5]):
print(f"{i+1}. 姓名: {contact.get('姓名', '无')}, 手机: {contact.get('手机', '无')}, 邮箱: {contact.get('邮箱', '无')}")
print(f"\n导出完成!文件保存在: {os.path.abspath(output_path)}")
def create_sample_vcf():
"""创建示例 VCF 文件用于测试"""
sample_vcf = """BEGIN:VCARD
VERSION:2.1
N:张;三;;;
FN:张三
TEL;CELL:13800138000
TEL;WORK:010-12345678
EMAIL;WORK:zhangsan@example.com
ORG:ABC公司
TITLE:经理
ADR;WORK:;;北京市朝阳区;北京市;;100000;中国
NOTE:示例联系人1
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:李;四;;;
FN:李四
TEL;CELL:13900139000
TEL;HOME:021-87654321
EMAIL;PERSONAL:lisi@example.com
ORG:XYZ公司
TITLE:工程师
ADR;HOME:;;上海市浦东新区;上海市;;200000;中国
NOTE:示例联系人2
END:VCARD
BEGIN:VCARD
VERSION:2.1
N:王;五;;;
FN:王五
TEL;CELL:13700137000
EMAIL;WORK:wangwu@company.com
ORG:技术部
TITLE:总监
NOTE:重要客户
END:VCARD"""
with open("sample_contacts.vcf", "w", encoding="utf-8") as f:
f.write(sample_vcf)
print("已创建示例文件: sample_contacts.vcf")
return "sample_contacts.vcf"
def create_sample_csv():
"""创建示例 CSV 文件用于测试"""
import pandas as pd
sample_data = {
'姓名': ['张三', '李四', '王五', '赵六'],
'手机': ['13800138000', '13900139000', '13700137000', '13600136000'],
'电话': ['010-12345678', '021-87654321', '', '0755-66668888'],
'邮箱': ['zhangsan@example.com', 'lisi@example.com', 'wangwu@company.com', 'zhaoliu@test.com'],
'公司': ['ABC公司', 'XYZ公司', '技术部', '市场部'],
'职位': ['经理', '工程师', '总监', '主管'],
'地址': ['北京市朝阳区', '上海市浦东新区', '广州市天河区', '深圳市南山区'],
'备注': ['示例联系人1', '示例联系人2', '重要客户', '新同事']
}
df = pd.DataFrame(sample_data)
df.to_csv("sample_contacts.csv", index=False, encoding="utf-8-sig")
print("已创建示例文件: sample_contacts.csv")
return "sample_contacts.csv"
if __name__ == "__main__":
exporter = ContactsExporter()
# 检查命令行参数
if len(sys.argv) > 1:
file_path = sys.argv[1]
if file_path.lower() == '--sample-vcf':
file_path = create_sample_vcf()
exporter.parse_vcf(file_path)
elif file_path.lower() == '--sample-csv':
file_path = create_sample_csv()
exporter.parse_csv(file_path)
elif os.path.exists(file_path):
file_ext = os.path.splitext(file_path)[1].lower()
if file_ext == '.vcf':
contacts = exporter.parse_vcf(file_path)
if contacts:
output_path = f"contacts_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
exporter.export_to_excel(contacts, output_path)
elif file_ext == '.csv':
contacts = exporter.parse_csv(file_path)
if contacts:
output_path = f"contacts_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
exporter.export_to_excel(contacts, output_path)
else:
print("不支持的文件格式,请使用 .vcf 或 .csv 文件")
else:
# 交互式运行
print("提示: 您可以使用以下命令快速测试:")
print(" python contacts_exporter.py --sample-vcf (创建并处理示例 VCF 文件)")
print(" python contacts_exporter.py --sample-csv (创建并处理示例 CSV 文件)")
print()
exporter.run()
首先,需要安装必要的依赖库:
pip install pandas openpyxl
python contacts_exporter.py
方式2:通过命令行参数指定文件
# 处理 VCF 文件
python contacts_exporter.py contacts.vcf
# 处理 CSV 文件
python contacts_exporter.py contacts.csv
# 生成示例文件并测试
python contacts_exporter.py --sample-vcf
python contacts_exporter.py --sample-csv
如果需要直接从手机获取通讯录,可以考虑以下方案:
Android:使用 ADB 命令获取通讯录数据库 iPhone:使用 iTunes 备份后解析备份文件 云服务:如果使用 Google 通讯录或 iCloud,可通过 API 获取这个工具提供了一个基础框架,您可以根据实际需求进行调整和扩展。