【爬虫入门知识讲解:正则表达式】

正则表达式

Regular Expression,译作正则表达式或正规表示法,表示有规则的表达式,意思是说,描述一段文本排列规则的表达式。

正则表达式并不是Python的一部分。而是一套独立于编程语言,用于处理复杂文本信息的强大的高级文本操作工具。正则表达式拥有自己独特的规则语法以及一个独立的正则处理引擎,我们根据正则语法编写好规则(模式)以后,引擎不仅能够根据规则进行模糊文本查找,还可以进行模糊分割,替换等复杂的文本操作,能让开发者随心所欲地处理文本信息。正则引擎一般由编程语言提供操作,像python就提供了re模块或regex模块来调用正则处理引擎。

正则表达式在处理文本的效率上不如系统自带的字符串操作,但功能却比系统自带的要强大许多。

最早的正则表达式来源于Perl语言,后面其他的编程语言在提供正则表达式操作时基本沿用了Perl语言的正则语法,所以我们学习python的正则以后,也可以在java,php,go,javascript,sql等编程语言中使用。

正则对字符串或文本的操作,无非是分割、匹配、查找和替换。

在线测试工具 http://tool.chinaz.com/regex/

【1】元字符(Metacharacters)

元字符是具有特殊含义的字符。

元字符 描述
[] 匹配一个中括号中出现的任意原子
[^原子] 匹配一个没有在中括号出现的任意原子
\ 转义字符,可以把原子转换特殊元字符,也可以把特殊元字符转成原子。
^ 叫开始边界符或开始锚点符,匹配一行的开头位置
$ 叫结束边界符或结束锚点符,匹配一行的结束位置
. 叫通配符、万能通配符或通配元字符,匹配1个除了换行符\n以外任何原子
***** 叫星号贪婪符,指定左边原子出现0次或多次
? 叫非贪婪符,指定左边原子出现0次或1次
+ 叫加号贪婪符,指定左边原子出现1次或多次
{n,m} 叫数量范围贪婪符,指定左边原子的数量范围,有{n},{n, }, {,m}, {n,m}四种写法,其中n与m必须是非负整数。
| 指定原子或正则模式进行二选一或多选一
() 对原子或正则模式进行捕获提取和分组划分整体操作,
import re
"""re.findall(正则模式, 文本)  基于正则模式查找所有匹配的文本内容"""
# part1: 通配符->.  字符集-> []
ret1 = re.findall("a", "a,b,c,d,e")
ret1 = re.findall(".", "a,b,c,d,e")
ret1 = re.findall("[ace]", "a,b,c,d,e")
ret1 = re.findall("[a-z]", "a,b,c,d,e")
ret1 = re.findall("[0-9]", "1,2,3,4,5")
ret1 = re.findall("\d", "1,2,3,4,5")
ret1 = re.findall("[0-9a-z]", "1,a,2,b,3")
ret1 = re.findall("[^a-z]", "1,a,2,b,3")
ret1 = re.findall("[^0-9,]", "1,a,2,b,3")
print(ret1)

# part2:重复元字符-> + * {} ?
ret2 = re.findall("[0-9a-zA-Z]", "apple,banana,orange,melon")
ret2 = re.findall("\w", "apple,banana,orange,melon")
ret2 = re.findall("\w+", "apple,banana,orange,melon")
ret2 = re.findall("\w+?", "apple,banana,orange,melon")  # 取消贪婪匹配
ret2 = re.findall("\w*", "apple,banana,orange,melon")
ret2 = re.findall("\w{6}", "apple,banana,orange,melon")

# part3: 位置元字符-> ^ $
ret3 = re.findall("^\w{5}", "apple,banana,peach,orange,melon")
ret3 = re.findall("\w{5}$", "apple,banana,peach,orange,melon")
ret3 = re.findall("^\w{5}$", "apple,banana,peach,orange,melon")
print(ret3)

# part4:
# | 指定原子或正则模式进行二选一或多选一
# () 具备模式捕获的能力,也就是优先提取数据的能力,通过(?:) 可以取消模式捕获
ret4 = re.findall(",\w{5},", ",apple,banana,peach,orange,melon,")  # 筛选出5个字符的单词
ret4 = re.findall(",(\w{5}),", ",apple,banana,peach,orange,melon,")  # 筛选出5个字符的单词
ret4 = re.findall("\w+@\w+\.com", "[email protected],[email protected],....")  # 筛选出5个字符的单词
ret4 = re.findall("(\w+)@qq\.com", "[email protected],[email protected],....")  # 筛选出5个字符的单词
ret4 = re.findall("(?:\w+)@(?:qq|163)\.com", "[email protected],[email protected],....")  # 筛选出5个字符的单词
print(ret4)

