小言_互联网的博客

Python爬虫、后端,使用正则表达式,看这一篇就够了!

464人阅读  评论(0)

一、正则表达式引入

1. 正则表达式简介

正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个或一批“字符串”是否符合这个特征。比如正则表达式“ab+” 描述的特征是“一个’a’ 和 任意个 ‘b’”,那么’ab’, ‘abb’,'abbbbbbbbbb’都符合这个特征。

正则表达式可以用来

  • 验证字符串是否符合指定特征,比如验证是否是合法的邮件地址;
  • 用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便;
  • 用来替换,比普通的替换更强大。

需要注意的是,正则表达式并非某一个编程语言所独有的,在不同的编程语言中都是适用的,只是不同语言会有各自额外的个性化部分,本文所涉及的正则表达式都是基于Python语言,包括通用的,也包括Python语言独有的。

2. Python的re模块

在Python中,如果需要使用正则表达式对字符串进行各种操作时(如:查找、替换、判断),可以使用re(regular expression)模块

二、正则表达式详解

1. re模块常用函数和对象

为了方便后续通过Python来演示基本的正则表达式操作,下面先介绍在Python的re模块中涉及的match()函数和match object对象及其基本的对象方法group()

match()函数

  • 函数原型:re.match(pattern, string, flags=0)
    • pattern:正则表达式
    • string:待匹配字符串
  • 函数功能:按照pattern指定的规则,从待匹配字符串的开头开始匹配,如果成功匹配,则返回一个match object,否则返回None

匹配成功对象

Python中,使用re模块中的函数通过正则表达式成功匹配后,多数函数的返回值都是match object,而不是直接以字符串的形式返回匹配得到的结果(如本文后面将介绍的search()函数等)。

进一步地,可通过match object的一系列方法,对匹配得到的结果进行操作,如获取匹配出的字符串等。

匹配成功对象方法group()

  • 方法原型:group([group1, …])groupN为非负整数)
  • 方法功能:
    • 在正则表达式中不使用分组()的情况下,不指定参数或仅能指定参数为0,此时返回匹配得到的字符串整体
    • 在正则表达式中使用分组()的情况下(相关举例请见后续分组匹配操作部分):
      • 如果仅指定一个非0正整数,则仅返回一个对应分组位置匹配得到的字符串;
      • 如果指定多个参数,则以元组形式返回对应参数数量的字符串;
      • 如果指定多个参数,且其中有0,则返回字符串组成的一个元组,且在0对应位置为匹配得到的字符串整体。

2. 常用匹配规则

下面的正则表达式在几乎所有语言中都通用。

2.1 匹配单个字符

符号 功能
\d 匹配数字,即0-9
[ ] 匹配[ ]中列举出的字符
. 匹配任意一个字符(’\n’除外)
\D 匹配非数字
\s 匹配空白,即空格或tab键
\S 匹配非空白
\w 匹配字母、数字、下划线,即a-z、A-Z、0-9、_
\W 匹配非字母、数字、下划线
  • \d:re.match(r"复仇者联盟\d", “待匹配字符串”),可成功匹配出"复仇者联盟0"、“复仇者联盟1” ...... "复仇者联盟9"这样的字符串。
  • []:re.match(r"复仇者联盟[123]", “待匹配字符串”)或者re.match(r"复仇者联盟[1-3]", “待匹配字符串”)可限制只能成功匹配出"复仇者联盟1"、“复仇者联盟2”、“复仇者联盟3”。

2.2 匹配多个字符

