Python爬虫——正则表达式

本文深入讲解Python中的正则表达式应用,包括基本概念、常见应用场景、re模块使用方法及分组功能,助您掌握高效文本处理技巧。

1.正则表达式简介

1.1 概念

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑

1.2 正则表达式的应用场景

  • 表单验证(例如:手机号,身份证号,订单号等等)
  • 爬虫

2.Python中的正则表达式

2.1 普通字符

  • 字⺟、数字、汉字、下划线、以及没有特殊定义的符号,都是"普通字符"。正则表达式中的普通字符,在匹配的时候,只匹配与⾃身相同的⼀个字符。 例如:表达式life,在匹配字符串life is long or short.时,匹配结果是:成功;匹配到的内容是life;匹配到的位置开始于0,结束于4。(注:下标从0开始还是从1开始,因当前编程语⾔的不同而可能不同)
import re
key_words = 'life'
words = 'life is long or short.'
print(re.match(key_words,words))            # <re.Match object; span=(0, 4), match='life'>
print(re.match(key_words,words).group())    #life
print(re.match(key_words,words).start())    #0
print(re.match(key_words,words).end())      #4
print(re.match(key_words,words).span())     #(0, 4)

  • match函数
    • match(pattern, string, flags=0)
    • pattern:正则表达式,若匹配成功则返回一个match对象,否返回None
    • string:表示要匹配的字符串
    • flag:控制匹配方式,例如:是否区分大小写、是否多行匹配等等
# match函数原码
def match(pattern, string, flags=0):
    """Try to apply the pattern at the start of the string, returning
    a Match object, or None if no match was found."""
    return _compile(pattern, flags).match(string)
# 后面的_compile(pattern, flags)函数较长,若有需要了解可以在pycharm中查看

2.2 元字符

正则表达式中使⽤了很多元字符,⽤来表示⼀些特殊的含义或功能

表达式匹配方式
.匹配换行符(\n)以外的任意一个字符
|逻辑或操作
[]匹配括号(字符集)中的一个字符
[^]对字符集求反,尖括号需在括号最前面
-指定字符集区间,例如a-z表示a到z的所有字母
\对其后一个字符进行转义
()对表达式进行分组
# 元字符
import re
# .
print(re.match(r'N.thani.l','Nathaniel').group())    #Nathaniel
# |
print(re.match(r'Nat|kds','Nathaniel').group())      #Nat
# []
print(re.match(r'大[1234]','大4').group())           #大4
# ^
# print(re.match(r'大[^1234]','大4').group())        #AttributeError: 'NoneType' object has no attribute 'group'
print(re.match(r'大[^1234]','大0').group())          #大0
# -
print(re.match(r'大[1-4]','大4').group())            #大4
# \
print(re.match(r'大.[1-4]','大a4').group())          #大a4
# print(re.match(r'大\.[1-4]','大a4').group())       #AttributeError: 'NoneType' object has no attribute 'group'
# ()
print(re.match(r'N|mathaniel','Nathaniel').group())  #N
print(re.match(r'(N|m)athaniel','Nathaniel').group())   #Nathaniel

⼀些⽆法书写或者具有特殊功能的字符,采⽤在前⾯加斜杠""进⾏转义的⽅法。例如:

表达式匹配方式
\r、\n匹配回车和换行符
\t匹配制表符
\\匹配斜杠\
\^匹配尖括号^
\$匹配符号$
\.匹配小数点.

尚未列出的还有问号?、星号*和括号等其他的符号。所有正则表达式中具有特 殊含义的字符在匹配⾃身的时候,都要使⽤斜杠进⾏转义。这些转义字符的匹
配⽤法与普通字符类似,也是匹配与之相同的⼀个字符

2.3 预定义匹配字符集

  • 正则表达式中的⼀些表示⽅法,可以同时匹配某个预定义字符集中的任意⼀个字符。⽐如,表达式\d可以匹配任意⼀个数字。虽然可以匹配其中任意字符, 但是只能是⼀个,不是多个
表达式匹配方式
\d匹配一个数字
\w匹配一个字母、数字或下划线
\s匹配空格、制表符或换页符等空白字符中的一个
\D匹配一个非数字字符
\W匹配一个非字母、数字或下划线的字符
\S匹配非空格、制表符或换页符等空白字符的字符
import re
# \d
print(re.match(r'\d','456').group())        #4
# \w
print(re.match(r'\w','haha456').group())    #h
print(re.match(r'\w','2haha456').group())   #2
print(re.match(r'\w','_haha456').group())   #_
# \s
print(re.match(r'\s','\nhaha456').group())  #

