3.1、正则表达式-基本语法
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。
re 模块使 Python 语言拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。
re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。
打开开源中国提供的正则表达式测试工具 tool.oschina.net/regex/
输入待匹配的文本,然后选择常用的正则表达式,就可以得出相应的匹配结果了。 例如,输入下面这段待匹配的文本:
这段字符串中包含了一个电话号码和一个电子邮件,接下来就尝试用正则表达式提取出来,如图所示。
re.match函数
re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
import re print(re.match('www', 'www.baidu.com').span()) # 在起始位置匹配 print(re.match('com', 'www.baidu.com')) # 不在起始位置匹配
(0, 3) None
re.search方法
re.search 扫描整个字符串并返回第一个成功的匹配。
import re print(re.search('www', 'www.baidu.com').span()) # 在起始位置匹配 print(re.search('com', 'www.baidu.com').span()) # 不在起始位置匹配
(0, 3) (10, 13)
re.match与re.search的区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
import re line = "Cats are smarter than dogs"; matchObj = re.match( r'dogs', line, re.M|re.I) if matchObj: print("match --> matchObj.group() : ", matchObj.group()) else: print("No match!!") matchObj = re.search( r'dogs', line, re.M|re.I) if matchObj: print("search --> searchObj.group() : ", matchObj.group()) else: print("No match!!")
No match!! search --> searchObj.group() : dogs
检索和替换
Python 的 re 模块提供了re.sub用于替换字符串中的匹配项。
import re phone = "2021-999-888 # 这是一个奇奇怪怪的电话号码" # 删除字符串中的 Python注释 num = re.sub(r'#.*$', "", phone) print("电话号码是 : ", num) # 删除非数字(-)的字符串 num = re.sub(r'\D', "", phone) print("电话号码是 : ", num)
电话号码是 : 2021-999-888 电话号码是 : 2021999888
import re # 将匹配的数字乘以 2 def double(matched): value = int(matched.group('value')) return str(value * 2) s = 'A23G4HFD567' print(re.sub('(?P<value>\d+)', double, s))
A46G8HFD1134
re.compile 函数
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
pattern = re.compile(r'\d+') # 用于匹配至少一个数字 m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配 print(m)
<_sre.SRE_Match object; span=(3, 5), match='12'>
m.group(0)
'12'
m.start(0)
3
m.end(0)
5
m.span(0)
(3, 5)
在上面,当匹配成功时返回一个 Match 对象,其中:
- group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);
- start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
- end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
- span([group]) 方法返回 (start(group), end(group))。
findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
import re pattern = re.compile(r'\d+') # 查找数字 result1 = pattern.findall('blue 123 google 456') result2 = pattern.findall('bbb88uuu123google456', 0, 10) print(result1) print(result2)
['123', '456'] ['88', '12']
re.finditer
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
import re it = re.finditer(r"\d+","12a32bc43jf3") for match in it: print (match.group() )
12 32 43 3
re.split
split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
re.split('a*', 'hello world') # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
['hello world']
其他用法可自行去查找资料学习,这里只做部分常用的介绍使用
3.2、xpath使用
XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 来选择。
XPath 于 1999 年 11 月 16 日成为 W3C 标准,它被设计为供 XSLT、XPointer 以及其他 XML 解析软件使用,更多的文档可以访问其官方网站:www.w3school.com.cn/xpath/index…www.runoob.com/xpath/xpath…
XPath 常用规则
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点 // 从当前节点选取子孙节点 . 选取当前节点 .. 选取当前节点的父节点 @ 选取属性 这里列出了 XPath 的常用匹配规则,示例如下: //title[@lang='eng']
这就是一个 XPath 规则,它代表选择所有名称为 title,同时属性 lang 的值为 eng 的节点。
后面会通过 Python 的 lxml 库,利用 XPath 进行 HTML 的解析
安装lxml库
**pip install lxml **
html文件实例:
这里需要注意的是,上面的HTML 文本中的最后一个 li 节点是没有闭合的,但是 etree 模块可以自动修正 HTML 文本
# 首先导入 lxml 库的 etree 模块 from lxml import etree # 然后声明了一段 HTML 文本,调用 HTML 类进行初始化,这样就成功构造了一个 XPath 解析对象 html = etree.parse('/home/mw/test.html', etree.HTMLParser()) result = etree.tostring(html) # 调用 tostring() 方法即可输出修正后的 HTML 代码,但是结果是 bytes 类型。这里利用 decode() 方法将其转成 str 类型 print(result.decode('utf-8'))
--------------------------------------------------------------------------- OSError Traceback (most recent call last) <ipython-input-28-8a2af1f47b82> in <module> 2 from lxml import etree 3 # 然后声明了一段 HTML 文本,调用 HTML 类进行初始化,这样就成功构造了一个 XPath 解析对象 ----> 4 html = etree.parse('/home/mw/test.html', etree.HTMLParser()) 5 result = etree.tostring(html) # 调用 tostring() 方法即可输出修正后的 HTML 代码,但是结果是 bytes 类型。这里利用 decode() 方法将其转成 str 类型 6 print(result.decode('utf-8')) src/lxml/etree.pyx in lxml.etree.parse() src/lxml/parser.pxi in lxml.etree._parseDocument() src/lxml/parser.pxi in lxml.etree._parseDocumentFromURL() src/lxml/parser.pxi in lxml.etree._parseDocFromFile() src/lxml/parser.pxi in lxml.etree._BaseParser._parseDocFromFile() src/lxml/parser.pxi in lxml.etree._ParserContext._handleParseResultDoc() src/lxml/parser.pxi in lxml.etree._handleParseResult() src/lxml/parser.pxi in lxml.etree._raiseParseError() OSError: Error reading file '/home/mw/test.html': failed to load external entity "/home/mw/test.html"
所有节点
我们一般会用 // 开头的 XPath 规则来选取所有符合要求的节点
result = html.xpath('//*') # * 选取所有类型的节点 print(result)
result = html.xpath('//li') # 选取li的节点 print(result) # 打印所有 print(result[0]) # 打印第一个
子节点
我们通过 / 或 // 即可查找元素的子节点或子孙节点
result = html.xpath('//li/a') # 通过追加 /a 即选择了所有 li 节点的所有直接 a 子节点 print(result)
result = html.xpath('//ul//a') # 此处的 / 用于选取直接子节点,如果要获取所有子孙节点,就可以使用 // print(result)
父节点
查找父节点可以用.. 来实现
result = html.xpath('//a[@href="link4.html"]/../@class') print(result)
属性匹配
在选取的时候,我们还可以用 @符号进行属性过滤。比如,这里如果要选取 class 为 item-1 的 li 节点
result = html.xpath('//li[@class="item-0"]') print(result)
文本获取
XPath 中的 text() 方法获取节点中的文本
result = html.xpath('//li[@class="item-0"]/a/text()') print(result)
属性获取
节点属性 @符号就可以获取节点内部文本
result = html.xpath('//li/a/@href') print(result)
更多详细操作可自行查阅资料
3.3、某度网站的网页解析
第一步、在制定页面调试浏览器,观察所需的文本内容
第二步、请求页面
import requests # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36', } # 请求参数 params = ( ('wd', '周杰伦'), ) response = requests.get('https://www.baidu.com/s', headers=headers, params=params) # 获取响应过长,只显示部分内容 # print(response.text)
第三步,检查是否有我们要的内容
第四步、导入解析库,解析内容
from lxml import etree import re
html = etree.HTML(response.text) print(html)
第五步、复制xpath : //*[@id="7"]/div/div[2]/div[1]
result = html.xpath('//*[@id="7"]/div/div[2]/div[1]') print(result)
result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/text()') print(result)
我们可以发现,我们所爬取的节点内容不包含span、em节点,所以里面的内容我们没有获取到//*[@id="7"]/div/div[2]/div[1]
result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/span/text()') print(result) # 输出的是列表格式 print(''.join(result)) # 转化为str格式 print(re.sub("小时前","", ''.join(result))) # 除去多余
result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/em/text()') # 获取了所有的节点的周杰伦 print(result)
result = html.xpath('//*[@id="7"]/div/div[2]/div[1]/em[1]/text()') # 获取了第一个节点的周杰伦 print(result)
Task3、作业
目标网站: 123.meibp.com/tag/%e6%af%…
目标:只需要能够正常提取出来既可以,并在Makrdown运行后,截图出运行内容既可
!! 温馨提示,该项目只作为练习,切勿做出对目标网站有所压力的行为
import requests from lxml import etree import re # 请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36', } response = requests.get('http://123.meibp.com/tag/%e6%af%8f%e6%97%a5%e7%83%ad%e7%82%b9%e5%85%b3%e9%94%ae%e8%af%8d', headers=headers) # 获取响应过长,只显示部分内容 html = etree.HTML(response.text)
article_len=len(html.xpath('//*[@id="hot"]/div/div/article')) print(article_len) for i in range(1,article_len+1): print(25*'*' + str(i) +25*'*') title=html.xpath(f'//*[@id="hot"]/div/div/article[{i}]/header/h2/a/text()') print('1.标题:',title[-1]) # 分类显示为空: xw_class=html.xpath(f'//*[@id="hot"]/div/div/article[{i}]/span[1]/span/text()') print('2.分类:',xw_class[-1]) mark=html.xpath(f'//*[@id="hot"]/div/div/article[{i}]/span[2]/span/a/text()') print('3.标签:',mark)
10 *************************1************************* 1.标题: 每日热点 - 星网大数据 2.分类: 分类: 3.标签: ['百度', '搜狗', '新浪', '大数据', '热点', '热搜', '关键词', '数据', '榜单', '狗', '每日热点', '百度热搜', '–', '星网大数据', '每日热点关键词'] *************************2************************* 1.标题: 星网大数据 - 汇聚全网词库 2.分类: 分类: 3.标签: ['大数据', '热点', '关键词', '数据', '分析', '行业词库', '词库', '关键词分析', '企业信用', '企业', '每日热点', '星网大数据', '汇聚全网词库', '关键词库', '全网词库'] *************************3************************* 1.标题: 微友助手-微信群管家 2.分类: 分类: 3.标签: ['微友助手', '微群管家', '微信管家', '微信群管理', '移动互联网', '微信群', '微信群数据', '消息群发', '微商', '电商', '淘宝客', '自动回复', '客服助手', '微信助手', '微友课堂', '微信多群直播', '社群营销', '安全稳定国内领先微信群管家', '超群助手'] *************************4************************* 1.标题: 爱微帮-每日热点 2.分类: 分类: 3.标签: ['新媒体', '新媒体运营', '素材', '爱微帮', '热点', '素材库', '运营工具', '媒体', '运营', '新闻热点', '历史上的今天', '选题', '互动', '每日热点', '选题素材库', '爱微帮公众号助手', '微博热搜', '微博热议', '百度热搜', '节日大全', '未来头条'] *************************5************************* 1.标题: 库 2.分类: 分类: 3.标签: ['关键词', '行业词库', '长尾', '词库', '词库网', '热门关键词库', '长尾关键词库', '网站关键词库', '行业关键词库', '热门词库', '关键词词库', '长尾关键词', '关键词分析', '关键词优化', '关键词排名', '热门关键词', '最新关键词', '行业关键词', '关键词预测'] *************************6************************* 1.标题: 排名查询 卖家店铺优化工具箱 2.分类: 分类: 3.标签: ['电子商务', '关键词', '电商', '互联网电商', '关键词排名', '卖家', '排名', '天猫', 'im', '工具箱', '查排名', '直通车', '淘宝', '宝贝关键词排名查询', '卖家店铺优化工具箱', '淘宝关键词排名查询', '淘宝宝贝排名查询', '卖家工具箱', '淘宝关键词优化', '直通车优化'] *************************7************************* 1.标题: 抽屉新热榜-聚合每日热门、搞笑、有趣资讯 2.分类: 分类: 3.标签: ['微博', '图片', '门户', '资讯', '社区', '新闻', '段子', '搞笑', '榜单', '用户', '抽屉', '抽屉新热榜', '聚合每日热门', '有趣资讯', '公众场合不宜', '节操'] *************************8************************* 1.标题: UC热点 2.分类: 分类: 3.标签: ['实时', '热点', '实时热点', 'U', 'UC热点'] *************************9************************* 1.标题: AnyKnew - 全网热点 2.分类: 分类: 3.标签: ['AnyKnew', '效率资讯', '高效读新闻', '5分钟遍历全网热点'] *************************10************************* 1.标题: 微博热点 2.分类: 分类: 3.标签: ['微博热点']