符号 功能
{m} 匹配{m}前一个字符出现m次
{m,n} 匹配{m, n}前一个字符出现m次到n次(注意数字m和n之间不能有空格
? 匹配?前一个正则表示的字符(串)出现0次或1次(同样可以表示正则表达式为非贪婪模式)
* 匹配*前一个正则表示的字符(串)出现0次或无限次
+ 匹配+前一个正则表示的字符(串)出现1次或无限次
  • {m}:re.match(r"\d{11}", “待匹配字符串”),仅当待匹配字符串的至少前11位为数字时,匹配成功;
  • {m,n}:re.match(r"\w{1,3}", “待匹配字符串”),仅当待匹配字符串的前1到3个字符是数字、字母、下划线时,匹配成功。

2.3 匹配开头结尾

符号 功能
^ 匹配字符串开头(需要主要的是,当^不出现在正则表达式的开头时,
表示取反操作
$ 匹配字符串结尾

re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", var_name),可判断var_name是否满足变量命名规范:

  • 由于match函数默认从字符串开头进行匹配,故符号^可省略;
  • [a-zA-Z_]表示:var_name必须以其中任意一个字符开始;
  • [a-zA-Z0-9_]*$表示:var_name在以字母或下划线开头后,直到结束,可以跟上0个或任意个数字、字母、下划线。

2.4 分组匹配操作

假设有这样一个需求:请匹配出Gmail格式的邮箱地址,且@符号之前必须有4到20位。

根据前面的知识,可以简单得出对应Python语句为re.match(r"[a-zA-Z0-9_]{4,20}@gmail.com$", email_addr)

如果基于上述需求,更进一步地,要求写出一个正则表达式,使之除了能够匹配出gmail格式的邮箱,还可以匹配出QQ、Foxmail等格式邮箱,此时则需要利用正则表达式中分组的概念。

字符 功能
| 匹配|左右任意一个表达式
(qwerty) 将括号中的所有字符作为一个分组
\num 引用分组num匹配到的字符串
(?P<name>) 为分组起别名
(?P=name) 引用别名为name分组匹配到的字符串

分组匹配((qwerty)|结合使用):

  • 语法:(pattern1|pattern2|…|patternN)
  • 举例:

对于正则表达式:re.match(r"[a-zA-Z0-9_]{4,20}@(gmail|qq|foxmail).com$", email_addr).group(),如果email_addr是符合要求的Gmail、QQ、Foxmail其中任意一种,该语句即返回邮箱全称。

group()方法参数:

对于re.match(r"([a-zA-Z0-9_]{4,20})@(gmail|qq|foxmail).com$", email_addr).group(),假设email_addr为larry_lage@gmail.com:

  • 如果在group()方法的参数位置填写1,则该正则的返回值为’larry_page’
  • 如果在group()方法的参数位置填写2,则返回值为’gmail’;
  • 如果在group()方法的参数位置填写0, 1, 2,则返回值为(‘larry_page@gmail.com’, ‘larry_page’, ‘gmail’)

引用分组((qwerty)\num结合使用):

如果现在的需求为:匹配出由一对HTML标签及其包裹内容在内的字符串,由于标签成对出现的特性,方便起见,可用到引用分组

如:

  • 对于正则表达式:re.match(r"<(\w*)>.*</\1>", html_str),在html_str格式满足<tag_name>content</tag_name>时匹配成功;
  • 对于正则表达式:re.match(r"<(\w*)><(\w*)>.*</\2></\1>", html_str),在html_str格式满足<tag_name1><tag_name2>content</tag_name2></tag_name1>时匹配成功。

分组起别名:

上述通过引用分组实现的方式,还可以通过为分组起别名的方式来实现,即:

对于正则表达式:re.match(r"<(?P<p1>\w*)><(?P<p2>\w*)>.*</(?P=p2)></(?P=p1)>", html_str),在html_str格式满足<tag_name1><tag_name2>content</tag_name2></tag_name1>时也可以匹配成功。

3. re模块高级函数和对象

3.1 search

  • 函数原型:re.search(pattern, string, flags=0)
    • pattern:同函数match()
    • string:同函数match()
  • 函数功能:扫描待匹配字符串,尝试匹配第一个符合pattern所指定规则的字符串,如果成功匹配,则返回一个match object,否则返回None
  • 使用案例:

对于正则:re.search(r"\d+", “当前Python在编程榜上排名为3”).group(),其返回值为3

3.2 findall

  • 函数原型:re.findall(pattern, string, flags=0)
    • pattern:同上
    • string:同上
  • 函数功能:
    • 以列表形式返回通过pattern匹配string成功后的所有字符串;
    • pattern以从左往右的顺序扫描string,返回的字符串按照匹配的顺序排列;
    • 如果在pattern中存在分组,则以列表形式返回一系列元组。
  • 使用案例:

对于正则:re.findall(r"\d+", “当前Python在编程榜上排名为3,Java为1”),其返回值为[‘3’, ‘1’]。

3.3 sub

  • 函数原型:re.sub(pattern, repl, string, count=0, flags=0)
    • pattern:同上
    • string:同上
    • repl:希望实现替换的对象,可以是字符串或者函数repl是函数的情形请见Python正则表达式模块中函数sub的高级用法)。
    • count:该参数用于指定最大的匹配替换次数,必须为非负整数,该参数如果省略或指定为0,则所有得到的匹配都将被替换。
  • 函数功能:返回一个被repl替换后的字符串,该字符串的生成方式为:将string所有符合规则pattern的子字符串全部替换成repl,且在替换时进行非重叠处理(关于非重叠的含义请见Python正则表达式模块中函数sub的高级用法
  • 使用案例:

对于正则:re.sub(r"\d+", “1”, “当前Python在编程榜上排名为3”),其返回值为’当前Python在编程榜上排名为1’。

3.4 split

  • 函数原型:re.split(pattern, string, maxsplit=0, flags=0)
    • pattern:同上
    • string:同上
    • maxsplit:非负整数,如果不指定或指定为0,则按照匹配出string中有符合pattern规则的次数来分割待匹配字符串,如指定非零正整数,则最多分割maxsplit次。
  • 函数功能:
    • 通过_pattern成功匹配出的字符串对string进行分割并以列表形式返回分割结果;
    • 如果在pattern中使用分组符号(),则通过pattern匹配出的用于的分割字符串也会在列表中被返回。
  • 使用案例:
  • 对于正则1:re.split(r’\W+’, ‘Words, words, words.’),其结果为:[‘Words’, ‘words’, ‘words’, ‘’]
  • 对于正则2:re.split(r’(\W+)’, ‘Words, words, words.’):,其结果为:[‘Words’, ', ', ‘words’, ', ‘, ‘words’, ‘.’, ‘’](用于分割的字符串’, '也被包含在了返回的列表中)。

4. 贪婪和非贪婪模式

  • 前面介绍的正则表达式修饰符*+?都会结合其前后的正则表达式,尽可能匹配出符合要求的最长字符串,这就是所谓的贪婪模式
  • 修饰符?可用于指定其之前的正则表达式仅匹配出符合要求的最短字符串,这就是所谓的非贪婪模式

举例来说:

  • re.match(r"<.*>", “<a>b<c>”).group()的结果为’<a>b<c>’,这就是所谓贪婪匹配;
  • re.match(r"<.*?>", “<a>b<c>”).group()(即加上非贪婪模式修饰符?)的结果为’<a>’,这就是所谓非贪婪匹配。

5. 原始字符串符号r

四、参考材料

  1. 揭开正则表达式的神秘面纱
  2. 收藏一波:常用正则表达式公式总结

转载:https://blog.csdn.net/weixin_37780776/article/details/105624008
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场