对着爬虫网页HTML学习Python正则表达式re
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 = re.compile(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 = re.compile(pattern)
# 匹配regex对象,string指代匹配的文本内容
mo = phoneNum.search(string)
等价于
mo = phoneNum.search(pattern, string)
如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。
不过,我们发现其实在待匹配的文本内容中出现了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="//www.ziroom.com/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 = re.compile(r'(Ha){3,5}')
In [17]: mo1 = greedyHaRegex.search('HaHaHaHaHa')
In [18]: mo1.group()
Out[18]: 'HaHaHaHaHa'
In [19]: greedyHaRegex = re.compile(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.字符类型
| 模式 | 描述 |
|---|---|
| ^ | 匹配字符串的开头 |
| $ | 匹配字符串的末尾。 |
| . | 匹配任意字符,除了换行符,当.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。 |
| [...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
| * | 匹配0个或多个的表达式。 |
| + | 匹配1个或多个的表达式。 |
| ? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
| { n} | 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o。 |
| { n,} | 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。 |
| { n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
| a|b | 匹配a或b |
| () | 对正则表达式分组并记住匹配的文本 |
| (?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。 |
| (?-imx) | 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。 |
| (?: ) | 类似 (...), 但是不表示一个组 |
| (?imx: ) | 在括号中使用i, m, 或 x 可选标志 |
| (?-imx: ) | 在括号中不使用i, m, 或 x 可选标志 |
| (?#...) | 注释. |
| (?= ) | 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。 |
| (?! ) | 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功 |
| (?> ) | 匹配的独立模式,省去回溯。 |
| \w | 匹配字母数字及下划线 |
| \W | 匹配非字母数字及下划线 |
| \s | 匹配任意空白字符,等价于 [ \t\n\r\f]。 |
| \S | 匹配任意非空字符 |
| \d | 匹配任意数字,等价于 [0-9]. |
| \D | 匹配任意非数字 |
| \A | 匹配字符串开始 |
| \Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。 |
| \z | 匹配字符串结束 |
| \G | 匹配最后匹配完成的位置。 |
| \b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, | 匹配一个换行符,匹配一个制表符 |
| \1...\9 | 匹配第n个分组的内容。 |
| \10 | 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。 |
| 实例 | 描述 |
|---|---|
| [Pp]ython | 匹配 "Python" 或 "python" |
| rub[ye] | 匹配 "ruby" 或 "rube" |
| [aeiou] | 匹配中括号内的任意一个字母 |
| [0-9] | 匹配任何数字。类似于 [0123456789] |
| [a-z] | 匹配任何小写字母 |
| [A-Z] | 匹配任何大写字母 |
| [a-zA-Z0-9] | 匹配任何字母及数字 |
| [^aeiou] | 除了aeiou字母以外的所有字符 |
| [^0-9] | 匹配除了数字外的字符 |
正则匹配模式表
| 标志 | 含义 |
|---|---|
| re.S(DOTALL) | 使.匹配包括换行在内的所有字符 |
| re.I(IGNORECASE) | 使匹配对大小写不敏感 |
| re.L(LOCALE) | 做本地化识别(locale-aware)匹配,法语等 |
| re.M(MULTILINE) | 多行匹配,影响^和$ |
| re.X(VERBOSE) | 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解 |
| re.U | 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B |
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层'
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理
想要获取更多Python学习资料可以加
QQ:2955637827私聊
或加Q群630390733
大家一起来学习讨论吧!
对着爬虫网页HTML学习Python正则表达式re的更多相关文章
- 【Python】【爬虫】如何学习Python爬虫?
如何学习Python爬虫[入门篇]? 路人甲 1 年前 想写这么一篇文章,但是知乎社区爬虫大神很多,光是整理他们的答案就够我这篇文章的内容了.对于我个人来说我更喜欢那种非常实用的教程,这种教程对于想直 ...
- Python正则表达式使用小记
最近做Python课实验发现正则表达式和它在py中的的标准库re有很多能多琢磨一下的点,遂决定写成一篇小记,以后想复习能再来看看. 名词 因为不同文献书籍对正则表达式的描述有差别,我在这里列出一下我已 ...
- Python正则表达式学习摘要及资料
摘要 在正则表达式中,如果直接给出字符,就是精确匹配. {m,n}? 对于前一个字符重复 m 到 n 次,并且取尽可能少的情况 在字符串'aaaaaa'中,a{2,4} 会匹配 4 个 a,但 a{2 ...
- Python 正则表达式 (python网络爬虫)
昨天 2018 年 01 月 31 日,农历腊月十五日.20:00 左右,152 年一遇的月全食.血月.蓝月将今晚呈现空中,虽然没有看到蓝月亮,血月.月全食也是勉强可以了,还是可以想像一下一瓶蓝月亮洗 ...
- Python 正则表达式学习
摘要 在正则表达式中,如果直接给出字符,就是精确匹配. {m,n}? 对于前一个字符重复 m到 n 次,并且取尽可能少的情况 在字符串'aaaaaa'中,a{2,4} 会匹配 4 个 a,但 a{2, ...
- Python 爬虫介绍,什么是爬虫,如何学习爬虫?
作为程序员,相信大家对“爬虫”这个词并不陌生,身边常常会有人提这个词,在不了解它的人眼中,会觉得这个技术很高端很神秘.不用着急,我们的爬虫系列就是带你去揭开它的神秘面纱,探寻它真实的面目. 爬虫是 ...
- python爬虫之Scrapy学习
在爬虫的路上,学习scrapy是一个必不可少的环节.也许有好多朋友此时此刻也正在接触并学习scrapy,那么很好,我们一起学习.开始接触scrapy的朋友可能会有些疑惑,毕竟是一个框架,上来不知从何学 ...
- Python 爬虫如何入门学习?
"入门"是良好的动机,但是可能作用缓慢.如果你手里或者脑子里有一个项目,那么实践起来你会被目标驱动,而不会像学习模块一样慢慢学习. 另外如果说知识体系里的每一个知识点是图里的点,依 ...
- 第14.1节 通过Python爬取网页的学习步骤
如果要从一个互联网前端开发的小白,学习爬虫开发,结合自己的经验老猿认为爬虫学习之路应该是这样的: 一. 了解HTML语言及css知识 这方面的知识请大家通过w3school 去学习,老猿对于html总 ...
随机推荐
- yii2.0 删除文件夹
/** * 删除文件缓存 */public function actionDelfilecache(){ $cachePath = Yii::getAlias('@app/runtime/cache' ...
- 地图上显示点在点上标注当前点的id
HTML: <div class="form-group field-company-state"> <div style="width:1000px; ...
- CF980C Posterized
先来吐槽一下这个 sb 翻译,根本就没做过题吧-- 大概就是让你给值域分成连续的几组,每组大小不能超过 \(k\),然后将序列中的值全部替换成其组内的最小值,要使得序列的字典序最小. 因为是字典序,所 ...
- Linux 学习笔记03丨Linux文件系统、文件基本属性、目录处理及文件查看
Chapter 2. 文件系统 2.1 Linux 系统目录结构 命令窗口下输入命令: $ ls /,能够看到根目录下的全部目录及文件 树状目录结构为: 最顶级的目录: / :根目录 / 是根目录,~ ...
- Kubernetes日志系统新贵Loki-Stack
Loki简介 Grafana Loki是可以组成功能齐全的日志记录堆栈的一组组件. 与其他日志记录系统不同,Loki是基于仅索引有关日志的元数据的想法而构建的:标签(就像Prometheus标签一样) ...
- 基于java实现的简单网页日历功能,有兴趣得可以把它转换到前端实现
之前做项目的时候,因为要用到不同日期显示不同的内容,就自己做了一个日期的显示和选择功能,今天抽空把以前的代码理了一下,顺便就把之前做的日期功能给拿出来回顾一下,大家可以提点意见,帮忙完善下设计.先上一 ...
- 微软面试题: LeetCode 151. 翻转字符串里的单词 出现次数:6
题目描述: 给定一个字符串,逐个翻转字符串中的每个单词. 说明: 无空格字符构成一个 单词 .输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括.如果两个单词间有多余的空格,将反转后 ...
- C#9.0新特性之四:顶级程序语句(Top-Level Programs)
1 背景与动机 通常,如果只想用C#在控制台上打印一行"Hello World!",这可不是Console.WriteLine("Hello World!"); ...
- 九. Vuex详解
1. 理解Vuex 1.1 Vuex功能 官方解释 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用 集中式存储 管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...
- 第15.37节 PyQt(Python+Qt)入门学习:containers容器类部件QMdiArea多文档界面部件详解及编程开发案例
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 一.引言 老猿在前期学习PyQt相关知识时,对每个组件的属性及方法都研 ...