700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 对着爬虫网页HTML学习Python正则表达式

对着爬虫网页HTML学习Python正则表达式

时间:2023-08-10 14:23:52

相关推荐

对着爬虫网页HTML学习Python正则表达式

文章目录

1.正则表达式初探2.用正则表达式匹配更多模式2.1.利用括号()进行分组2.2.利用管道|匹配多个分组2.3.用问号?实现可选匹配2.4.用星号*实现0次或多次2.5.用加号+实现1次或多次2.6.用花括号{}匹配特定次数3.贪心和非贪心匹配4.字符类型5.split()函数

1.正则表达式初探

用比较经典的例子,查找一段文本中的手机号码。比如对于文本“我现在用的电话是188-8888-8888,之前那个186-6666-6666已经不用了”,我们想获取其中的手机号码信息,用正则表达式可以这么做呢?

正则表达式,简称为 regex,是文本模式的描述方法。例如,\d 是一个正则表达式,表示一位数字字符,即任何一位 0 到 9 的数字。 Python 使用正则表达式\d\d\d-\d\d\d\d-\d\d\d\d,来匹配3 个数字、一个短横线、4 个数字、一个短横线、4 个数字。所有其他字符串都不能匹配\d\d\d-\d\d\d\d-\d\d\d\d 正则表达式。

在一个表达式后加上花括号包围的 3({3}),就是说,“匹配这个模式 3 次”。所以较短的正则表达式\d{3}-\d{4}-\d{4},也可以匹配正确的手机号码格式。

引入正则表达式库re,该库是python自带的哈。

In [1]: import re...: # 创建一个regex模式对象...: phoneNum = pile(r'\d\d\d-\d\d\d\d-\d\d\d\d')...: # 匹配regex对象...: mo = phoneNum.search('我现在用的电话是188-8888-8888,之前那个186-6666-6666已经不用了')In [2]: mo.group()Out[2]: '188-8888-8888'

其实,以下是等价的

# 创建一个regex模式对象,pattern指待匹配的正则表达式phoneNum = pile(pattern)# 匹配regex对象,string指代匹配的文本内容mo = phoneNum.search(string)

等价于

mo = phoneNum.search(pattern, string)

如果需要多次使用这个正则表达式的话,使用 pile() 和保存这个正则对象以便复用,可以让程序更加高效。

不过,我们发现其实在待匹配的文本内容中出现了2个手机号码,但是re.search()只返回了第一个匹配成功的文本。如何可以获取全部匹配成功的项呢,咱们可以使用**re.findall()**来进行操作,其返回的结果是由所有匹配组成的列表。

In [3]: re.findall(r'\d{3}-\d{4}-\d{4}', '我现在用的电话是188-8888-8888,之前那个186-6666-6666已经不用了')Out[3]: ['188-8888-8888', '186-6666-6666']

2.用正则表达式匹配更多模式

在实际解析网页HTML文本的时候,我们可能需要取匹配中某个部分分组文本、或者需要选择性匹配多个文本、又或者对某些字符或者分组需要匹配0/1次或者多次等等。

以下是待解析的某待租房间信息

info= '''<h5 class="title sign"><a href="///x/712447913.html" target="_blank" style="line-height: 0.9em;">合租·DBC加州小镇C区4居室-南卧</a></h5><div class="desc"><div>23.3㎡ | 5/15层</div><div class="location">小区距高楼金站步行约178米</div></div><div class="price "><span class="rmb">¥</span><span class="num">188</span><span class="unit">/天</span></div><div class="tag"><span>可短租</span><span>离地铁近</span><span>米苏4.0</span></div>'''

对于这种文本,由于存在很多空白字符类如换行、空格等等,我需要先用re.sub()进行简单的清洗。

info = re.sub(r'\s','',info) # \s 匹配任意空白字符

2.1.利用括号()进行分组

比如,我需要匹配子字符中的房间租金信息,因租金为数字但是还有别的一些信息也是数字(如房间大小等),因此我们在匹配的时候需要代入前后一些字符做唯一匹配,但是实际只需要对应的数字文本内容,因此需要进行分组。