# part5:  转义符-> \d \D  \w \W      \n    \s \S  \b \B
""" \b 1个单词边界原子 """
txt = "my name is nana. nihao,nana"
ret = re.findall(r"na", txt)
ret = re.findall(r"\bna", txt)
ret = re.findall(r"\bna\w{2}", txt)
print(ret)  # ['na', 'na', 'na']


转义元字符是\开头的元字符,由于某些正则模式会在开发中反复被用到,所以正则语法预定义了一些特殊正则模式以方便我们简写。

元字符 描述 示例
\d 匹配一个数字原子,等价于[0-9] \d
\D 匹配一个非数字原子。等价于[^0-9][^\d] “\D”
\b 匹配一个单词边界原子,也就是指单词和空格间的位置。 er\b
\B 匹配一个非单词边界原子,等价于 [^\b] r"\Bain"r"ain\B"
\n 匹配一个换行符
\t 匹配一个制表符,tab键
\s 匹配一个任何空白字符原子,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v] “\s”
\S 匹配一个任何非空白字符原子。等价于[^ \f\n\r\t\v][^\s] “\S”
\w 匹配一个包括下划线的单词原子。等价于[A-Za-z0-9_] “\w”
\W 匹配任何非单词字符。等价于[^A-Za-z0-9_][^\w] “\W”

【2】常用正则表达式

工作中,正则一般用于验证数据、校验用户输入的信息、爬虫、运维日志分析等。其中如果是验证用户输入的数据:

