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模块匹配字符串一般步骤:
- 先将正则表达式的字符串形式编译为Pattern实例。
- 然后使用Pattern实例处理文本并获得匹配结果(一个Match实例)。
- 最后使用Match实例获得信息,进行其他的操作。
到这里我们就可以从一堆字符串中提取出我们想要的部分了。
部分内容参考:
http://www.jb51.net/article/31491.htm
http://www.jb51.net/article/34642.htm