# \D
# print(re.match(r'\D','456').group())        #print(re.match(r'\W','haha456').group())
print(re.match(r'\D','_456').group())         #_
# \W
# print(re.match(r'\W','haha456').group())    #print(re.match(r'\W','haha456').group())
print(re.match(r'\W','$456').group())         #$
# \S
# print(re.match(r'\S','\nhaha456').group())  #print(re.match(r'\W','haha456').group())
print(re.match(r'\S','456').group())          #4

2.4 重复匹配

  • 前⾯的表达式,⽆论是只能匹配⼀种字符的表达式,还是可以匹配多种字符其中任意⼀个的表达式,都只能匹配⼀次。但是有时候我们需要对某个字段进⾏重复匹配,如果依然采取以上的匹配方式,毫无疑问,工作量将会十分巨大,于是就需要重复匹配方式。
表达式功能
{m}表达式重复m次
{m,n}表达式至少重复m次,最多重复n次
{m,}表达式至少重复m次
匹配表达式0次或1次
+表达式至少出现1次
*表达式出现0到任意次
# 重复匹配
import re
# {m}
print(re.match(r'\d{3}','1234').group())        #123
# {m,n}
print(re.match(r'\d{3,5}','123').group())       #123
print(re.match(r'\d{3,5}','1234').group())      #1234
print(re.match(r'\d{3,5}','12345').group())     #12345
# {m,}
print(re.match(r'\d{3,}','1234145').group())    #1234145
# print(re.match(r'\d{3,}','12').group())       #AttributeError: 'NoneType' object has no attribute 'group'
# ?
print(re.match(r'\d?','1235').group())          #1
print(re.match(r'\d?','r235').group())          #
# +
print(re.match(r'\d+','1235').group())          #1235
# print(re.match(r'\d+','r235').group())        #AttributeError: 'NoneType' object has no attribute 'group'
# *
print(re.match(r'\d*','1235').group())          #1235
print(re.match(r'\d*','r235').group())          #

2.5 位置匹配和非贪婪匹配

2.5.1 位置匹配

当对匹配位置有一定要求时,例如:开头、结尾等,便需要对一定的位置进行要求

表达式匹配功能
^在字符开始的地方进行匹配,字符本身不参与匹配
$在字符结束的地方进行匹配,字符本省不参与匹配
\b匹配一个单词边界,即单词和空格之间的位置,符号本身不参与匹配
\B匹配非单词边界,即左右两边都不是\w范围或左右两边都不是\w范围时的字符间隙
# 位置匹配
import re
# ^
print(re.match(r'^\d','1235').group())              #1
# $
print(re.match(r'\d*$','1235').group())             #1235
# \b
print(re.match(r'\b\w+\b','hello world').group())   #hello
# \B
print(re.match(r'\w\B\w','hello world').group())    #he

2.5.2 贪婪匹配和非贪婪匹配模式

在重复匹配时,正则表达式默认总是尽可能多的匹配,这被称为贪婪模式。例如,针对⽂本bababab,表达式(b)(\w+)(b)中的\w+将匹配第⼀个b和最后⼀ 个v之间的所有字符ababa。可⻅,\w+在匹配的时候,总是尽可能多的匹配符合它规则的字符。同理,带有?、*和{m,n}的重复匹配表达式都是尽可能地多 匹配

# 贪婪匹配与非贪婪匹配
# 贪婪匹配
import re
print(re.match(r'<div>.*<div>','<div>alice<div>nathaniel<div>').group())
#<div>alice<div>nathaniel<div>
# 非贪婪匹配
print(re.match(r'<div>.*?<div>','<div>alice<div>nathaniel<div>').group())
#<div>alice<div>

检验数字的相关表达式:

