>>> from env_helper import info; info()
待更新

1.7. 项目:电话号码和 E-mail 地址提取程序

假设你有一个无聊的任务,要在一篇长的网页或文章中, 找出所有电话号码和邮件地址。如果手动翻页, 可能需要查找很长时间。如果有一个程序, 可以在剪贴板的文本中查找电话号码和E-mail地址, 那你就只要按一下Ctrl-A选择所有文本, 按下Ctrl-C 将它复制到剪贴板,然后运行你的程序。 它会用找到的电话号码和 E-mail地址, 替换掉剪贴板中的文本。

当你开始接手一个新项目时,很容易想要直接开始写代码。 但更多的时候,最好是后退一步,考虑更大的图景。 我建议先草拟高层次的计划,弄清楚程序需要做 什么。暂时不要思考真正的代码,稍后再来考虑。 现在,先关注大框架。

例如,你的电话号码和E-mail地址提取程序需要完成以下任务:

  • 从剪贴板取得文本。

  • 找出文本中所有的电话号码和E-mail地址。

  • 将它们粘贴到剪贴板。

现在你可以开始思考,如何用代码来完成工作。代码需要做下面的事情:

  • 使用 pyperclip 模块复制和粘贴字符串。

  • 创建两个正则表达式,一个匹配电话号码,另一个匹配E-mail地址。

  • 对两个正则表达式,找到所有的匹配,而不只是第一次匹配。

  • 将匹配的字符串整理好格式,放在一个字符串中,用于粘贴。

  • 如果文本中没有找到匹配,显示某种消息。

这个列表就像项目的路线图。在编写代码时, 可以独立地关注其中的每一步。每一步都很好管理。 它的表达方式让你知道在 Python 中如何去做。

1.7.1. 第1步:为电话号码创建一个正则表达式

首先,你需要创建一个正则表达式来查找电话号码。 创建一个新文件,输入以下代码, 保存为 phoneAndEmail.py :

>>> # phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard.
>>>
>>> import pyperclip,re
>>>
>>> phoneRegex = re.compile(r'''(
>>>     (\d{3}|\(\d{3}\))?
>>>
>>>     (\s|-|\.)?
>>>
>>>     (\d{3})
>>>
>>>     (\s|-|\.)
>>>
>>>     (\d{4})
>>>
>>>     (\s\*(ext|x\|ext.)\s*(\d{2,5}))? )''', re.VERBOSE)
>>>
>>> # TODO: Create email regex.
>>>
>>> # TODO: Find matches in clipboard text.
>>>
>>> # TODO: Copy results to the clipboard.

TODO 注释仅仅是程序的框架。当编写真正的代码时, 它们会被替换掉。

电话号码从一个“可选的”区号开始, 所以区号分组跟着一个问号。因为区号可能只是3个数字 (即 \d{3}),或括号中的3个数字(即\(\d{3}\)), 所以应该用管道符号连接这两部分。 可以对这部分多行字符串加上正则表达式注释# Area code, 帮助你记忆(\d{3}|\(\d{3}\))? 要匹配的是什么。

电话号码分割字符可以是空格(\s)、短横(|-)或句点(.), 所以这些部分也应该用管道连接。 这个正则表达式接下来的几部分很简单: 3个数字,接下来是另一个分割符,接下来是4个数字。 最后的部分是可选的分机号,包括任意数目的空格, 接着 extxext. ,再接着2到5位数字。

1.7.2. 第2步:为 E-mail 地址创建一个正则表达式

还需要一个正则表达式来匹配 E-mail 地址。 让你的程序看起来像这样:

>>> #! python3
>>> # phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard.
>>> import pyperclip, re
>>>
>>> phoneRegex = re.compile(r'''(
>>>     (\d{3}|\(\d{3}\))?
>>>
>>>     (\s|-|\.)?
>>>
>>>     (\d{3})
>>>
>>>     (\s|-|\.)
>>>
>>>     (\d{4})
>>>
>>>     (\s\*(ext|x\|ext.)\s*(\d{2,5}))? )''', re.VERBOSE)
>>> # Create email regex.
>>>
>>> emailRegex=re.compile(r'''(
>>>
>>>          [a-zA-Z0-9._%+-]+      #username
>>>          @                      #@ symbol
>>>          [s-zA-Z0-9.-]+         #domain name
>>>          (\.[a-zA-Z]{2,4})      #dot-something
>>>          )''',re.VERBOSE)
>>>
>>> # TODO: Find matches in clipboard text.
>>>
>>> # TODO: Copy results to the clipboard.

