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

假设你有一个无聊的任务,要在一篇长的网页或文章中,找出所有电话号码和
邮件地址。如果手动翻页,可能需要查找很长时间。如果有一个程序,可以在剪贴板的文本中查找电话号码和 E-mail 地址,那你就只要按一下Ctrl-A 选择所有文本,按下Ctrl-C 
将它复制到剪贴板,然后运行你的程序。它会用找到的电话号码和 E-mail地址,替换掉剪贴板中的文本。
当你开始接手一个新项目时,很容易想要直接开始写代码。但更多的时候,最好是后退一步,考虑更大的图景。我建议先草拟高层次的计划,弄清楚程序需要做什么。暂时不要思考真正的代码,稍后再来考虑。现在,先关注大框架。
例如,你的电话号码和E-mail 地址提取程序需要完成以下任务:

•    从剪贴板取得文本。
•    找出文本中所有的电话号码和 E-mail 地址。
•    将它们粘贴到剪贴板。
现在你可以开始思考,如何用代码来完成工作。代码需要做下面的事情:
•    使用pyperclip 模块复制和粘贴字符串。
•    创建两个正则表达式,一个匹配电话号码,另一个匹配 E-mail 地址。
•    对两个正则表达式,找到所有的匹配,而不只是第一次匹配。
•    将匹配的字符串整理好格式,放在一个字符串中,用于粘贴。
•    如果文本中没有找到匹配,显示某种消息。
这个列表就像项目的路线图。在编写代码时,可以独立地关注其中的每一步每一步都很好管理。它的表达方式让你知道在Python 中如何去做。