<spanclass="num">188</span>

比如以上,我们想要获得价格188,可以使用**(\d{3})**进行匹配。

注意:这里是的匹配模式是4位数字的精确匹配,在实际的操作中价格可能存在不确定的位置甚至带有小数,我们需要用到更复杂的匹配模式,具体见后续讲解。

In [4]: re.findall(r'<spanclass="num">(\d{3})</span>', info)Out[4]: ['188']

2.2.利用管道|匹配多个分组

以示例的info文本,在爬虫过程中其价格有时候类型是天或者月,我们匹配的可能就是诸多表达式中的一个,此时可以使用 | 进行操作。正则表达式r“天|月”即可匹配 天 或者 月。

<spanclass="unit">/天</span># 或者<spanclass="unit">/月</span>

我们采用正则表达式 r“天|月” 可实现匹配。

In [5]: re.findall(r'<spanclass="unit">/(月|天)</span>', info)Out[5]: ['天']In [6]: s = '<spanclass="unit">/月</span>'In [7]: re.findall(r'<spanclass="unit">/(月|天)</span>', s)Out[7]: ['月']

2.3.用问号?实现可选匹配

对于房间的面积,有的可能是整数有的可能是小数,因此小数点及小数点后的数字其实是可选项,为了更好的匹配这个面积文本,我们需要用到问号?。字符?表示它前面的分组在这个模式中是可选的

<div>23.3㎡|5/15层</div># 或者<div>23㎡|5/15层</div>

我们可以用 r’(\d{2}.?\d?)‘来进行匹配,如果为了在整个html里找且怕存在重复,可以用r’(\d{2}.?\d?)|5/15层’。这里需要注意我们在 | 前面加了 转义字符 \,区别于 | 本身,否则可能无法得出正确结果。

In [8]: re.findall(r'<div>(\d{2}\.?\d?)㎡\|5/15层</div>',info)Out[8]: ['23.3']In [9]: re.findall(r'<div>(\d{2}\.?\d?)㎡\|5/15层</div>','<div>23㎡|5/15层</div>')Out[9]: ['23']

2.4.用星号*实现0次或多次

对于楼层信息来说,我们要获取其楼层和楼高,有的可能有楼层信息但是有的可能没有,楼层和楼高可能是个位数或者十位数。这种情况下,我们可以使用星号进行匹配。字符*表示它前面的分组在这个模式中是出现0次或者多次。

<div>23.3㎡|5/15层</div># 或者<div>23㎡|9层</div>

