10.16 my学习日记(XPath的基础语法,lxml库的应用)
- XPath的基础语法
- XPath查找标签
- XPath谓语
- lxml库在爬虫中的应用
- etree库etree_Element对象
- 使用etree.HTML建立etree._Element对象,并用etree.tostring方法打印HTML对象中的内容
- 使用html解析器
- 使用.xpath方法在Python中使用XPath语法
- 运用lxml库将新垣结衣获奖情况从百科页面中提取出来
XPath的基础语法
XPath查找标签
表达式 | 效用 | 示例代码 | 运行结果 | 评价 |
---|---|---|---|---|
/ | 若’/‘号前没有标签名,则再根节点下查找标签;若’/‘前有标签名,则在该标签的子标签中查找标签 | 1./html;2…/div/span | 1.在根目录下查找html标签;2.在div标签的子标签中查找span标签 | 由于‘/’只能在子标签中查找,若只通过‘/’来查找标签会比较繁琐,通常配合‘//’使用 |
// | 在根标签或者某标签的子孙节点中查找标签 | //div | 找到网页源代码中所有的div标签 | 常用,须灵活使用 |
@ | 通过节点的属性筛选标签 | //div[@class] | 筛选出具有class属性的div标签 | 在查找特定标签时配合谓词‘=’使用十分好用 |
* | 通配符,@*表示所有含有属性的标签,/ *表示所有子标签 | //div[@*] | 筛选出所有有属性的div标签 | 特殊情况可能会使用到 |
XPath谓语
含有谓语的表达式 | 表达式效果 |
---|---|
//tr/td[1] | 查找在tr子标签中的第一个td标签 |
//tbody/tr[@class=‘content’] | 在tbody的子标签中查找属性class为content的tr标签 |
//div/span[last()] | 查找div子标签中最后一个span标签 |
//body/div[position()>3] | 查找body子标签中除了前三个div标签的所有div标签 |
//tr/a[contains(@href,‘123’)] | 在tr的子标签中查找含有属性href的值为123的a标签 |
Google浏览器的XPath Helper插件用快捷键Ctrl+Shift+X启用,使用Ctrl+Shift与鼠标选择页面上的内容,即可在XPath的Query中显示该页面元素的绝对路径,可以方便我们通过代码查找需要的页面内容。
lxml库在爬虫中的应用
etree库etree_Element对象
使用etree.HTML建立etree._Element对象,并用etree.tostring方法打印HTML对象中的内容
from lxml import etree #使用etree库
text="""
<div id="left-container" class="left-container"><div class="item-wrap"><div class="article " id="article" data-islow-browser="0"><div class="article-content"><p><span class="bjh-p">
军已离开叙北部重要据点曼比吉市。几小时后,叙利亚官方媒体报道说叙政府军已进入曼比吉市中心,并在那里升起了国旗。土耳其总统14日曾称,土军队已准备好发动对曼比吉的进攻。对此,俄罗斯政府叙利亚问题特使拉夫伦捷夫周二对俄新社称,
俄罗斯“不会允许”叙政府军与土耳其军队之间发生战斗。</span></p><p><span class="bjh-p">15日,库尔德人领导的叙利亚北部地区政府呼吁世界关注当地的人道主义灾难。联合国人道主义援助协调员称,自土耳其开始军事行动以来,叙东
北部至少有16万平民流离失所,“每天都有大量平民死亡”。</span></p><p><span class="bjh-p">对土耳其的军事行动,法新社15日援引法国外交部官员的话称,法国总统马克龙前一天分别与特朗普和埃尔多安通了电话,马克龙坚称土耳
其在叙东北部发动的进攻对整个地区和欧洲构成了威胁。德国总理默克尔15日再次呼吁土耳其停火,她表示,土军事行动应该结束,“这显然给人类造成了很大的痛苦,也为打击IS带来了很多不确定性”。意大利和英国外交部当天表示,正在审查或
暂停对土出售武器。</span></p><p><span class="bjh-p"><span class="bjh-strong">内容请参见今日出版的《环球时报》或下载登录新版“环球TIME”客户端。</span></span></p></div><audio height="0" wi
dth="0" id="musicAudio" data-play-index><source></source></audio></div></div></div>
"""htmlElement=etree.HTML(text)
#用text生成一个HTML对象,可以对这个对象使用xpath语法
print(type(htmlElement))
#<class 'lxml.etree._Element'> 是一个类的对象
print(etree.tostring(htmlElement,encoding='utf-8').decode('utf-8'))
# HTML类会自动添加html与body标签使语法完整
该代码运行结果如下:
使用etree.HMTL(text),可以创建一个etree._Element对象
从print(type(htmlElement))语句的输出可以看出来,htmlElement的类型为etree._Element的对象,用etree.HTML创建的对象会将传入的部分页面代码自动补齐,可以看见打印出的结果比原代码多出了html
与body标签。
etree.tostring函数可以将etree_Element对象储存的页面代码提取出来,具体语法为etree.tostring(HMTL, encoding=‘utf-8’).decode(‘utf-8’),由于etree._Element对象是以bytes类型保存输入的文本的,故需要先解码,才能打印出正常的易读的页面代码。
使用html解析器
在使用etree.HTML()生成的对象中,有些网页的源代码并不能很好的通过tostring函数打印出来,这可能是页面的解析方式出了问题,因为HTML创捷的对象默认解析方式是xml,而通常我们见到的网页解析方式都是html。可以通过建立一个html的解析器来解决这个问题。
实现代码如下:
from lxml import etree
parser=etree.HTMLParser(encoding='utf-8')
#创建一个html的解析器是HTMLParser类的实例
html=etree.parse('baidu_Aralaki Yui2.html',parser=parser)
#使用parse类可以用文件信息创捷信息,不会自动补齐标签,默认xml解析
print(etree.tostring(html,encoding='utf-8').decode('utf-8'))
可以看到,我们通过etree.HTMLParser创建了一个html解析器对象,然后使用etree.parse,用刚刚建立的解析器建立了一个parse对象,然后使用tostring提取了这个parse对象中的代码信息。parse与HTML的不同之处在于,parse是基于文件中的信息建立对象,而HTML是根据一个程序中的str变量生成的对象。
(该代码中的html文件是新垣结衣百度百科页面的源代码,由于运行结果过长,就不予展示了)
使用.xpath方法在Python中使用XPath语法
在上述程序生成的parse对象中,如果我们要对网页源代码中的各种标签进行索引,应该怎么办呢?
我们可以使用.xpath方法,在该方法中,我们可以使用XPath的语法,很方便的对网页的各种标签进行索引。
.xpath方法的基本语法是:
.xpath(‘XPath语句’)
如果我们要在新垣结衣百度百科页面源码中完成以下任务:
- 获取所有的tr标签
- 获取第二个tr标签
- 获取所有含有class属性并且该属性值含para的标签
- 获取所有a标签中的href属性的值
我们就需要用到.xpath方法。
具体代码如下:
from lxml import etree
parser=etree.HTMLParser(encoding='utf-8')
html=etree.parse('baidu_Aralaki Yui2.html',parser=parser)
#获取所有的tr标签
#xpath方法返回列表,再xpath方法中可以使用xpath语法
trs=html.xpath('//tr')[0]
for tr in trs:print(etree.tostring(tr,encoding='utf-8').decode('utf-8'))
#获取第二个tr标签
tr2=html.xpath('//tr[2]')[0]
print(etree.tostring(tr2,encoding='utf-8').decode('utf-8'))
#获取所有class含para的div标签
contents=html.xpath('//div[contains(@class,"para")]')
for content in contents:print(etree.tostring(content,encoding='utf-8').decode('utf-8'))
#获取所有a标签的href属性
hrefs=html.xpath('//a/@href')
#这里用/@href获得所有href的值,而不是用[@href]来获得含有href的所有a标签
for href in hrefs:print('https://baike.baidu.com'+href)
#href的值已经是文本了不需要tostring函数,再人为给href前加上域名就可以使用href中的链接了
需要注意的是,.xpath返回的是列表,所以如果要打印出返回信息,必须用遍历的方式将所有提取到的符合筛选要求的标签打印出来。如果要用方法对列表中的对象进行操作,则必须用下标将元素提取出来才可。
运用lxml库将新垣结衣获奖情况从百科页面中提取出来
首先通过XPath插件找到了百科页面源代码中含有新垣结衣获奖情况的部分。我将这部分代码复制下来,以html文件的形式储存了下来。
可以看见,获奖记录这一个大表是被储存在一个table标签下的。
将这个table标签的class值在网页源代码中查找,可以找到对应的代码段。(class属性为horizontal-module j-module-4740102 j-common-module)
将这个table标签中的所有代码独立保存为一个html文件,命名为’Aragaki Yui获奖情况.html’,我们可以通过以下代码将新垣结衣所有的获奖情况提取到一个列表中:
# 提取Aragaki Yui的所有获奖情况(纯文本)
from lxml import etree
Yuiaward=[] #建立一个列表用来储存最后的获奖信息
parser=etree.HTMLParser(encoding='utf-8')
html=etree.parse('Aragaki Yui获奖情况.html',parser=parser)
trs=html.xpath('//tr')
#建立解析器,解析html文件,并获取html代码中所有的tr标签
for i in range(int(len(trs)/2)):
#经过观察可以发现,所有的奖项大类型都储存在奇数tr中,所有的奖项细节都在偶数tr中,故需要分开考虑awardlist=[] #建立一个列表用来储存每一种奖项的信息awardtype=trs[2*i].xpath('.//td/text()')[0] #获取奇数tr下td标签中的奖项类别名称awardlist.append(awardtype) #将名称添加到列表中for li in trs[2*i+1].xpath('.//li'): #偶数tr的所有细节储存在li标签中,找出所有的li标签awardtime=li.xpath('.//span[@class="first column"]/text()')[0] awardcontent=li.xpath('.//span[@class="column"]/text()')[0]awarddetail={'awardtime':awardtime,'awardcontent':awardcontent} #保存标签中的文本信息并储存在字典中awardlist.append(awarddetail) #把细节信息的字典添加到列表中Yuiaward.append(awardlist) #再把每一种奖项的列表添加到总表中
for l in Yuiaward:print(l)
需要注意,parse对象进行XPath语法检索得到的列表中的标签元素,在使用.xpath()方法搜寻该标签的子孙标签中,必须在’//‘前加一个’.’,表示在该标签的子孙标签中检索,否则会自动在整个parse对象中查找。
上述程序运行结果如下:
[’\n日本电影学院奖\n’, {‘awardtime’: ‘2008\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第31届\n\xa0\xa0\xa0最佳新人奖’}, {‘awardtime’: ‘2008\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第31届\n\xa0\xa0\xa0话题奖-最具话题演员奖\n\xa0\xa0\xa0恋空\n\xa0\xa0\xa0(获奖)\n\xa0\xa0\xa0’}]
[’\n日本电影蓝丝带奖\n’, {‘awardtime’: ‘2018\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第60届\n\xa0\xa0\xa0最佳女主角’}, {‘awardtime’: ‘2008\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第50届\n\xa0\xa0\xa0最佳新人奖’}]
[’\n日刊体育电影大奖\n’, {‘awardtime’: ‘2007\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第20届\n\xa0\xa0\xa0最佳新人奖’}]
[’\n横滨电影节\n’, {‘awardtime’: ‘2007\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第29届\n\xa0\xa0\xa0最优秀新人奖’}]
[’\n日刊体育日剧大赏\n’, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第21届\n\xa0\xa0\xa0夏季电视剧最佳女配角’}, {‘awardtime’: ‘2016\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第20届\n\xa0\xa0\xa0秋季电视剧最佳女主角奖’}, {‘awardtime’: ‘2016\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第20届\n\xa0\xa0\xa0年度最佳女主角奖’}, {‘awardtime’: ‘2009\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第13届\n\xa0\xa0\xa0最佳女配角’}]
[’\nTV LIFE年度日剧大赏\n’, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第27届\n\xa0\xa0\xa0最佳女配角奖’}, {‘awardtime’: ‘2016\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第26届\n\xa0\xa0\xa0最佳女主角奖’}, {‘awardtime’: ‘2007\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第17届\n\xa0\xa0\xa0最佳女配角奖’}]
[’\n日剧学院赏\n’, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第91届\n\xa0\xa0\xa0最佳女主角’}, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第94届\n\xa0\xa0\xa0最佳女配角’}, {‘awardtime’: ‘2014\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第79届\n\xa0\xa0\xa0最佳女配角\n\xa0\xa0\xa0LEGAL HIGH 2\n\xa0\xa0\xa0(获奖)\n\xa0\xa0\xa0’}, {‘awardtime’: ‘2009\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第61届\n\xa0\xa0\xa0最佳女配角’}]
[’\n日剧信心奖\n’, {‘awardtime’: ‘2016\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第2届\n\xa0\xa0\xa0最佳女主角奖’}, {‘awardtime’: ‘2016\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第6届\n\xa0\xa0\xa0最佳女主角奖’}]
[’\n其他奖项\n’, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第25届\n\xa0\xa0\xa0桥田赏俳优桥田奖’}, {‘awardtime’: ‘2017\n\xa0\xa0\xa0’, ‘awardcontent’: ‘东京电视剧大赏最佳女主角’}, {‘awardtime’: ‘2008\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第45届\n\xa0\xa0\xa0金箭奖电影奖’}, {‘awardtime’: ‘2008\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第1届\n\xa0\xa0\xa0BEST GIRL OF 2007 :最高奖’}, {‘awardtime’: ‘2007\n\xa0\xa0\xa0’, ‘awardcontent’: “第32届\n\xa0\xa0\xa0Élan d’or奖新人奖”}, {‘awardtime’: ‘2007\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第4届\n\xa0\xa0\xa0TVnavi日剧年度大赏年度最佳女配角奖\n\xa0\xa0\xa0父女七日变\n\xa0\xa0\xa0(获奖)\n\xa0\xa0\xa0’}, {‘awardtime’: ‘2001\n\xa0\xa0\xa0’, ‘awardcontent’: ‘第4届\n\xa0\xa0\xa0nicola模特儿试演会:最优秀大奖\n\xa0\xa0\xa0(获奖)\n\xa0\xa0\xa0’}]
可见,用lxml库与XPath语法结合,确实可以大大方便我们提取网页中的元素。