爬虫必学知识之正则表达式下篇

895 阅读6分钟

这是日常学python的第13篇原创文章

继上篇文章说了正则表达式的简单用法,那今天我们就继续说一下正则表达式的复杂的用法。好了,废话不多说,直接进入正题。

进入正题

情景:当你想要匹配一个qq号,qq号码长度为5-10位,那根据上篇文章的说法,很容易就可以想到该正则:

[0-9]{5,10}


这样是可以的,但是当你匹配一个长度大于10的号码时就会出错,这时就会去该字符串的前10个数字出来,如下:

import re
a='221753259265'
r=re.findall('[0-9]{5,10}',a)#明显当查找的字符串长度大于8位时就会出错,只会截取前一部分长度
print(r)
# 结果
['2217532592']

这样的话你就会得到一个错误的qq号码。


这时就需要引入边界匹配了:

  • ^:这个是从左边开始匹配,规定左边的首个字符

  • $:这个是从右边开始匹配,规定右边的首个字母

现在再写个匹配qq号码的正则

r=re.findall('^[0-9]{5,10}$',a)#这个表示从左边起为5-10的数字长度,右边也是一样
print('第一个匹配结果:',r)
a = '2217532592'
r=re.findall('^[0-9]{5,10}$',a)
print('第二个匹配结果:',r)
# 结果
第一个匹配结果: []
第二个匹配结果: ['2217532592']

这样就可以匹配到了,是不是很神奇?

:前面我们有用 [ ] 来匹配,中括号里面表示的是或关系,而这里的组表示的是并关系,并且用小括号括起来 ( )

比如:重复 python 字样三次

import re
a='pythonpythonpythonjakjpythonpythonsdjjpythonpythonpythonsd'
r=re.findall('(python){3}',a)
print(r)
# 结果
['python', 'python']

这里的结果不是返回三个python,而是返回这个组,当符合一次就会将此组添加到返回列表中一次。

这个组还挺好用的,再看下这个需求:获取下列英文中的life和python之间的内容。

a='life is short,i use python'
r=re.findall('life(.*)python',a,re.S)
print(r) # 这样获取的就是组内的内容
# 结果
[' is short,i use ']

这个组还常用,因为在我们经常在用正则来解析html元素时,经常需要获取两个标签之间的内容,标签是确定的,标签内容不确定,就可以用这个了。如下这个html元素:

<strong><a href="#py2">python进阶 </a>
           <a href="#python3">python入门 </a>
       <a href="#vce">vce解决方法 </a>
       <a href="demo06.html#new" target="_blank">百度 </a>
       <a href="mailto: 2217532592@qq.com">反馈意见</a>
       <a href="img/1.jpg">下载图片 </a>
   </strong>

这样就可以用组来获取a标签的内容了:<a .*?>(.*?)</a>。?表示非贪婪哦!


re.findall(pattern,string,flags):这个方法的前两个参数对你们来说都很熟悉了,第一个参数为正则表达式,第二个参数为要进行匹配的字符串,而第三个可选参数为匹配模式,有如下几种匹配模式:

  • re.I(re.IGNORECASE) :使匹配对大小写不敏感

  • re.L(re.LOCAL):做本地化识别(locale-aware)匹配

  • re.M(re.MULTILINE):多行匹配,影响 ^ 和 $

  • re.S(re.DOTALL):使 . 匹配包括换行在内的所有字符(这个常用)

  • re.U(re.UNICODE):根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.

  • re.X(re.VERBOSE):该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解


当需要写多个匹配模式时,可以用 | 分隔每个模式

代码如下:

a='Java12Python89'
r=re.findall('python',a,re.I)
print(r)
a='hsjhj h123jfkksf hajkGH\nkj fjfk'
r=re.findall('.',a,re.I|re.S)
print(r)
# 结果
['Python']
['h', 's', 'j', 'h', 'j', ' ', 'h', '1', '2', '3', 'j', 'f', 'k', 'k', 's', 'f', ' ', 'h', 'a', 'j', 'k', 'G', 'H', '\n', 'k', 'j', ' ', 'f', 'j', 'f', 'k']


正则除了可以用来检索字符串,还可以用来替换字符串,常见的可以用来替换那些文本中的空格,制表符和回车等,这些都是用一个正则就可以搞定的了。


python中用这个方法来进行正则替换

re.sub(pattern, repl, string, count=0, flags=0) 

  • pattern :正则表达式

  • repl :替换后的字符串,可为函数

  • string :要进行替换的字符串

  • count :替换的次数,顺序为 从左往右,默认值为0,表示无限次。

  • falgs : 匹配模式,和findall()差不多

代码如下:

import re
a='skjC#ksjfc#jkdsc#'
r=re.sub('c#','gg',a)#返回值是替换后的字符串
print(r)
print(a)
r=re.sub('c#','gg',a,1) # 这个加了替换次数
print(r)
r=re.sub('c#','gg',a,1,re.I) # 加了匹配模式,忽视大小写
print(r)
# 结果
skjC#ksjfggjkdsgg
skjC#ksjfggjkdsc#
skjggksjfc#jkdsc#


