以下两条链接是使用正则表达式的爬虫,知识点与实例结合起来有助于理解哦!
Python正则表达式
字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在。那么有木有一种方法是专门用来匹配字符串的呢?正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
re 模块使 Python 语言拥有全部的正则表达式功能。所以在使用正则表达式时,我们需要先导入re模块—import re。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
一、基础知识
- 在正则表达式中,如果直接给出字符,就是精确匹配。用
\d
可以匹配一个数字,\w
可以匹配一个字母或数字; .
可以匹配任意字符;- 要匹配变长的字符,在正则表达式中,用
+
表示至少一个字符,用*
表示任意个字符(包括0个),用?
表示0个或1个字符,用{n}
表示n个字符,用{n,m}
表示n-m个字符。
import re #导库
astr="s34份额是否as586c河1源..is市9.1d3防4H不0防h7b不仅4.r5cd"
print(re.findall("\d",astr)) #findall()在字符串中找到所有的数字,并以列表的形式返回
print(re.search("\w",astr)) #匹配一个字母或数字,并将第一个匹配到的结果返回
print(re.findall("\w\d",astr)) #匹配连续的两个数字,一个字母一个数字,一个汉字一个数字,并将所有匹配到的结果以列表的形式返回
print(re.findall(r"\d.\d",astr)) #匹配连续的三位——第一位和第三位都是数字,中间为任意字符
print(re.findall("\d+",astr)) #匹配1位到无穷多位的数字
print(re.findall("\d\d+",astr)) #匹配2位到无穷多位的数字
print(re.findall("\d\d*",astr)) #匹配1位到无穷多位的数字
print(re.findall("\d\d?",astr)) # ? 表示0或1位字符,故表示一位或两位连续数字
print(re.findall("\d{3}",astr)) #返回三位连续数字
print(re.findall("\d{1,3}",astr)) #贪婪匹配,优先三位查找,接着两位,最后一位
print(re.findall("\d{2,3}?",astr)) #?可将贪婪匹配变成非贪婪;先匹配两位数字,然后再匹配三位数字
运行结果:
['3', '4', '5', '8', '6', '1', '9', '1', '3', '4', '0', '7', '4', '5']
<_sre.SRE_Match object; span=(0, 1), match='s'>
['s3', 's5', '86', '河1', '市9', 'd3', '防4', '不0', 'h7', '仅4', 'r5']
['586', '9.1', '3防4']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '586']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '58', '6', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['586']
['34', '586', '1', '9', '1', '3', '4', '0', '7', '4', '5']
['34', '58']
二、强化阶段
要做更精确地匹配,可以用[]
表示范围,比如:
-
[0-9a-zA-Z\_]
可以匹配一个数字、字母或者下划线; -
[0-9a-zA-Z\_]+
可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100'
,'0_Z'
,'Py3000'
等等; -
[a-zA-Z\_][0-9a-zA-Z\_]*
可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量; -
[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}
精确地限制了变量的长度是1-20个字符(前面1个字符+后面(0-19)个字符)。
import re
astr="A_c8fd33jd9_k0ja3"
print(re.findall("[0-9a-zA-Z\_]",astr))
print(re.findall("[0-9a-zA-Z\_]+",astr))
print(re.findall("[a-zA-Z\_][0-9a-zA-Z\_]*",astr))
print(re.findall("[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}",astr))
运行结果:
['A', '_', 'c', '8', 'f', 'd', '3', '3', 'j', 'd', '9', '_', 'k', '0', 'j', 'a', '3']
['A_c8fd33jd9_k0ja3'] #贪婪匹配
['A_c8fd33jd9_k0ja3']
[]
A|B
可以匹配A或B,所以(P|p)ython
可以匹配'Python'
或者'python'
。^
表示行的开头,^\d
表示必须以数字开头。$
表示行的结束,\d$
表示必须以数字结束。- 你可能注意到了,
py
也可以匹配'python'
,但是加上^py$
就变成了整行匹配,就只能匹配'py'
了。
import re
astr="""csd3份额是否a9
sjhbh353758cdbsv河
1源..is市....cd3防4H不胜
防hh787bb不仅4.r5cd是is范德萨;‘’a8"""
print(re.findall(r"^\d",astr)) #换行符号不可见,将整段字符串按一行处理;行的开头为数字,则返回该数字,如不是,返回一个空列表
print(re.findall(r"^\d",astr,re.M)) #re.M 是换行符号可见,将astr字符串转换为4行;若每行的开头为数字,则返回该数字
print(re.findall(r"\d$",astr)) #行的结尾为数字,则返回该数字
print(re.findall(r"\d$",astr,re.M)) #re.M 是换行符号可见,将astr字符串转换为4行;行的结尾为数字,则以列表的形式返回该数字
运行结果:
[]
['1']
['8']
['9', '8']
三、正则表达式的具体用法
3.1、查询
3.1.1、re.match()
re.match 从字符串的起始位置匹配一个模式,如果起始位置匹配成功 re.match() 方法返回一个匹配的对象;起始位置没有匹配成功的话,match() 就返回 None。
函数语法:
re.match(pattern, string, flags=0)
3.1.2、re.search()
re.search() 扫描整个字符串并返回第一个成功的匹配。
函数语法:
re.search(pattern, string, flags=0)
3.1.3、re.findall()
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
函数语法:
re.findall (pattern, string, flags=0)
函数参数说明:
- pattern:匹配的正则表达式
- string:要匹配的字符串
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
补充:标志位
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
3.1.4、re.match()、re.search() 和 re.findall() 的区别
re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None;而 re.search 匹配整个字符串,直到找到一个匹配。注意: match 和 search 是匹配一次 ,而 findall 匹配所有。
实例:
import re
astr="""sd3份额是否a
sjhb353cdbsv河
1源..is市....cd3防4H不胜0
6防8h787bb不仅4.r5cd是is范德萨;‘’a"""
#检索
print(re.match("is",astr))
print(re.match("sd",astr))
print(re.match("sd",astr).span())
print(re.search("is",astr))
print(re.findall("is",astr))
print(re.findall("h",astr))
print(re.findall("h",astr,re.I))
print(re.findall(r"\d.\d",astr))
print(re.findall(r"\d.\d",astr,re.S))
print(re.findall(r"^\d",astr))
print(re.findall(r"^\d",astr,re.M))
运行结果:
None # is 不在字符串的开始,匹配失败,函数返回None
<_sre.SRE_Match object; span=(0, 2), match='sd'> #匹配成功
(0, 2) #匹配成功,返回字符串的范围
<_sre.SRE_Match object; span=(27,29), match='is'> #匹配成功,返回第一个 is
['is', 'is'] #在字符串中找到所有 is ,并返回一个列表。
['h', 'h'] #只找小写h
['h', 'H', 'h'] #re.I 忽略大小写
['353', '3防4', '6防8', '787'] # . 任意字符;默认不包含换行
['353', '3防4', '0\n6', '8h7'] #re.S 使 . 匹配包括换行在内的所有字符
[] # ^ 代表开头;按整篇算,返回第一个数字
['1', '6'] #re.M 多行匹配,影响 ^ 和 $
3.1.5、分组 — group()
如果正则表达式中定义了组,就可以在 Match
对象上用 group()
方法提取出子串来。
注意:group(0)
永远是原始字符串,group(1)
、group(2)
……表示第1、2、……个子串。
函数说明:
- group(num):匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
- groups():返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
实例:
import re
#分别定义了两个组,直接从匹配的字符串中提取出区号和本地号码
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') #以三个数字为行开头,中间是 - ,以三到八个数字为行结尾的字符串
print(m)
print(m.group(0))
print(m.group(1))
print(m.group(2))
print(m.groups())
运行结果:
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
010-12345 #原生字符串
010 #返回第一组所对应值的元组
12345 #返回第二组所对应值的元组
('010', '12345') #包含所有小组字符串的元组
补充:特征匹配
- 大多数字母和数字前加一个反斜杠( \ )时会拥有不同的含义。
- 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
- 反斜杠( \ )本身需要使用反斜杠转义。
- 由于正则表达式通常都包含反斜杠,所以使用时最好用原始字符串来表示它们。模式元素(如 r'\t',等价于 '\\t')匹配相应的特殊字符。这也就是上面的代码部分匹配时加 ' r ' 的原因。
下表列出了正则表达式模式语法中的特殊元素。
3.2、编译 — re.compile()
当我们在Python中使用正则表达式时,re模块内部会干两件事情:
- 编译正则表达式,如果正则表达式的字符串本身不合法,会报错;
- 用编译后的正则表达式去匹配字符串。
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配。re.compile() 函数就是用来实现此功能的。compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match()、search() 和 findall() 这三个函数使用。
函数语法:
re.compile( pattern [ ,flags ] )
函数参数说明:
- pattern : 一个字符串形式的正则表达式
- flags : 可选,表示匹配模式,比如忽略大小写,多行模式等。具体使用见上面标志位的讲解。
实例:
import re
astr="A12as34er567q!'3456"
#编译m1
m1=re.compile(r"\d\d") #找两位连续的数字
#使用
print(m1.search(astr)) #从字符串中找两位连续的数字,并返回第一组值
print(m1.search(astr,6)) #从6号位置( 默认起始位置为0 )开始找两位连续的数字,并返回第一组值
print(m1.findall(astr)) #从astr中找到所有两位连续的数字,并以列表的形式返回
print(m1.findall(astr,3,12)) #指定在字符串当中的某个位置找(第3-12位)
运行结果:
<_sre.SRE_Match object; span=(1, 3), match='12'>
<_sre.SRE_Match object; span=(9, 11), match='56'>
['12', '34', '56', '34', '56']
['34', '56']
#编译m2
m2=re.compile(r"a",re.I) #找字母a,忽略大小写
#使用
print(m2.findall(astr))
运行结果:
['A', 'a']