由于楼高是一定存在的,而楼层不一定存在,因为我们可以用r’(\d*)/*(\d+)'来进行匹配,注意字符+代表至少一次,详见后续说明。

In [10]: re.findall(r'<div>\d{2}\.?\d?㎡\|(\d*)/*(\d+)层</div>', info)Out[10]: [('5', '15')]In [11]: re.findall(r'<div>\d{2}\.?\d?㎡\|(\d*)/*(\d+)层</div>', '<div>23㎡|9层</div>')Out[11]: [('', '9')]

2.5.用加号+实现1次或多次

我们在2.4中其实看到了字符 +的使用场景,其代表的就是它前面的分组在这个模式中是出现1次或者多次

<spanclass="num">188</span># 或者<spanclass="num">1888</span>

我们回到 2.1.中 匹配租金的案例,其实对于租金来说除了3位数之外,租金金额其实是一个大于0的值,也就是至少出现1次数字,因此我们可以用 r’(\d+)’ 来匹配。

In [12]: re.findall(r'<spanclass="num">(\d+)</span>', info)Out[12]: ['188']In [13]: re.findall(r'<spanclass="num">(\d+)</span>', '<spanclass="num">1888</span>')Out[13]: ['1888']

2.6.用花括号{}匹配特定次数

再以2.3.中的房间面积为例,我们认为房间面积不可能超过3位数、最低1位数 为正常值。

如果想要一个分组重复特定次数,就在正则表达式中该分组的后面,跟上花括号包围的数字。例如,正则表达式(Ha){3}将匹配字符串’HaHaHa’,但不会匹配’HaHa’,因为后者只重复了(Ha)分组两次。

除了一个数字,还可以指定一个范围,即在花括号中写下一个最小值、一个逗号和一个最大值。例如,正则表达式(Ha){3,5}将匹配’HaHaHa’、 ‘HaHaHaHa’和’HaHaHaHaHa’。

也可以不写花括号中的第一个或第二个数字, 不限定最小值或最大值。例如,(Ha){3,}将匹配 3 次或更多次实例, (Ha){,5}将匹配 0 到 5 次实例。

不过,在使用过程中一定要慎重,同样的分组在不同的匹配模式可能带来不同的结果。

In [14]: re.findall(r'(\d{2,3})㎡','<div>3456㎡|5/15层</div>')Out[14]: ['456']In [15]: re.findall(r'<div>(\d{2,3})㎡','<div>3456㎡|5/15层</div>')Out[15]: []

3.贪心和非贪心匹配

Python 的正则表达式默认是“贪心” 的,这表示在有二义的情况下,它们会尽可能匹配最长的字符串。

在表达式后面加上符号?,即为非贪心匹配。

In [16]: greedyHaRegex = pile(r'(Ha){3,5}')In [17]: mo1 = greedyHaRegex.search('HaHaHaHaHa')In [18]: mo1.group()Out[18]: 'HaHaHaHaHa'In [19]: greedyHaRegex = pile(r'(Ha){3,5}?')In [20]: mo2 = greedyHaRegex.search('HaHaHaHaHa')In [21]: mo2.group()Out[21]: 'HaHaHa'In [22]: re.findall(r'(Ha){3,5}?','HaHaHaHaHa')Out[22]: ['Ha']In [23]: re.findall(r'(Ha){3,5}','HaHaHaHaHa')Out[23]: ['Ha']In [24]: re.findall(r'((Ha){3,5})','HaHaHaHaHa')Out[24]: [('HaHaHaHaHa', 'Ha')]In [25]: re.findall(r'((Ha){3,5}?)','HaHaHaHaHa')Out[25]: [('HaHaHa', 'Ha')]

4.字符类型

正则匹配模式表

5.split()函数

根据正则匹配分割字符串,返回分割后的一个列表

split(pattern, string, maxsplit=0, flags=0)

pattern: 正则模型

string : 要匹配的字符串

maxsplit:指定分割个数

flags : 匹配模式

当我们获取了全部房源信息后,需要对一些信息进行二次解析,比如房屋信息的解析。

In [26]: # 房屋信息解析...: s1 = '合租·李村东里3居室-北卧'...: s2 = '合租·强佑·府学上院4居室-北卧'...: s3 = '整租·铁二区1室1厅-北'...: s4 = '整租·厂甸11号院1室1厅-东'...: s5 = '整租·牛街182室1厅-西'In [27]: re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s1)Out[27]: ['', '合租', '李村东里', '3居室', '北卧', '']In [28]: re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s2)Out[28]: ['', '合租', '强佑·府学上院', '4居室', '北卧', '']In [29]: re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s3)Out[29]: ['', '整租', '铁二区', '1室1厅', '北', '']In [30]: re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s4)Out[30]: ['', '整租', '厂甸11号院', '1室1厅', '东', '']In [31]: re.split(r'(\S*?)·(.*)(\d居*室.*)-(.*)',s5)Out[31]: ['', '整租', '牛街18', '2室1厅', '西', '']

大家可以尝试更多种正则表达式匹配规则,比如能把前后的空字符串去掉的等等。

如果我们要解析出 房间面积、楼层和楼高信息,观测数据发现存在以下3种情况,大家觉得怎么写正则表达式能实现呢?

# 房间信息解析# 我们在数据处理中发现存在异常数据(楼层如 7层 或 -1/5层)s1 = '87.26㎡|11/29层's2 = '87㎡|7层's3 = '8.6㎡|-1/5层'

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。