我们试试第二个参数为函数的情况

def convert(value):#他是把对象传进去这个参数
   print(value)
   #可以通过group()方法来获取内容
   return '!!'+value.group()+"!!"
r=re.sub('c#',convert,a,flags=re.I)#接收个参数后,更改后的内容为他的返回值
print(r)
# 结果
<_sre.SRE_Match object; span=(3, 5), match='C#'>
<_sre.SRE_Match object; span=(9, 11), match='c#'>
<_sre.SRE_Match object; span=(15, 17), match='c#'>
skj!!C#!!ksjf!!c#!!jkds!!c#!!


这个第二个参数为convert函数,里面的.group() 方法是获取匹配后的字符串的值,所以我们就可以根据匹配后的字符串来进行相对应的替换内容,比如这个简单的小需求:

把字符串中的数字大于50的改为99,小于的就改为11。

a='ds+45sd78asd12568asd45asd74ew+9ddf12sd45'
def func(value):
   if int(value.group())>50:
       return '99'
   else:
       return '11'

r=re.sub('\d{1,2}',func,a)
print(r)
# 结果
ds+11sd99asd119911asd11asd99ew+11ddf11sd11


另谈两个函数

  • re.match(pattern,string,flags) :这个是从字符串的首个字母开始匹配,若首个字母不符合,就会返回None, 反之返回一个 Match对象。而他只会匹配第一个结果,不会返回所有符合结果的内容。参数内容与findall()方法一样。

  • re.search(pattern,string,flags) :这个与match方法差不多,不过不是从首字符开始匹配,也是只返回一个正确的匹配内容。


代码:

import re
a='pythonphpjavacphp'
r=re.match('php',a)#这个一开始没有就返回None
print(r)
r=re.search('php',a)#这个搜索到之后就返回一个对象
#返回的对象可以通过group()方法来获取他的内容
print(r)
# 获取匹配内容
print(r.group())
# 结果
None
<_sre.SRE_Match object; span=(6, 9), match='php'>
php


这两个函数返回的内容的几个属性:

  • group() :获取匹配的内容

  • statr() :获取到匹配字符的起始位置

  • end() :获取匹配到字符的结束位置

  • span() :获取匹配到字符的起始和结束位置,元组形式返回。


前面提到组的概念,试下这两个方法的组的用法:

import re
#获取life和python之间的内容
a='life is short,i use python'
r=re.search('life(.*)python',a,re.S)#用小括号的就是一组
print(r.group(1))#这个下标1就是对应的中间部分

#也可以获取中间的两部分
a='javawoshipythonjunephp'
r=re.search('java(.*)python(.*)php',a)#两个小括号就是分成了两组
print(r.group(1),r.group(2))#分别打印第一第二组
print(r.groups())#这个获取所有分组信息
# 结果
is short,i use
woshi june
('woshi', 'june')

上面的代码注释已经很清楚了,还有个group()方法是获取整个正则匹配的内容,不按分组。match()方法也一样,就不演示了。

最后一个问题:怎样拆分含有多种分隔符的字符串?

比如:kfs;hsji'fhsikf*bhsfk=jsf/shj。要将不属于字母的都去掉,你是不是会想到用字符串的循环,然后再一个一个分割出来?我告诉你,学了正则之后,再也不用这么麻烦了。re库里面有个split()方法,如下:


re.split(pattern, string, maxsplit=0),参数看名字应该就能知道。直接一行代码进行分割:

a = 'kfs;hsjifhsikf*bhsfk=jsf/shj'
r = re.split('[;*=/]', a)
print(r)
# 结果
['kfs', 'hsjifhsikf', 'bhsfk', 'jsf', 'shj']

是不是很完美?所以说正则必须得学!


END

这个正则复杂点的已经说完了,还有些进阶的,不过暂时没有用到,就不打算说了,需要的可以去百度看看哈!


留个小练习证明自己正则学得好怎么样:

1.kevintian126@126.com 

2. 1136667341@qq.com 

3. meiya@cn-meiya.com 

4. wq901200@hotmail.com 

5. meiyahr@163.com

6. meiyuan@0757info.com 

7. chingpeplo@sina.com 

8. tony@erene.com.com

9. melodylu@buynow.com

用正则把上面的@与com之间的内容匹配出来,可以把你的答案写在留言区上,过两天在留言区公布答案哈!

上述文章如有错误欢迎在留言区指出,如果这篇文章对你有用,点个赞,转个发如何?


MORE延伸阅读

◐◑爬虫必学知识之正则表达式上篇

◐◑ python爬虫常用库之requests详解

◐◑ python使用requests+re简单入门爬虫


1803114089.png


日常学python

代码不止bug,还有美和乐趣