Python 爬虫之正则表达
爬虫程序:请求网站并提取数据的自动化程序。
正则表达:Regular Expression(Regex)
正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式是一种文本模式,模式描述在搜索文本时要匹配的一个或多个字符串。(摘自百度百科)其中元字符是指一个或一组用来代替一个或多个字符的字符,(这里表述不够准确,Python中并没有字符的概念,只有字符串(str)类型,正则表达也不是Python独有,莫得办法)。
Python正则表达必备:re库
Signature: re.match(pattern, string, flags=0)
Signature: re.sub(pattern, repl, string, count=0, flags=0)
re.compile(pattern, flags=0)
1 import re 2 3 #re.match() # Try to apply the pattern at the start of the string, returning a Match object, or None if no match was found. 4 #re.search() # Scan through string looking for a match to the pattern, returning a Match object, or None if no match was found. 5 #re.findall() #Return a list of all non-overlapping matches in the string. 6 #re.sub() 7 # re.compile() #Compile a regular expression pattern, returning a Pattern object.
re库中常用的几个函数如上所示。注意各个函数的返回结果的类型。
match()函数(Signature: re.match(pattern, string, flags=0))用于匹配一个目标,且只能从待匹配的字符串第一个字符开始匹配,字符串内部满足模式(pattern)的不会匹配成功。如下代码所示,字符串string中显然有‘BC’,却显示匹配失败,原因在于‘BC’并不在该字符串的开端。
1 import re 2 3 string = 'ABCDEF' 4 5 result1 = re.match('AB',string) 6 result2 = re.match('BC',string) 7 if result1: 8 print('AB匹配成功') 9 else: 10 print('AB匹配失败') 11 if result2: 12 print('BC匹配成功') 13 else: 14 print('BC匹配失败')
AB匹配成功
BC匹配失败
search()函数(Signature: re.search(pattern, string, flags=0)), 用于匹配满足pattern的字符串,返回第一个满足pattern 的字符串,查询即停止。测试代码如下,通过直接打印返回结果可从span参数得到匹配结果的位置。re.match 对象有内置函数 span()与 group()用于匹配结果的提取。span()函数用于得到匹配目标的位置,group()函数用于得到匹配结果。
1 import re 2 3 string = 'ABCDBCEF' 4 5 result1 = re.search('AB',string) 6 result2 = re.search('BC',string) 7 8 print(result1) 9 print(result2) 10 11 print(result1.span()) 12 print(result1.group())
---------------------------------------------------------------------------------------------------------------------
<re.Match object; span=(0, 2), match='AB'>
<re.Match object; span=(1, 3), match='BC'>
(0, 2)
AB
re.findall()函数,(re.match(pattern, string, flags=0)),正则匹配中的大哥大。可以以列表的形式返回所有满足pattern的匹配结果,注意返回数据类型为列表。由于数据类型不再是 re.match 对象,因此不再支持 span()函数与 group()函数。
1 import re 2 3 string = 'ABCDBCEBCF' 4 5 result1 = re.findall('BC',string) 6 7 print(result1)
-------------------------------------------------------------------
['BC', 'BC', 'BC']
至此,已经有了正则匹配的能力(+_+),但是突然发现以上三个函数匹配的字符串并没有什么用,因为我们完全知道我们会得到什么字符串,并不神奇,这就关系到正则表达中的元字符的使用。
元字符:有哪些元字符
(我与百度有合作,这里可以直接问百度。=_=!)
常用的元字符:
^:用于指定模式的开端
[xyz] :[]用于匹配括号内的任意一个字符,注意 是一个,一个(醒目的红色,看到了吗),一个,字符
[^xyz]:^用于取反,即用于匹配不包含 x,y,x的任意一个字符,一个,一个,一个。。。。(注意 区分 ^ 单独使用的情景)
[a-z]:用于匹配a到 z的任意一个字符,一个
[^a-z]:懒得解释
+:匹配前面的子表达式,一次或多次,出现啦,多次,多个
*:匹配前面的子表达式,零(大写的0)次或多次,注意与 + 的微妙区别(常用)
?:匹配前面的子表达式 零次 或一次(好烦啊,)
{n}:确定匹配次数
{n,}:至少出现次数为 n
{n,m}:次数n-m次,注意‘,’后不能有空格
?:(是的还是我,不一样的烟火),当?在 限制符号之后时,用于非贪婪匹配。(常用)
(pattern):匹配pattern并获取这以匹配 (常用)
.:(能看到🐎,是个小点),用于匹配除换行符之外的任意字符
其他的真的可以问百度了。
1 import re 2 3 string = 'yijingbengpanle,qishishiyigejienengzhuyi,yijingbengpanle,qishishiyigejienengzhuyi.aaaaaaaaaaaaaaaaaaaaaaaaaa111' 4 5 result1 = re.findall('^yijingbengpanle',string) # ^ 用来确定从字符串开头 开始搜索,注意与下一行的区别 6 print(result1) 7 result1 = re.findall('yijingbengpanle',string) 8 print(result1) 9 10 result2 = re.findall('[^a-z]',string) # ^ 此时表示 非, 非a-z的字符 11 print(result2) 12 13 result3 = re.findall('a+',string) #匹配 一个或多个 14 print(result3) 15 result3 = re.findall('a*', string) # 匹配 0 个 或多个 0个 a就是空字符串 16 print(result3) 17 18 result3 = re.findall('a{3}', string) #匹配三个a, 19 print(result3) 20 21 result3 = re.findall('a{3,}', string) #匹配至少3 个a, 22 print(result3)
--------------------------------------------------------------------------------------------------------------------------
['yijingbengpanle']
['yijingbengpanle', 'yijingbengpanle']
[',', ',', ',', '.', '1', '1', '1']
['a', 'a', 'aaaaaaaaaaaaaaaaaaaaaaaaaa']
['', '', '', '', '', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'aaaaaaaaaaaaaaaaaaaaaaaaaa', '', '', '', '']
['aaa', 'aaa', 'aaa', 'aaa', 'aaa', 'aaa', 'aaa', 'aaa']
['aaaaaaaaaaaaaaaaaaaaaaaaaa'
好像还是不够神奇。。。。。。。。。。。。。。。。。。。。。。。。。。。咋办。那是因为只是用了单独的元字符啊。
以下是我瞎写的HTML 伪代码,我们现在使用以上所学将,背诵的课文给提取出来。
1 <!DOCTYPE HTML> 2 <head> 3 4 </head> 5 6 <body> 7 8 <t1>课文背诵</t1> 9 <ul> 10 <li>下雪啦</li> 11 <li>下雪啦</li> 12 <li>雪地里来了一群小画家</li> 13 <li>我忘记啦</li> 14 </ul> 15 16 </body>
1 import re 2 3 string = '''<!DOCTYPE HTML> 4 <head> 5 6 </head> 7 8 <body> 9 <t1>课文背诵</t1> 10 <ul> 11 <li>下雪啦</li> 12 <li>下雪啦</li> 13 <li>雪地里来了一群小画家</li> 14 <li>我忘记啦</li> 15 </ul> 16 </body>''' 17 18 results = re.findall('<li>(.*?)</li>',string, re.S) 19 print(results) 20 for r in results: 21 print(r)
--------------------------------------------------------------------------------------------------------
['下雪啦', '下雪啦', '雪地里来了一群小画家', '我忘记啦']
下雪啦
下雪啦
雪地里来了一群小画家
我忘记
这就是简单的爬虫,简不简单,神不神奇, 一行代码就能得到我们想要的数据。
以上代码findall()中的pattern为较常用的类型,用确定的字符来明确的暗示,我就是要 <li></li>之间的内容,再通过与其他泛匹配,与非贪婪匹配的结合,得到想要的结果。其中?非贪婪的重要性将会在下次探讨。