场景 正则表达式
用户名 ^[a-z0-9_-]{3,16}$
密码 ^[a-z0-9_-]{6,18}$
手机号码 ^(?:\+86)?1[3-9]\d{9}$
颜色的十六进制值 `^#?([a-f0-9]{6}
电子邮箱 ^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+\.[a-z]+$
URL `^(?:https://
IP 地址 `((2[0-4]\d
HTML 标签 ^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>
utf-8编码下的汉字范围 ^[\u2E80-\u9FFF]+$

【3】re模块的方法

Python提供了处理正则表达式的模块有标准库的re模块和第三方模块regex。

导入re模块后,可以开始使用正则表达式了。

import re

查找一个手机号码

import re

txt = "13312345678"
ret = re.search("^1[3-9]\d{9}$", txt)
if ret:
    print(ret.group())

注意:python本身没有内置正则处理的,python中的正则就是一段字符串,我们需要使用python模块中提供的函数把字符串发送给正则引擎,正则引擎会把字符串转换成真正的正则表达式来处理文本内容。

re模块提供了一组正则处理函数,使我们可以在字符串中搜索匹配项:

函数 描述
findall 按指定的正则模式查找文本中所有符合正则模式的匹配项,以列表格式返回结果。
search 在字符串中任何位置查找首个符合正则模式的匹配项,存在则返回re.Match对象,不存在返回None
match 判定字符串开始位置是否匹配正则模式的规则,匹配则返回re.Match对象,不匹配返回None
split 按指定的正则模式来分割字符串,返回一个分割后的列表
sub 把字符串按指定的正则模式来查找符合正则模式的匹配项,并可以替换一个或多个匹配项成其他内容。
findall

findall()函数返回包含所有匹配项的列表,如果找不到匹配项,则返回一个空列表。

search

search()函数搜索匹配的字符串,如果匹配上则返回匹配对象re.Match。如果有多个匹配项,则仅返回匹配项的第一个匹配项,如果找不到匹配项,则返回值为None:

import re

ret = re.search("1[3-9]\d{9}", "我的手机号码是13928835900,我女朋友的手机号是15100363326")
print(ret)
print(ret.start(), ret.end(), ret.span())
print(ret.group())

ret = re.search("(?P1[3-9]\d{9}).*?(?P\[email protected])", "我的手机号码是13928835900,我的邮箱是[email protected]")
print(ret)
print(ret.group("tel"))
print(ret.group("email"))
match

match()函数搜索匹配的字符串开始位置,如果匹配上则返回匹配对象,如果找不到匹配项,则返回值为None

split

split()函数返回一个列表,对字符串进行正则分割。

import re

txt = "my name is moluo"
ret = re.split("\s", txt)
print(ret)  # ['my', 'name', 'is', 'moluo']

可以通过指定maxsplit参数来控制分割的次数,例如,仅在第1次出现时才拆分字符串:

import re

txt = "my  name        is    yuan"
ret = re.split("\s+", txt)
print(ret)
sub

sub()函数用选择的文本替换匹配:

import re

txt = "my  name        is    yuan"
# ret = re.sub("\s+"," " ,txt)
ret = re.sub("\s+", " ", txt, 2)
print(ret)

【4】正则进阶

.*?
import re

text = '<12>   <1a!#e2> <>'

ret = re.findall("<\d+>", text)
ret = re.findall("<\w+>", text)
ret = re.findall("<.+>", text)
ret = re.findall("<.+?>", text)
ret = re.findall("<.*?>", text)

print(ret)
模式修正符

模式修正符,也叫正则修饰符,模式修正符就是给正则模式增强或增加功能的。

修正符 re模块提供的变量 描述
i re.I 使模式对大小写不敏感,也就是不区分大小写
m re.M 使模式在多行文本中可以多个行头和行位,影响 ^ 和 $
s re.S 让通配符. 可以代码所有的任意原子(包括换行符\n在内)
import re

text = """
<12
>
 
  
 
  
 
 <1a!#
 e2> 
 
 <>
"""

ret = re.findall("<.*?>", text)
ret = re.findall("<.*?>", text, re.S)

print(ret)

【5】练习

1、编写正则,匹配整数或者小数(包括正数和负数)

2、编写正则,匹配年月日日期 格式2018-12-31

3、编写正则,匹配qq号 5-12

4、编写正则,11位的电话号码

5、编写正则,长度为8-10位的用户密码 : 包含数字字母下划线

6、编写正则,从18位省份证中提取用户生日日期

7、编写正则,从文本"a@com  [email protected] [email protected]  [email protected]   [email protected]"中匹配qq邮箱地址

8、从以下多行文本中提取href=""中的双引号的值,并提取标签内容 <a>内容<a>
"""
hahaha
tmall
tmall
"""

课堂代码

import re

"""
1、编写正则,匹配文本中的整数或者小数(包括正数和负数)
"""
# txt = "10.3 10 20 -20 +20 --20 ++20 -30.5444"
# # ret = re.findall(r"-?\+?\d+", txt)
# ret = re.findall(r"[\+\-]?(?:(?:\d+\.\d+)|(?:\d+))", txt)
# print(ret)
# # ['10', '3', '10', '20', '-20', '+20', '-20', '+20']



"""
2、编写正则,匹配年月日日期 格式: 2018-12-31
"""

# txt = "2018-12-31  2018-12-01 2018-12  2018-31 0000-12-31 2018-1-31 2018-01-31  20-01-31  20-01-1  2020-1-1  2020-01-01"
# ret = re.findall(r"[12]\d{3}-\d+-\d+", txt)
# print(ret)  # ['2018-12-31', '2018-12-01', '2018-1-31', '2018-01-31', '2020-1-1', '2020-01-01']
#

"""
3、编写正则,匹配qq号 5-12数字
"""
# txt = "20181231  40001 2202020133  13311233220222 20202012024222 222050sss2222  33020202222  2001  202011  2020.0101222"
# ret = re.findall(r"[1-9]\d{4,11}", txt)
# print(ret)  # ['20181231', '40001', '2202020133', '133112332202', '202020120242', '222050', '33020202222', '202011', '101222']



"""
4、编写正则,11位的手机号码
"""
# txt = "1331234546 1501233453 15812345678  158-1234-5678  158 1234 5678   20022221111  10012345678  19012345678"
# ret = re.findall(r"1[3-9]\d{9}", txt)
# print(ret)  #
#
# # 如果 158-1234-5678 和 158 1234 5678也算呢?
# txt = "1331234546 1501233453 15812345678  158-1234-5678  158 1234 5678   20022221111  10012345678  19012345678"
# ret = re.findall(r"1[3-9]\d[\- ]?\d{4}[\- ]?\d{4}", txt)
# print(ret)  #


"""
5、编写正则,长度为8-10位的用户密码 : 包含数字字母下划线
"""
# password = input("请输入长度为8-10位的用户密码(包含数字字母下划线):")
# ret = re.match(r"^\w{8,10}$", password)
# print(ret)

"""
6、编写正则,从18位省份证中提取用户生日日期
"""
# idCard = "51142119991021155x"
# ret = re.findall(r"^[1-6]\d{5}(\d{8})\d{3}[\dxX]$", idCard)
# # ret = re.findall(r"^(?:1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5])\d{4}(\d{8})\d{3}[\dxX]$", idCard)
# print(ret)

"""
7、编写正则,从文本"a@com  [email protected] [email protected]  [email protected]   [email protected]"中匹配qq邮箱地址
"""
# txt = "a@com  [email protected] [email protected]  [email protected]   [email protected]"
# ret = re.findall(r"\w+@\w+\.\w+(?:.cn)?", txt)
# print(ret)  # ['[email protected]', '[email protected]', '[email protected]', '[email protected]']

"""
8、从以下多行文本中提取href=""中的双引号的值,并提取标签内容 内容
"""

# txt = """
# hahaha
# tmall
# tmall
# """
# ret = re.findall(r'(?P.*?)', txt, re.M+re.S)
# print(ret)

你可能感兴趣的:(爬虫,正则表达式)