表达式功能
^[0-9]*$数字
^\d{n}$n位数字
^\d{n,}$至少n位的数字
^[0-9]+[.[0-9]{2}]$有两位小数的正实数
^-[1-9]\d*$非零的负整数
^\d+(\.\d+)?$非负浮点数
^(-?\d+)(\.\d+)?$浮点数
# 检验数字的相关表达式
import re
# 数字
print(re.match(r'[0-9]*','41753').group())      #41753
# n位数字
print(re.match(r'[0-9]{4}','41753').group())    #4175
# 至少n位数字
print(re.match(r'[0-9]{4,}','41753').group())   #41753
# 有两位小数的正实数
print(re.match(r'[0-9]+\.[0-9]{2}','417.53').group())   #417.53
# 非零负整数
print(re.match(r'\-[1-9]\d*','-41753').group())         #-41753
# 非负浮点数
print(re.match(r'\d+(\.\d+)?','0.41753').group())       #0.41753
# 浮点数
print(re.match(r'\-?\d+(\.\d+)?','-10.41753').group())  #-10.41753

3.re模块常用方法

方法描述返回值
compile创建正则表达式字符串的模式对象re对象
search在字符串中查找第一个匹配到的对象或None
match在字符串开始处进行匹配在字符串开头匹配到的对象或者None
split根据模式的匹配项来分割字符分割后的字符串列表
findall列出字符串中模式的所有匹配项所有匹配到的字符串列表
sub将字符串中所有的匹配项用指定项进行替换完成替换后的新字符串
  • compile(pattern[,flags=0])
    • 该方法用于将正则表达式编译为pattern模式对象。第二个参数flags是匹配模式。
flags匹配模式描述
re.AASCII字符模式
re.I不区分字符大小写的模式
re.L做本地化识别匹配
re.M多行匹配,影响^和$
re.S使.这个通配符能够匹配包括换行在内的所有字符,针对多行匹配
re.U根据Unicode字符集解析字符。这个标志影响\b、\B、\w和\W
re.X该标志给与你更灵活的格式以便更将正则表达式写的易于理解
# flags匹配模式,考虑到标志的使用频率,这里主要演示re.I、re.M和re.S
import re
# re.I
str = 'abc123\n456\n789'
print(re.match('^ABC',str))              #None
print(re.match('^ABC',str,flags=re.I))   #<re.Match object; span=(0, 3), match='abc'>
# re.M
print(re.findall('^\w+',str))           #['abc123']
print(re.findall('^\w+',str,flags=re.M))#['abc123', '456', '789']
# re.S
print(re.match('.+',str))               #<re.Match object; span=(0, 6), match='abc123'>
print(re.match('.+',str,flags=re.S))    #<re.Match object; span=(0, 14), match='abc123\n456\n789'>
  • search(pattern,string[,flags=0])
    • 在文本内查找,返回第一个匹配到的字符串,与match的唯一区别是查找位置不是固定在开头
  • match(pattern,string[,flags=0])
    • 从文本开头进行查找,返回第一个匹配到的字符串
  • split(pattern,string[,maxsplit=0,flags=0])
    • 利用特定的字符对字符串进行分割。maxsplit代表分割次数
  • findall(pattern,string[,flags=0])
    • findall也是搜索函数,但它比match和search更加强大,前两者匹配到一个结果便会返回,而findall会对全文进行查找,将所有的匹配结果存到一个列表中用于返回
  • sub(pattern,repl,string[,count=0,flags=0])
    • 用指定的repl内容对字符串pattern进行替换,可以指定替换次数
# re模块常用方法
import re
# compile
str = 'abc123abc456abc789'
r = re.compile('abc')
print(r)                #re.compile('abc')
print(r.match(str))     #<re.Match object; span=(0, 3), match='abc'>
# search
r = re.search('123',str)
print(r)                #<re.Match object; span=(3, 6), match='123'>
# match
print(re.match('123',str))  #None
print(re.match('abc',str))  #<re.Match object; span=(0, 3), match='abc'>
# split
r = re.split('[b1]',str)
print(r)                    #['a', 'c', '23a', 'c456a', 'c789']
# findall
r = re.findall('abc',str)
print(r)                    #['abc', 'abc', 'abc']
# sub
r = re.sub('abc','def',str)
print(r)                    #def123def456def789

4.分组功能

Python的re模块有⼀个分组功能。这个分组可以理解为从已经匹配到的结果中对需要的部分再次进行筛选,具体由()实现分组。获取分组内容可以通过group()和groups()两种方法实现。

# 分组功能
import re
info = "Tom's score is 66,and Nathaniel's score is 88,last but not the least,Jerry got 99."
r = re.match('[\D]*(\d*)[\D]*(\d*)[\D]*(\d*)[\D]*',info)
print(r.group())                #Tom's score is 66,and Nathaniel's score is 88,last but not the least,Jerry got 99.
print(r.groups())               #('66', '88', '99')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值