有了前面的基础后,大家现在应该可以顺利地得到一个网页源码字符串,那么怎么提取我们想要的信息呢?在这里,给大家介绍的是正则表达式!
什么是正则表达式
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
比如,我们想要提取'Stay hungry, 123 stay foolish!'
中的数字部分,可以使用(\d+)
这个式子来表达。
正则表达式入门
上图来自:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
仔细研究一下上图,相信大家已经开始有点明白正则表达式是怎么回事了,如果想要更详细的学习资料,推荐:
re模块
re
模块用于在字符串中执行正则表达式匹配和替换。下面,我们就来了解一下re
模块的一些内容吧!
函数
以下函数用于执行模式匹配和替换。
re.compile(pattern, flags=0)
将正则表达式模式字符串编译为正则表达式对象。此对象可以作为模式参数传递给随后的所有函数。该对象还提供了许多方法,稍后将介绍。flags
是以下标志的按位OR(|
操作符)结果。
标志 | 描述 |
---|---|
re.A 或re.ASCII | 执行仅8位ASCII字符匹配 |
re.I 或re.IGNORECASE | 执行不区分大小写的匹配 |
re.L 或re.LOCALE | 为\w ,\W ,\b 和\B 使用地区设置 |
re.M 或re.MULTILINE | 将^ 和$ 应用于包括整个字符串的开始和结尾的每一行(在正常情况下,^ 或$ 仅适用于整个字符串的开始和结尾) |
re.S 或re.DOTALL | 使用(. )字符匹配所有字符,包括换行符 |
re.X 或re.VERBOSE | 忽略模式字符串中未转义的空格和注释 |
re.escape(pattern)
返回一个字符串,其中所有非字母数字字符都带有反斜杠。
举个例子:
>>> print(re.escape('python.exe'))
python\.exe
re.findall(pattern, string, flags=0)
返回string
中与pattern
匹配的所有未重叠的值,包括空匹配值。如果模式包含分组,将返回与分组匹配的文本列表。如果使用了不止一个分组,那么列表中的每项都是一个元组,包含每个分组的文本。flags
的含义与re.compile()
相同。
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r'\w+ly',text)
['carefully', 'quickly']
re.finditer(pattern, string, flags=0)
与re.findall()
含义相同,但返回一个迭代器对象。迭代器返回类型为Match
的项。
re.match(pattern, string, flags=0)
检查string
的开头是否有字符与pattern
匹配。如果成功,则返回Match
;否则返回None
。flags
的含义与re.compile()
相同。
re.search(pattern, string, flags=0)
在string
中搜索pattern
的第一个匹配值。flags
的含义与re.compile()
相同。如果成功,则返回Match
;如果未找到匹配值,则返回None
。
re.split(pattern, string, maxsplit=0, flags=0)
根据pattern
出现的位置拆分string
。则返回字符串列表,其中包括与模式中任何分组匹配的文本。maxsplit
是执行拆分的最高次数。默认情况下,将执行所有的拆分。
re.sub(pattern, repl, string, count=0, flags=0)
使用替换值repl
替换string
中最左侧的、未重叠的pattern
的出现位置。repl
可以是字符串或函数。如果它是一个函数,则使用Match
调用它,并返回替换字符串。如果repl
是一个字符串,则使用反向引用(如\6
)来引用模式中的分组。序列\g<name>
用于引用给定名称的分组。count
是执行替换的最高次数。默认情况下,将替换所有出现的位置。
re.subn(pattern, repl, string, count=0, flags=0)
与re.sub()
相同,但返回一个元组,其中包含新字符串和替换次数。
正则表达式对象
由re.compile()
函数创建的经过编译的正则表达式对象r
具有以下方法和属性。
r.flags
在编译正则表达式时使用flags
参数,如果没有指定标志则使用0。
r.groupindex
一个字典,将r'(?P<id>)'
定义的符号分组名称映射到分组编号。
r.pattern
一个模式字符串,正则表达式从它编译而来。
r.findall(string[, pos[, endpos]])
类似于re.findall()
函数。pos
和endpos
指定搜索的开始和结束位置。
r.finditer(string[, pos[, endpos]])
类似于re.finditer()
函数。pos
和endpos
指定搜索的开始和结束位置。
r.match(string[, pos[, endpos]])
检查在string
的开头是否有匹配的字符。pos
和endpos
指定要搜索的string
范围。如果找到匹配值,则返回Match
;否则返回None
。
r.search(string[, pos[, endpos]])
在string
中搜索匹配值。pos
和endpos
指定搜索的开始和结束位置。如果找到匹配值,则返回Match
;否则返回None
。
r.split(string, maxsplit=0) # 类似于于r.split()函数
r.subn(repl, string, count=0) # 等效于re.subn()函数
r.sub(repl, string, count=0) # 等效于re.sub()函数
匹配对象
re.search()
和 re.match()
返回的Match
对象实例包含关于分组内容的信息,以及匹配值的位置数据。Match
实例m
具有以下方法和属性。
m.expand(template)
返回一个字符串,该字符串可通过在字符串template
上置换正则表达式反斜杠来获得。
m.group([group1, ...])
返回匹配值的一个或多个子分组。其中的参数指定分组编号或分组名称。如果未给定分组名称,则将返回整个匹配值;如果仅给定了一个分组,将返回一个字符串,其中包含与该分组匹配的文本;否则返回一个元组,其中包含与所请求的每个分组匹配的文本。如果给定的分组编号或名称无效,将抛出IndexError
。
m.groups(default=None)
返回一个元组,其中包含与模式中所有分组匹配的文本。default
是针对未包含在匹配值中的分组的返回值(默认为None
)。
m.groupdict(default=None)
返回一个字典,其中包含所有匹配值的所有给定名称的分组。default
是针对未包含在匹配值中的分组的返回值(默认为None
)。
更多内容请查看:https://docs.python.org/3/library/re.html
爬取糗事百科
首先,来到糗事百科,然后单击鼠标右键,审查(检查)元素,如下图所示:
可以发现,每个段子都包含在一个<div class="content">……</div>
标签中,我们用urllib库获取到网页源码文本后,就可以用正则表达式把段子提取出来啦。话不多说,直接动手吧!
import urllib.request
from urllib.request import Request
import re
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 OPR/55.0.2994.61'
}
request = Request('https://www.qiushibaike.com', headers=headers)
response = urllib.request.urlopen(request)
html = response.read().decode('utf-8')
pattern = re.compile('<div\s*class="content">\s+?<span>(.*?)</span>', re.S)
results = pattern.findall(html)
for result in results:
result = re.sub('<br/>',' ', result)
print(result)
运行结果如下:
段子顺利得到啦!