python3爬虫(二) re模块与正则表达式

时间:2021-08-11 21:55:28

python3爬虫(二) re模块与正则表达式


爬虫总结目录:
- python3爬虫(一)requests库的学习
- python3爬虫(二) re模块与正则表达式


在python3爬虫(一)中,我总结了requests库的相关方法和一些使用举例,通过requests模块,我们可以获取网页的html或者其他形式的内容,但是这些内容太过于繁杂,所以我们要对这些内容进行处理和提取,比如如果我们想要下载网页中存在的图片,那我们需要找出所有内容中以.jpg或者.png等结尾的字符串并提取出来。

1、正则表达式

正则表达式就是一组可以匹配一类字符串的表达式。通过提取网页中与正则表达式相匹配的字符串,我们就可以获取想要的字符串,但是正则表达式的语法和语法规则是怎么样的呢?

(1)正则表达式匹配的格式

下表总结了正则表达式常用的规则:

语法 意义 说明
“.” 任意字符
“^” 字符串开始 ‘^hello’匹配’helloworld’而不匹配’aaaahellobbb’
“$” 字符串结尾 与上同理
“*” 0 个或多个字符(贪婪匹配) <*>匹配<\title>chinaunix<\title>
“+” 1 个或多个字符(贪婪匹配) 与上同理
“?” 0 个或多个字符(贪婪匹配) 与上同理
*?,+?,?? 以上三个取第一个匹配结果(非贪婪匹配) <*>匹配<\title>
{m,n} 对于前一个字符重复m到n次,{m}亦可 a{6}匹配6个a、a{2,4}匹配2到4个a
{m,n}? 对于前一个字符重复m到n次,并取尽可能少 ‘aaaaaa’中a{2,4}只会匹配2个
“\” 特殊字符转义或者特殊序列
[] 表示一个字符集 [0-9]、[a-z]、[A-Z]、[^0]
(…) 匹配括号中任意表达式
(?#…) 注释,可忽略
(?=…) Matches if … matches next, but doesn’t consume the string. ‘(?=test)’ 在hellotest中匹配hello
(?!…) Matches if … doesn’t match next. ‘(?!=test)’ 若hello后面不为test,匹配hello
(?<=…) Matches if preceded by … (must be fixed length). ‘(?<=hello)test’ 在hellotest中匹配test
(? Matches if not preceded by … (must be fixed length). ‘(?

正则表达式特殊序列表

符号 意义
\A 只在字符串开始进行匹配
\Z 只在字符串结尾进行匹配
\b 匹配位于开始或结尾的空字符串
\B 匹配不位于开始或结尾的空字符串
\d 相当于[0-9]
\D 相当于[^0-9]
\s 匹配任意空白字符:[\t\n\r\r\v]
\S 匹配任意非空白字符:[^\t\n\r\r\v]
\w 匹配任意数字和字母:[a-zA-Z0-9]
\W 匹配任意非数字和字母:[^a-zA-Z0-9]

(2) 贪婪格式和非贪婪格式

1 概述:贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。

属于贪婪模式的量词,也叫做匹配优先量词,包括:
“{m,n}”、“{m,}”、“?”、“*”和“+”。

在一些使用NFA引擎的语言中,在匹配优先量词后加上“?”,即变成属于非贪婪模式的量词,也叫做忽略优先量词,包括:
“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。

2 贪婪与非贪婪模式匹配原理

举例:

源字符串:aa<div>test1</div>bb<div>test2</div>cc 

正则表达式一:<div>.*</div> 

匹配结果一:<div>test1</div>bb<div>test2</div> 

正则表达式二:<div>.*?</div> 

匹配结果二:<div>test1</div>(这里指的是一次匹配结果,所以没包括<div>test2</div>

仅从应用角度分析,可以这样认为,贪婪模式,就是在整个表达式匹配成功的前提下,尽可能多的匹配,也就是所谓的“贪婪”,通俗点讲,就是看到想要的,有多少就捡多少,除非再也没有想要的了.非贪婪模式,就是在整个表达式匹配成功的前提下,尽可能少的匹配,也就是所谓的“非贪婪”,通俗点讲,就是找到一个想要的捡起来就行了,至于还有没有没捡的就不管了。

3 前提条件

无论是贪婪模式还是非贪婪模式,都是在字符串成功匹配的前提下,字符串匹配不成功,贪婪模式和非贪婪模式都不会发挥作用。

例如下面的例子:

正则表达式三:<div>.*</div>bb 

匹配结果三:<div>test1</div>bb 

正则表达式四:<div>.*?</div>cc 

匹配结果四:<div>test1</div>bb<div>test2</div>cc 

2、re模块

re模块提供了正则表达式匹配操作,要搜索的模式和字符串都可以是Unicode字符串(str)以及8位字符串(bytes)。但是,Unicode字符串和8位字符串不能混用:也就是说,不能将Unicode字符串与字节模式匹配,反之亦然; 类似地,当要求替换时,替换字符串必须与模式和搜索字符串的类型相同。

下面总结了re模块常用的方法

(1)re.compile(pattern,flags = 0 )

将一个正则表达式模式编译成一个正则表达式对象,可以用它进行匹配 match(),search()以及其他方法,如下所述。

prog = re.compile(pattern)
result = prog.match(string)

相当于:

result = re.match(pattern, string)

flags定义包括:

re.I:忽略大小写
re.L:表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M:多行模式
re.S:' . '并且包括换行符在内的任意字符(注意:' . '不包括换行符)
re.U: 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库

所以re.compile()如果在单个程序中多次使用表达式,则使用并保存生成的正则表达式对象以便重用将会更有效。

(2) re.search(pattern,string,flags = 0 )

扫描字符串查找正则表达式模式产生匹配的第一个位置 ,并返回相应的匹配对象。如果字符串中没有位置与模式匹配,则返回None.

>>> re.search(r'[ab]',"aaaasdfg")
<_sre.SRE_Match object; span=(0, 1), match='a'>

(3) re.match(pattern,string,flags = 0 )

如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的匹配对象。如果字符串不匹配模式,则返回None。

>>> re.match("a", "abcdef")
<_sre.SRE_Match object; span=(0, 1), match='a'>

请注意,即使在MULTILINE模式下,re.match()只会匹配字符串的开头,而不是每行的开头。

search()与match()的区别

match()只匹配字符串的开头!

看例子:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<_sre.SRE_Match object; span=(2, 3), match='c'>

以正值开始的正则表达式’^’可用于search()限制字符串开始处的匹配:

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<_sre.SRE_Match object; span=(0, 1), match='a'>

但是请注意,在MULTILINE模式中,match()只匹配字符串的开头,而使用search()正则表达式’^’匹配每行的开头。

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<_sre.SRE_Match object; span=(4, 5), match='X'>

match对象

匹配对象总是有一个布尔值True。由于match()与search()返回None 时没有匹配,我们可以测试是否有一个简单的匹配 if语句:

match = re.search(pattern, string)
if match:
    process(match)

匹配对象支持以下方法和属性:

  • match.group([ group1,… ] )
  • match.groups(default=None)
  • match.groupdict(default=None)
  • match.start([group])
  • match.end([group])
  • match.span([group])

(4)re.findall(pattern,string,flags = 0 )

返回字符串中模式的所有非重叠匹配项,作为字符串列表。 字符串从左到右扫描,匹配按找到的顺序返回。 如果模式中存在一个或多个组,则返回组的列表; 如果该模式有多个组,这将是一个元组列表。

>>> re.findall(r'a',"aboisdhiajhsasa")
['a', 'a', 'a', 'a']

(5)re.split(pattern, string, maxsplit=0, flags=0)

由模式发生的分割字符串。如果在模式中使用捕获圆括号,则模式中所有组的文本也将作为结果列表的一部分返回。如果maxsplit不为零,则最多 发生maxsplit分割,并将其余的字符串作为列表的最后一个元素返回。
举例:

>>> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

如果分隔符中存在捕获组,并且匹配字符串的开头,则结果将以空字符串开头。字符串的末尾也是一样:

>>> re.split('(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']

总结
re模块匹配字符串一般步骤:

  1. 先将正则表达式的字符串形式编译为Pattern实例。
  2. 然后使用Pattern实例处理文本并获得匹配结果(一个Match实例)。
  3. 最后使用Match实例获得信息,进行其他的操作。

到这里我们就可以从一堆字符串中提取出我们想要的部分了。

部分内容参考:
http://www.jb51.net/article/31491.htm
http://www.jb51.net/article/34642.htm