E-mail地址的用户名部分是一个或多个字符, 字符可以包括:小写和大写字母、数字、句点、 下划线、百分号、加号或短横。 可以将所有这些放入一个字符分类:[a-zA-ZO-9._%+-]

域名和用户名用@符号分割, 域名允许的字符分类要少一些,只允许字母、 数字、句点和短横:[a-zA-ZO-9.-] 。 最后是“dot-com”部分(技术上称为“顶级域名”), 它实际上可以是“dot-anything”。它有2到4个字符。

E-mail地址的格式有许多奇怪的规则。 这个正则表达式不会匹配所有可能的、 有效的E-mail地址,但它会匹配你遇到的大多数典型的电子邮件地址。

1.7.3. 第3步:在剪贴板文本中找到所有匹配

既然已经指定了电话号码和电子邮件地址的正则表达式, 就可以让 Python 的 re模块做辛苦的工作, 查找剪贴板文本中所有的匹配。 pyperdip.paste() 函数 将取得一个字符串,内容是剪贴板上的文本, findall() 正则表达式方法将返回一个元组的列表。

让你的程序看起来像这样:

#! python3
# phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard.

import pyperclip, re

phoneRegex = re.compile(r'''(
    (\d{3}|\(\d{3}\))?

    (\s|-|\.)?

    (\d{3})

    (\s|-|\.)

    (\d{4})

    (\s\*(ext|x\|ext.)\s*(\d{2,5}))? )''', re.VERBOSE)
# Find matches in clipboard text.
text = str(pyperclip.paste())

matches =[]
for groups in phoneRegex.findall(text):
    phoneNum = '-'.join([groups[1], groups[3], groups[5]])
    if groups[8] :
        phoneNum += ' x' + groups[8]
    matches.append(phoneNum)
for groups in emailRegex.findall(text):
    matches.append(groups[0])

# TODO: Copy results to the clipboard.

每个匹配对应一个元组, 每个元组包含正则表达式中每个分组的字符串。 回忆一下,分组0匹配整个正则表达式, 所以在元组下标0处的分组,就是你感兴趣的内容。

matches =[]处可以看到, 你将所有的匹配保存在名为 matches 的列表变量中。 它从一个空列表开始,经过几个 for 循环。 对于 E-mail 地址,你将每次匹配的分组0添加到列表中。 对于匹配的电话号码,你不想只是添加分组0。 虽然程序可以“检测”几种不同形式的电话号码, 你希望添加的电话号码是唯一的、标准的格式。 phoneNum 变量包含一个字符串, 它由匹配文本的分组1、3、5和8构成。 (这些分组是区号、前3个数字、后4个数字和分机号。)

1.7.4. 第4步:所有匹配连接成一个字符串,复制到剪贴板

现在, E-mail 地址和电话号码已经作为字符串 列表放在 matches 中,你希望将它们复制到剪贴板。 pyperclip.copy() 函数只接收一个字符串值, 而不是字符串的列表,所以你在 matches 上调用 join() 方法。

为了更容易看到程序在工作,让我们将所有找到的 匹配都输出在终端上。如果没有找到电话号码或 E-mail地址,程序应该告诉用户。

让你的程序看起来像这样:

#! python3
# phoneAndEmail.py - Finds phone numbers and email addresses on theclipboard.
import pyperclip, re

phoneRegex = re.compile(r'''(
    (\d{3}|\(\d{3}\))?

    (\s|-|\.)?

    (\d{3})

    (\s|-|\.)

    (\d{4})

    (\s\*(ext|x\|ext.)\s*(\d{2,5}))? )''', re.VERBOSE)
# Find matches in clipboard text.
text = str(pyperclip.paste())

matches =[]

for groups in emailRegex.findall(text):
    matches.append(groups[0])

# Copy results to the clipboard.
if len(matches) > 0:
    pyperclip.copy('\n'.join(matches))
    print('Copied to clipboard:')
    print('\n'.join(matches))
else:
    print('No phone numbers or email addresses found.')

1.7.5. 第5步:运行程序

作为一个例子,打开你的Web浏览器,访问No Starch Press的联系页面 http://www.nostarch.com/contactus.htm。 按下Ctrl-A 选择该页的所有文本, 按下 Ctrl-C将它复制到剪贴板。 运行这个程序,输出看起来像这样:

Copied to clipboard:
800-420-7240
415-863-9900
415-863-9950
info@nostarch.com
media@nostarch.com
academic@nostarch.com
help@nostarch.com