介绍
lxml也是一个用于筛选指定html内容的模块,pyquery就是基于lxml。
使用lxml主要须要了解xpath
xpath语法
- /:在子节点里面找
- //:在子子孙孙节点里面找
- //div:查找当前网页的全部div标签
- //div/p:先找到全部的div标签,再从div的字标签中找p标签
- //div//p:先找到全部的div标签,再从div的子孙标签中找p标签
- //div/a:先查找全部div标签,再从div的子标签中找a标签
- //div/a[@id]:先查找全部div标签,再从div的子标签中找有id属性的a标签
- //div//a[@id='fuck']:先查找全部div标签,再从div的子孙标签中找有id='fuck'的a标签
- //div/a[1]:先查找全部的div标签,再找div的子标签中的第一个a标签,这里的索引是从1开始的,不是0
- //div/a[last()]:和上面同样,不过这里是最后一个a标签
- //div/a[position()<3]:前两个a标签
- //div/a[@price]:拥有price属性的a标签
- //div/a[@price=10]:拥有price属性,而且值等于10的a标签。固然里面还支持>,<,>=,<=等等
- **//div/*:星号表示通配符,选取全部div的子标签**
- //div/a[@*]:选取全部div的子标签中带有属性的a标签,什么属性均可以,id、class、href等等均可以
- //div/a | //div/p:选取全部的div的子标签中的全部a元素和p元素
- //div/a[contains(@class, "BDE")]:找出全部div的子标签中的class属性包含"BDE"的a标签
- //div/a[starts-with(@class, "BDE")]:找出全部div的子标签中的class属性以"BDE"开头的a标签
- //div/a[contains(@href, "mashiro")]:找出全部div的子标签中的class属性包含"mashiro"的a标签
- //a/@href:获取href属性
- //a/@class:获取class属性
- //a/text():获取文本
- //a[contains(text(), "清纯可爱")]:找出文本包含"清纯可爱"的a标签
咱们可能注意到:@href、@class、text()前面只有一个/,若是是两个/的话,好比div标签,它里面是没有href属性的。可是div里面有a标签,a标签里面有href属性,因此咱们仍然能够经过//div//@href去获取,此时获取的是里面的a标签里面的href,可是//div/@href是获取不到的,由于//div/@href表示的是获取div标签里面的href,而div没有href属性。
所以若是是/@href,那么前面必须是有href属性的标签,不然获取不到。但若是是//@href的话,前面的标签就没有太多要求了,能够是p标签,也能够是div标签,只要里面有具备href属性的标签便可
使用lxml
from lxml import etree
import requests
res = requests.get("http://www.baidu.com",
headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"})
res.encoding = res.apparent_encoding
# 调用etree内部的HTML方法,将html文本传进去,便获得一个能够进行xpath的对象
# 咱们能够调用etree.tostring(html),会获得一个字节对象,再解码会获得字符串,这里就不演示了。
html = etree.HTML(res.text)
# 找出class属性等于"toindex"的a标签
result = html.xpath("//a[@class='toindex']")
for res in result:
# 打印的结果是一个标签
print(res) # <Element a at 0x1b2cb5cecc8>
# 能够调用tostring转成字节
print(etree.tostring(res)) # b'<a class="toindex" href="/">百度首页</a>'
print(str(etree.tostring(res), encoding="utf-8")) # <a class="toindex" href="/">百度首页</a>
# 内部还有一个etree.parse()方法,能够直接传入html文件或者xml文件的路径,进行解析
# 获取一下内部属性
result = html.xpath("//a[@class='toindex']/@href")
# 因为标签只有一个,因此列表里面只有一个元素
print(result) # ['/']
result = html.xpath("//a[@class='toindex']/@class")
print(result) # ['toindex']
result = html.xpath("//a[@class='toindex']/text()")
print(result) # ['百度首页']
# 能够看到此时的result又都不是标签了,这是为何?
# 若是咱们不选择href、class等具体属性的话,那么获得的是一个标签,若是选择属性那么获得是字符串
# 由于不止一个标签,因此会将全部的字符串组合成一个列表
# 即使只有一个元素,获得依旧是一个列表
result = html.xpath("//div[contains(@class, 'tab_inner')]")
# 如今获取的result里面只有一个元素
for res in result:
print(res) # <Element div at 0x2644f88>
print(etree.tostring(res)) # b'<div class="s_tab_inner">\n <b>网页</b>\n <a href=。。。。。。
# 咱们看到了,若是不是获取href、class、text等属性的时候,获得的依旧是一个Element对象,这就意味着咱们能够继续使用xpath
titles = res.xpath(".//a/text()") # 注意这里是.//不是//,由于咱们要在当前元素的子孙中去查找
print(titles) # ['资讯', '贴吧', '知道', '音乐', '图片', '视频', '地图', '文库', '更多»']
# 咱们试试不加.
titles = res.xpath("//a/text()")
# 能够看到内容就多了,由于即使是res.xpath,但指定//的话依旧会在全局html页面中查找
print(titles)
"""
# ['手写', '拼音', '关闭', '百度首页', '设置', '登陆', '新闻', 'hao123',
'地图', '视频', '贴吧', '学术', '登陆', '设置', '更多产品', '资讯', '贴吧',
'知道', '音乐', '图片', '视频', '地图', '文库', '更多»', '把百度设为主页',
'关于百度', 'About\xa0\xa0Baidu', '百度推广', '使用百度前必读', '意见反馈',
'京公网安备11000002000001号']
"""
# 进一步证明了二者结果是同样的
print(res.xpath("//a/text()") == html.xpath("//a/text()")) # True