第 1 步:为电话号码创建一个正则表达式
首先,你需要创建一个正则表达式来查找电话号码。创建一个新文件,输入以下代码,保存为 phoneAndEmail.py:
#!  python3
#  phoneAndEmail.py  -  Finds  phone  numbers  and  email  addresses  on  the  clipboard. import  
pyperclip,  re
phoneRegex  =  re.compile(r'''( (\d{3}|\(\d{3}\))?                        #  area  code
(\s|-|\.)?                                     #  separator
(\d{3})                                          #  first  3  digits
(\s|-|\.)                                       #  separator
(\d{4})                                          #  last  4  digits
(\s*(ext|x|ext.)\s*(\d{2,5}))?    #  extension
)''',  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 位数字。


第 2 步:为 E-mail 地址创建一个正则表达式
还需要一个正则表达式来匹配E-mail 地址。让你的程序看起来像这样:
#!  python3
#  phoneAndEmail.py  -  Finds  phone  numbers  and  email  addresses  on  the  clipboard. import  
pyperclip,  re

phoneRegex  =  re.compile(r'''(
--snip--

#  Create  email  regex. emailRegex  =  re.compile(r'''(
➊            [a-zA-Z0-9._%+-]+           #  username
➋            @                                         #  @  symbol
➌            [a-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-Z0-9._%+-]。
域名和用户名用@符号分割➋,域名➌允许的字符分类要少一些,只允许字母、数字、句点和短横:[a-zA-Z0-9.-]。最后是“dot-com”部分(技术上
称为“顶级域名”),它实际上可以是“dot-anything”。它有 2 到 4 个字符。
E-mail 地址的格式有许多奇怪的规则。这个正则表达式不会匹配所有可能的、有效的 E-mail 地址,但它会匹配你遇到的大多数典型的电子邮件地址。

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

#!  python3
#  phoneAndEmail.py  -  Finds  phone  numbers  and  email  addresses  on  the  clipboard. import  
pyperclip,  re
phoneRegex  =  re.compile(r'''(
--snip--

#  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 的列表变量中。它从一个空列表开始,经过几个 for 循环。对于E-mail 地址,你将每次匹配的分组 0 
添加到列表中➌。对于匹配的电话号码,你不想只是添加分组 0。虽然程序可以“检测”几种不同形式的电话号码,你希望添加的电话号码是唯一的、标准的格式。 phoneNum 
变量包含一个字符串,它由匹配文本的分组 1、3、5 和 8 构成➋。(这些分组是区号、前 3 个数字、后 4 个数字和分机号。)
第 4 步:所有匹配连接成一个字符串,复制到剪贴板
现在,E-mail 地址和电话号码已经作为字符串列表放在 matches 中,你希望将它们复制到剪贴板。pyperclip.copy()函数只接收一个字符串值,而不是字符串的列表,所以你在 
matches 上调用 join()方法。
为了更容易看到程序在工作,让我们将所有找到的匹配都输出在终端上。如果没有找到电话号码或 E-mail 地址,程序应该告诉用户。
让你的程序看起来像这样:
#!  python3
#  phoneAndEmail.py  -  Finds  phone  numbers  and  email  addresses  on  the  clipboard.

--snip--
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.')

第 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


第 6 步:类似程序的构想
识别文本的模式(并且可能用 sub()方法替换它们)有许多不同潜在的应用。
•    寻找网站的URL,它们以 http://或 https://开始。
•    整理不同日期格式的日期(诸如 3/14/2015、03-14-2015 和 2015/3/14),用唯一的标准格式替代。
•    删除敏感的信息,诸如社会保险号或信用卡号。
•    寻找常见打字错误,诸如单词间的多个空格、不小心重复的单词,或者句子末尾处多个感叹号。它们很烦人!!

7.16    小结

虽然计算机可以很快地查找文本,但你必须精确地告诉它要找什么。正则表达


式让你精确地指明要找的文本模式。实际上,某些文字处理和电子表格应用提供了查找替换功能,让你使用正则表达式进行查找。
Python 自带的 re 模块让你编译 Regex 对象。该对象有几种方法:search()查找单词匹配,findall()查找所有匹配实例,sub()对文本进行查找和替换。
除本章介绍的语法以外,还有一些正则表达式语法。你可以在官方 Python 文档中找到更多内容:http://docs.python.org/3/library/re.html。指南网站 
http://www.regular- expressions.info/也是很有用的资源。
既然已经掌握了如何操纵和匹配字符串,接下来就该学习如何在计算机硬盘上读写文件了。


7.17    习题


1.创建 Regex 对象的函数是什么?
2.在创建Regex 对象时,为什么常用原始字符串?
3.search()方法返回什么?
4.通过 Match 对象,如何得到匹配该模式的实际字符串?
5.用 r'(\d\d\d)-(\d\d\d-\d\d\d\d)'创建的正则表达式中,分组 0 表示什么?分组 1

呢?分组 2 呢?
6.括号和句点在正则表达式语法中有特殊的含义。如何指定正则表达式匹配

真正的括号和句点字符?
7.findall()方法返回一个字符串的列表,或字符串元组的列表。是什么决定它提供哪种返回?
8.在正则表达式中,|字符表示什么意思?
9.在正则表达式中,?字符有哪两种含义?
10.在正则表达式中,+和*字符之间的区别是什么?
11.在正则表达式中,{3}和{3,5}之间的区别是什么?
12.在正则表达式中,\d、\w 和\s 缩写字符类是什么意思?
13.在正则表达式中,\D、\W 和\S 缩写字符类是什么意思?
14.如何让正则表达式不区分大小写?
15.字符.通常匹配什么?如果 re.DOTALL 作为第二个参数传递给 re.compile(),它会匹配什么?
16..*和*?之间的区别是什么?
17.匹配所有数字和小写字母的字符分类语法是什么?
18.如果 numRegex = re.compile(r'\d+'),那么 numRegex.sub('X', '12 drummers, 11 pipers, five rings, 3 
hens')返回什么?
19.将 re.VERBOSE 作为第二个参数传递给 re.compile(),让你能做什么?
20.如何写一个正则表达式,匹配每 3 位就有一个逗号的数字?它必须匹配以下数字:
•    '42'
•    '1,234'
•    '6,368,745'
但不会匹配:
•    '12,34,567'  (逗号之间只有两位数字)
•    '1234'  (缺少逗号)
21.如何写一个正则表达式,匹配姓 Nakamoto 的完整姓名?你可以假定名字总是出现在姓前面,是一个大写字母开头的单词。该正则表达式必须匹配:
•    'Satoshi Nakamoto'
•    'Alice Nakamoto'
•    'RoboCop Nakamoto'
但不匹配:
•    'satoshi Nakamoto'(名字没有大写首字母)
•    'Mr. Nakamoto'(前面的单词包含非字母字符)
•    'Nakamoto'  (没有名字)
•    'Satoshi nakamoto'(姓没有首字母大写)
22.如何编写一个正则表达式匹配一个句子,它的第一个词是 Alice、Bob  或

Carol,第二个词是 eats、pets 或 throws,第三个词是 apples、cats 或 baseballs。该句子以句点结束。这个正则表达式应该不区分大小写。它必须匹配:
•    'Alice eats apples.'
•    'Bob pets cats.'
•    'Carol throws baseballs.'
•    'Alice throws Apples.'
•    'BOB EATS CATS.'
但不匹配:
•    'RoboCop eats apples.'
•    'ALICE THROWS FOOTBALLS.'
•    'Carol eats 7 cats.'

.18    实践项目
作为实践,编程完成下列任务。


7.18.1   强口令检测
写一个函数,它使用正则表达式,确保传入的口令字符串是强口令。强口令的定义是:长度不少于 8 个字符,同时包含大写和小写字符,至少有一位数字。你可能需要用多个正则表达式来测试该字符串,以保证它的强度。

7.18.2   strip()的正则表达式版本
写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样。如果只传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符。否则,函数第二个参数指定的字符将从该字符串中去除。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大飞哥软件自习室

希望支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值