python爬虫信息技术实践之三 re正则库
正则表达式(Regular Expression,简称正则或RegExp)是一种强大的文本模式匹配工具,被广泛应用于字符串的搜索、替换、验证等场景。Python 的 re 库为正则表达式提供了丰富的支持,使得开发者能够在处理文本数据时更加高效和灵活。本文将深入探讨 Python 中的 re 库,包括基本语法、常见用法、高级技巧以及一些最佳实践,旨在帮助读者更全面地理解和运用正则表达式。
正则表达式基础
什么是正则表达式?
正则表达式是一种用于描述字符串匹配规则的表达式。它由普通字符和元字符(特殊字符)组成,通过这些字符的组合,可以构建出具有强大匹配能力的规则。正则表达式在处理文本时能够实现高度灵活的模式匹配,从而满足不同场景下的需求。
基本语法
在 Python 的 re 库中,通过导入该库,我们可以使用正则表达式的基本功能。以下是一些基本的正则表达式元字符:
.:匹配除换行符以外的任意字符。
^:匹配字符串的开头。
$:匹配字符串的结尾。
*:匹配前一个字符的零次或多次。
+:匹配前一个字符的一次或多次。
?:匹配前一个字符的零次或一次。
[]:字符集,匹配其中的任一字符。
|:或,匹配两个或多个正则表达式之一。
通过组合这些元字符,我们可以构建出丰富多彩的正则表达式。
re 模块的基本用法
简单正则
让我们从最简单的正则表达式开始吧。由于正则表达式是用来操作字符串的,我们将从最常见的任务开始:匹配字符。
关于正则表达式背后的计算机科学的详细解释(确定性和非确定性有限自动机),你可以参考几乎所有关于编写编译器的教科书。
匹配字符
大多数字母和符号都会简单地匹配自身。例如,正则表达式 test
将会精确地匹配到 test
。(你可以启用不区分大小写模式,让这个正则也匹配 Test
或 TEST
,稍后会详细介绍。)
但该规则有例外。有些字符是特殊的 元字符(metacharacters),并不匹配自身。事实上,它们表示匹配一些非常规的内容,或者通过重复它们或改变它们的含义来影响正则的其他部分。本文的大部分内容都致力于讨论各种元字符及其作用。
这是元字符的完整列表。它们的含义将在本 HOWTO 的其余部分进行讨论。
. ^ $ * + ? { } [ ] \ | ( )
首先介绍的元字符是 [
和 ]
。这两个元字符用于指定一个字符类,也就是你希望匹配的字符的一个集合。这些字符可以单独地列出,也可以用字符范围来表示(给出两个字符并用 '-'
分隔)。例如,[abc]
将匹配 a
、b
、c
之中的任意一个字符;这与 [a-c]
相同,后者使用一个范围来表达相同的字符集合。如果只想匹配小写字母,则正则表达式将是 [a-z]
。
元字符 (除了 \
) 在字符类中是不起作用的。 例如,[akm$]
将会匹配以下任一字符 'a'
, 'k'
, 'm'
或 '$'
;'$'
通常是一个元字符,但在一个字符类中它的特殊性被消除了。
你可以通过对集合 取反 来匹配字符类中未列出的字符。方法是把 '^'
放在字符类的最开头。 例如,[^5]
将匹配除 '5'
之外的任何字符。 如果插入符出现在字符类的其他位置,则它没有特殊含义。 例如:[5^]
将匹配 '5'
或 '^'
。
也许最重要的元字符是反斜杠,\
。 与 Python 字符串字面量一样,反斜杠后面可以跟各种字符来表示各种特殊序列。它还用于转义元字符,以便可以在表达式中匹配元字符本身。例如,如果需要匹配一个 [
或 \
,可以在其前面加上一个反斜杠来消除它们的特殊含义:\[
或 \\
。
一些以 '\'
开头的特殊序列表示预定义的字符集合,这些字符集通常很有用,例如数字集合、字母集合或非空白字符集合。
让我们举一个例子:\w
匹配任何字母数字字符。 如果正则表达式以 bytes 类型表示,\w
相当于字符类 [a-zA-Z0-9_]
。如果正则表达式是 str 类型,\w
将匹配由 unicodedata
模块提供的 Unicode 数据库中标记为字母的所有字符。 通过在编译正则表达式时提供 re.ASCII
标志,可以在 str 表达式中使用较为狭窄的 \w
定义。
以下为特殊序列的不完全列表。 有关 Unicode 字符串正则表达式的序列和扩展类定义的完整列表,参见标准库参考中 正则表达式语法 的最后一部分 。通常,Unicode 版本的字符类会匹配 Unicode 数据库的相应类别中的任何字符。
\d
-
匹配任何十进制数字,等价于字符类
[0-9]
。 \D
-
匹配任何非数字字符,等价于字符类
[^0-9]
。 \s
-
匹配任何空白字符,等价于字符类
[ \t\n\r\f\v]
。 \S
-
匹配任何非空白字符,等价于字符类
[^ \t\n\r\f\v]
。 \w
-
匹配任何字母与数字字符,等价于字符类
[a-zA-Z0-9_]
。 \W
-
匹配任何非字母与数字字符,等价于字符类
[^a-zA-Z0-9_]
。
这些序列可以包含在字符类中。 例如,[\s,.]
是一个匹配任何空白字符、','
或 '.'
的字符类。
本节的最后一个元字符是 .
。 它匹配除换行符之外的任何字符,并且有一个可选模式( re.DOTALL
),在该模式下它甚至可以匹配换行符。 .
通常用于你想匹配“任何字符”的场景。
重复
能够匹配各种各样的字符集合是正则表达式可以做到的第一件事,而这是字符串方法所不能做到的。但是,如果正则表达式就只有这么一个附加功能,它很难说的上有多大优势。另一个功能是,你可以指定正则的某部分必须重复一定的次数。
我们先来说说重复元字符 *
。 *
并不是匹配一个字面字符 '*'
。实际上,它指定前一个字符可以匹配零次或更多次,而不是只匹配一次。
例如,ca*t
将匹配 'ct'
( 0 个 'a'
)、'cat'
( 1 个 'a'
)、 'caaat'
( 3 个 'a'
)等等。
类似 *
这样的重复是 贪婪的 。当重复正则时,匹配引擎将尝试重复尽可能多的次数。 如果表达式的后续部分不匹配,则匹配引擎将回退并以较少的重复次数再次尝试。
通过一个逐步示例更容易理解这一点。让我们分析一下表达式 a[bcd]*b
。 该表达式首先匹配一个字母 'a'
,接着匹配字符类 [bcd]
中的零个或更多个字母,最后以一个 'b'
结尾。 现在想象一下用这个正则来匹配字符串 'abcbd'
。
步骤 |
匹配 |
说明 |
---|---|---|
1 |
|
正则中的 |
2 |
|
引擎尽可能多地匹配 |
3 |
失败 |
引擎尝试匹配 |
4 |
|
回退,让 |
5 |
失败 |
再次尝试匹配 |
6 |
|
再次回退,让 |
6 |
|
再次尝试匹配 |
此时正则表达式已经到达了尽头,并且匹配到了 'abcb'
。 这个例子演示了匹配引擎一开始会尽其所能地进行匹配,如果没有找到匹配,它将逐步回退并重试正则的剩余部分,如此往复,直至 [bcd]*
只匹配零次。如果随后的匹配还是失败了,那么引擎会宣告整个正则表达式与字符串匹配失败。
另一个重复元字符是 +
,表示匹配一次或更多次。请注意 *
与 +
之间的差别。 *
表示匹配 零次 或更多次,也就是说它所重复的内容是可以完全不出现的。而 +
则要求至少出现一次。举一个类似的例子, ca+t
可以匹配 'cat'
( 1 个 'a'
)或 'caaat'
( 3 个 'a'
),但不能匹配 'ct'
。
此外还有两个重复操作符或限定符。 问号 ?
表示匹配一次或零次;你可以认为它把某项内容变成了可选的。 例如,home-?brew
可以匹配 'homebrew'
或 'home-brew'
。
最复杂的限定符是 {m,n}
,其中 m 和 n 都是十进制整数。 该限定符表示必须至少重复 m 次,至多重复 n 次。 例如,a/{1,3}b
将匹配 'a/b'
, 'a//b'
和 'a///b'
。 它不能匹配 'ab'
,因为其中没有斜杠,也不能匹配 'a////b'
,因为其中有四个斜杠。
m 和 n 不是必填的,缺失的情况下会设定为默认值。缺失 m 会解释为最少重复 0 次 ,缺失 n 则解释为最多重复无限次。
最简单情况 {m}
将与前一项完全匹配 m 次。 例如,a/{2}b
将只匹配 'a//b'
。
细心的读者可能会注意到另外三个限定符都可以使用此标记法来表示。 {0,}
等同于 *
, {1,}
等同于 +
, 而 {0,1}
等同于 ?
。 在可能的情况下使用 *
, +
或 ?
会更好,因为它们更为简短易读。
现在我们已经了解了一些简单的正则表达式,那么我们如何在 Python 中实际使用它们呢? re
模块提供了正则表达式引擎的接口,可以让你将正则编译为对象,然后用它们来进行匹配。
编译正则表达式
正则表达式被编译成模式对象,模式对象具有各种操作的方法,例如搜索模式匹配或执行字符串替换。
import re
2.基本的正则表达式操作
2.1 搜索模式
search
函数在整个字符串中搜索模式。pat = re.compile("AA") # 此处的AA是正则表达式,用来验证其他的字符串
m = pat.search("CBA") # search字符串被校验的内容
m = pat.search("ABCAA")
m = pat.search("AABCAADDCCAAA") # search方法,进行比对查找
print(m)
# 没有模式对象
m = re.search("asd", "Aasd") # 前面的字符串是规则(模板), 后面的字符串是被校验的对象
print(m)
2.2 查找所有匹配项
findall
函数查找字符串中所有匹配项。pattern = r'\d+'
string = 'There are 123 apples and 456 oranges'
matches = re.findall(pattern, string)
print("All matches:", matches)
2.3 替换匹配项
sub
函数替换字符串中的匹配项pattern = r'apples'
replacement = 'bananas'
string = 'I like apples and apples are sweet'
result = re.sub(pattern, replacement, string)
print("Replaced string:", result)
反斜杠灾难
如前所述,正则表达式使用反斜杠字符 ('\'
) 来表示特殊形式或允许使用特殊字符而不调用它们的特殊含义。 这与 Python 在字符串文字中用于相同目的的相同字符的使用相冲突。
假设你想要编写一个与字符串 \section
相匹配的正则,它可以在 LaTeX 文件中找到。 要找出在程序代码中写入的内容,请从要匹配的字符串开始。 接下来,您必须通过在反斜杠前面添加反斜杠和其他元字符,从而产生字符串 \\section
。 必须传递给 re.compile()
的结果字符串必须是 \\section
。 但是,要将其表示为 Python 字符串文字,必须 再次 转义两个反斜杠。
字符 |
阶段 |
---|---|
|
被匹配的字符串 |
|
为 |
|
为字符串字面转义的反斜杠 |
简而言之,要匹配文字反斜杠,必须将 '\\\\'
写为正则字符串,因为正则表达式必须是 \\
,并且每个反斜杠必须表示为 \\
在常规Python字符串字面中。 在反复使用反斜杠的正则中,这会导致大量重复的反斜杠,并使得生成的字符串难以理解。
解决方案是使用 Python 的原始字符串表示法来表示正则表达式;反斜杠不以任何特殊的方式处理前缀为 'r'
的字符串字面,因此 r"\n"
是一个包含 '\'
和 'n'
的双字符字符串,而 "\n"
是一个包含换行符的单字符字符串。 正则表达式通常使用这种原始字符串表示法用 Python 代码编写。
此外,在正则表达式中有效但在 Python 字符串文字中无效的特殊转义序列现在导致 DeprecationWarning
并最终变为 SyntaxError
。 这意味着如果未使用原始字符串表示法或转义反斜杠,序列将无效。
注意事项
3.、match、fullmatch、findall、finditer、search
match是根据传入的正则表达式匹配对应的字符串。并且是从开始字符匹配。匹配到字符不能匹配的位置然后返回匹配的对象。返回的对象是MatchObject或者空。
fullmatch,和match的区别是fullmatch必须是目标字符串从开始到结束全部都匹配这个传入的正则表达式才可以返回匹配对象。否则返回空。返回的对象也是MatchObject对象。
findall,从字符串中从前向后依次查找匹配正则表达式的子串。最后把多个匹配的子串用列表形式返回。列表的每个元素是一个匹配的子串。
finditer,和findall功能相同。但是finditer返回的是一个可迭代对象。可以用next()方法读取。然后再用group方法读取匹配的内容。
search,是从前向后依次扫描整个字符串。返回第一个匹配的子串。匹配位置不一定是开始字符。但是如果用*来表示0-n次重复时需要测试。有时候会返回空字符串。
- 点赞
- 收藏
- 关注作者
评论(0)