>>> 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个数字。
最后的部分是可选的分机号,包括任意数目的空格, 接着 ext
、 x
或
ext.
,再接着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