python3网络爬虫——正则表达式的基本使用
学习指引:视频教程《python3网络爬虫实战》
为了避免学习后短时间内遗忘,让自己随时可以查阅前方自己学过的知识,特意注册csdn博客,方便自己学习时做笔记,也方便随时回顾。也希望自己的学习过程能给同样初学python爬虫的你带来一点指引!由于自己是新手,只有一点点的python基础,所以笔记中难免会有很多纰漏,还望各路大神多多包涵,多多指教,如有不正确的地方,欢迎指出。
在线的正则表达式公式:
http://tool.oschina.net/regex
正则表达式:
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。
re 模块使 Python 语言拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。
简单来说,什么是正则表达式呢?正则表达式就是使用单个字符串来描述和匹配一系列或者匹配某个句法规则的字符串。它用非常精炼的语法,就可以检索文本中我们所需要的字符串。在网页解析的时候,我们通常得到一个网页的所有文本信息,这些信息包括html的各种标签,我们检索标签内文字的方法有很多,一种是标签匹配,比如文本<html><body>hello</body></html>
这样的标签,想要提取hello,那我们可以直接匹配<body>
里的hello文本,但是假如有多个标签怎么办,比如
<html>
<body>
<p>hello</p>
<p>goodbye</p>
</body>
</html>
那这个时候我们要匹配hello就没有一开始那么容易了吧?这里有两个<p>
标签,而且两个<p>
标签里面的文本不同,这个时候,如果使用正则表达式则可以帮上大忙。上面这个例子算是一个引子吧,下面开始学习正则表达式:
先放上一些其他博客的教程:
博主唯心不易的Python 正则表达式入门:
http://www.cnblogs.com/chuxiuhong/p/5885073.html
菜鸟教程的Python3 正则表达式:
http://www.runoob.com/python3/python3-reg-expressions.html
这里面都有非常丰富的实例和语法讲解。
我下面的学习笔记将会摆一些我在练习时候用到的完整代码,大家复制粘贴到idle中可以直接运行,主要是方便各位初学者能够找到实例,方便更好的理解,也避免我在学习之后的以往。
元字符
|
描述
|
\
|
将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\\n”匹配\n。“\n”匹配换行符。序列“\\”匹配“\”而“\(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
|
^
|
匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
|
$
|
匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
|
*
|
匹配前面的子表达式任意次。例如,zo*能匹配“z”,也能匹配“zo”以及“zoo”。*等价于o{0,}
|
+
|
匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
|
?
|
匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。
|
{n}
|
n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
|
{n,}
|
n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
|
{n,m}
|
m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
|
?
|
当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多的匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少的匹配“o”,得到结果 [‘o’, ‘o’, ‘o’, ‘o’]
|
.点
|
匹配除“\r\n”之外的任何单个字符。要匹配包括“\r\n”在内的任何字符,请使用像“[\s\S]”的模式。
|
x|y
|
匹配x或y。例如,“z|food”能匹配“z”或“food”(此处请谨慎)。“[zf]ood”则匹配“zood”或“food”。
|
[xyz]
|
字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
|
[^xyz]
|
负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”。
|
[a-z]
|
字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身.
|
[^a-z]
|
负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
|
\b
|
匹配一个单词边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
|
\B
|
匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
|
\cx
|
匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
|
\d
|
匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
|
\D
|
匹配一个非数字字符。等价于[^0-9]。grep要加上-P,perl正则支持
|
\f
|
匹配一个换页符。等价于\x0c和\cL。
|
\n
|
匹配一个换行符。等价于\x0a和\cJ。
|
\r
|
匹配一个回车符。等价于\x0d和\cM。
|
\s
|
匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
|
\S
|
匹配任何可见字符。等价于[^ \f\n\r\t\v]。
|
\t
|
匹配一个制表符。等价于\x09和\cI。
|
\v
|
匹配一个垂直制表符。等价于\x0b和\cK。
|
\w
|
匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的”单词”字符使用Unicode字符集。
|
\W
|
匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
|
\xn
|
匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。
|
\num
|
匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
|
\n
|
标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
|
\nm
|
标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
|
\nml
|
如果n为八进制数字(0-7),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
|
\un
|
匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。
|
\<
\>
|
匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串”for the wise”中的”the”,但是不能匹配字符串”otherwise”中的”the”。注意:这个元字符不是所有的软件都支持的。 |
( ) | 将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。 |
| | 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配”it belongs to him”和”it belongs to her”,但是不能匹配”it belongs to them.”。注意:这个元字符不是所有的软件都支持的。 |
摘引自百度百科:
https://baike.baidu.com/item/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1700215?fr=aladdin有改动
废话不多说,我们开始码代码吧!
首先要正则表达式的模块是re模块,我们就要调用re模块,用import re
语句就可以调用了。
re.match(pattern, string, flags=0)
re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none
最常规的匹配方法
import re
content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}\s\w\w\s\w\s\w{5}\sDemo$',content)#这里的句子可以说是完全与上面content的字符串匹配了,具体每一个字符的用途在上面表格中已经给出,这里注意的是,开头的^是表示开头要匹配这个字符,末尾的$是表明字符串末尾要匹配这一段,中间的\w{10}是表明匹配任何非单词字符,而且一次性连续匹配十个,那我们就不用输入10个\w了。
print(result)
print(result.group())
print(result.span())
输出结果应该是:
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)
泛匹配
那像刚刚那样匹配的情况用的是比较少的,毕竟匹配要求太精确了,我们很多情况时匹配一种格式,满足这种格式就可以了,下面的匹配是匹配头和尾,中间任意字符都允许的一种匹配方式,主要用到了’.’和’‘,’.’就是表明可以出现任意单个字符,’‘表明前一个符号所代表的字符可以出现任意多次。
import re
content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello.*Demo$',content)
print(result)
print(result.group())
print(result.span())
运行结果:
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)
匹配目标
import re
content = 'Hello 1234567 World_This is a Regex Demo'#注意1234567之间没有空格了
result = re.match('^Hello\s(\d+)\sWorld.*Demo$',content)#注意小括号,有了小括号,那么就会给它分组,想要把1234567提取出来,我们将它分个组,提取group(1)就可以了
print(result)
print(result.group())
print(result.group(1))
print(result.span())
运行结果:
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
Hello 1234567 World_This is a Regex Demo
1234567
(0, 40)
贪婪匹配
import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7
(0, 40)
这里的.*会把所有的符合单个字符的先匹配完再轮到\d执行,相当于贪婪地匹配。
非贪婪匹配
import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello.*?(\d+)\sWorld.*Demo$',content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567
(0, 40)
这里的.?中的?会匹配最少的.,符合非贪婪匹配。会最少的符合.*的单个字符就轮到\d执行,相当于非贪婪匹配。
匹配模式
如果不指定匹配模式的话,它是不会匹配换行符的
import re
content = '''Hello 1234567 World_This is a Regex Demo '''
result = re.match('^He.*?(\d+).*?Demo$',content,re.S)#指定了匹配模式
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
1234567
(0, 41)
re.search
re.search扫描整个字符串并返回第一个成功的匹配
import re
content = 'I am clever Hello 1234567 World_This is a Regex Demo I am clever'
result = re.search('He.*?(\d+).*?Demo',content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(12, 52), match='Hello 1234567 World_This is a Regex Demo'>
1234567
我们来做一个练习
import re
html = '''
<div class="v-hd2">
<a href="/discover/playlist/" class="tit f-ff2 f-tdn">热门推荐</a>
<div class="tab">
<a href="/discover/playlist/?cat=%E5%8D%8E%E8%AF%AD" class="s-fc3">华语</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%B5%81%E8%A1%8C" class="s-fc3">流行</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%91%87%E6%BB%9A" class="s-fc3">摇滚</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%B0%91%E8%B0%A3" class="s-fc3">民谣</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E7%94%B5%E5%AD%90" class="s-fc3">电子</a>
</div>
<span class="more"><a href="/discover/playlist/" class="s-fc3">更多</a><i class="cor s-bg s-bg-6"> </i></span>
</div>
'''
results = re.findall('<a.*?playlist.*?cat.*?class=\"(.*?)\"\>(.*?)</a>',html,re.S)
print(results)
print(type(results))
for result in results:
print(result)
输出结果为
[('s-fc3', '华语'), ('s-fc3', '流行'), ('s-fc3', '摇滚'), ('s-fc3', '民谣'), ('s-fc3', '电子')]
<class 'list'>
('s-fc3', '华语')
('s-fc3', '流行')
('s-fc3', '摇滚')
('s-fc3', '民谣')
('s-fc3', '电子')
re.sub
替换字符串中每一个匹配的子串后返回替换后的字符串
re.sub(pattern, repl, string, count=0, flags=0)
直接上例子:
import re
html = '''
<div class="v-hd2">
<a href="/discover/playlist/" class="tit f-ff2 f-tdn">热门推荐</a>
<div class="tab">
<a href="/discover/playlist/?cat=%E5%8D%8E%E8%AF%AD" class="s-fc3">华语</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%B5%81%E8%A1%8C" class="s-fc3">流行</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%91%87%E6%BB%9A" class="s-fc3">摇滚</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E6%B0%91%E8%B0%A3" class="s-fc3">民谣</a>
<span class="line">|</span>
<a href="/discover/playlist/?cat=%E7%94%B5%E5%AD%90" class="s-fc3">电子</a>
</div>
<span class="more"><a href="/discover/playlist/" class="s-fc3">更多</a><i class="cor s-bg s-bg-6"> </i></span>
</div>
'''
content = re.sub('<a.*?>|</a>','',html)#将所有的a标签去掉再提取,就方便很多
print(content)
result = re.findall('</span>(.*?)<',content,re.S)#提取内容,注意有换行符,要加上re.S print(result) for r in result: print(r.strip())#这个strip函数可以去掉空白字符输出,打印出的内容就没有换行符和空格啦
<div class="v-hd2">
热门推荐
<div class="tab">
华语
<span class="line">|</span>
流行
<span class="line">|</span>
摇滚
<span class="line">|</span>
民谣
<span class="line">|</span>
电子
</div>
<span class="more">更多<i class="cor s-bg s-bg-6"> </i></span>
</div>
['\n 流行\n ', '\n 摇滚\n ', '\n 民谣\n ', '\n 电子\n ', '\n ']
流行
摇滚
民谣
电子
re.compile
re.compile(pattern, flags=0)
将正则表达式串编译成正则对象,以便于复用该匹配模式
import re
content = '''Hello 1234567 World_This is a Regex Demo '''
pattern = re.compile('^He.*?(\d+).*?Demo$',re.S)#将整个匹配模式的方式编译成一个对象,存到pattern变量中,下次随时可以调用
result = re.match(pattern,content)
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
1234567
(0, 41)
大概知识点就是这么多啦,大家学习愉快