0%

Python 写入vcf文件

好久没有写 Python 脚本了。最近遇到的一个实际33需求就是:如何将 Excel 表格中的单元
格数据提取出成一个 vCard 文件?表格内容看起来是这样的:

ID Address Name Phone Number
在 Python 数目众多的第三方库中搜索,我找到了
vobject.

安装 openpyxl 和 vobject 库

直接 pip 安装,对于 Linux 用户可以在 pip 后加上 --user 参数安装到用户目录下。

1
pip install --user openpyxl vobject

使用openpyxl 提取数据

openpyxl 是一个读取/写入 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库,很容易上手,官方的文档写得很详细,项目也在活跃开发中。不过我碰到的最大的缺点是在一次性写入大量数据的时候比较耗 CPU. 这里简要介绍一下如何读取 Excel 单元格数据。

打开并读取 excel 文件, 获取所需 sheet:

1
2
wb = load_workbook('mydata.xlsx')
ws = wb['Sheet1']

读取制定列元素. openpyxl 的语法和 Excel 函数很像:

  • 获取一整列或者一整行单元格
    1
    2
    colA = ws['A']
    row10 = ws['10']
  • 获取制定范围内(类似于矩形选择)的单元格
    1
    2
    3
    DataRange = ws['A3':'G5']
    # 获取制定的一个单元格
    Cell = ws['G6']

    调用 cell.value 获取单元格值

    1
    2
    c.value = 'hello, world'
    print(c.value)

    .vcf 文件格式介绍

    FileInfo 网站上面的介绍是这样的:

    VCF 文件是一种存储联系人信息的标准文件格式,文件内通常包括姓名、地址、电话、电子邮箱等联系人信息。VCF 文件同样也可以存储制定域、图像和其它媒体信息。手机用户可以直接由联系人应用导入VCF文件。

.vcf 文件是纯文本文件,这也意味着你可以直接使用文本编辑器查看和编辑它,这也为我们使用 python 的字符串拼接写入文本信息提供了可能。用文本编辑器打开它会显示成这个样子:

如图片中显示的那样,vCard 文件首先以 BEGIN VCARD 开始,以 END:VCARD 结束。 VERSION:3.0 表示采用 3.0 版本的标准。详细的标准可以参看 vCard Format Specification 手册。这里我们要用到的就是以下几个:

  1. FN: Full Name 注意一个vCard 文件必须包含 FN 类型

    Purpose: To specify the formatted text corresponding to the name of the object the vCard represents.
    Value type: A single text value.

  2. TEL: Telephone Number

    Purpose: To specify the telephone number for telephony communication with the object the vCard represents.

    Value type: By default, it is a single free-form text value

利用vobject 写入 .vcf 文件

  1. 创建 vCard 对象:
    1
    2
    import vobject
    j = vobject.vCard()
  2. 添加MIME 信息
    1
    2
    j.add('fn').value = yourname
    j.add('tel').value = yourtel
  3. Serializing, 自动补全所有其它需要的属性。
    1
    2
    3
    j.serialize()
    # 打印输出测试
    j.prettyPrint()

    遇到的问题

    在写这个很简易的脚本中,最困扰我的问题就是添加联系人地址。如果你简单地写
    1
    j.add('add').value = some_string
    那么当运行 j.serialize() 的时候,会出现报错:
    1
    vobject.base.NativeError: "In transformFromNative, unhandled exception on line None <class 'AttributeError'>: 'str' object has no attribute 'box'"
    为了解决这个问题,我查了一下源码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Address(object):
    def __init__(self, street='', city='', region='', code='',
    country='', box='', extended=''):
    """
    Each name attribute can be a string or a list of strings.
    """
    self.box = box
    self.extended = extended
    self.street = street
    self.city = city
    self.region = region
    self.code = code
    self.country = country
    出现这个问题的的原因是 vobject 默认地址包含 box、code、country 等附加完整信息,解决的方法也很简单,直接制定 adrvobject.vcard.Address('some)_place') 即可避免报错。正确的方式参看官方测试实例:
    1
    2
    3
    4
    # 传递多个值
    card.add('adr').value = vobject.vcard.Address(u'5\u1234 Nowhere, Apt 1', 'Berkeley', 'CA', '94704', 'USA')
    # 传递单个值也是可以的
    j.add('adr').value = vobject.vcard.Address(‘A Simple Address‘)