一、XPath语法
XPath使用路径表达式来选取XML文档里的节点或者节点集。这些路径表达式和我们在常规电脑文件系统中看到的表达式十分相似
表达式 | 描述 | 实例 | 结果 |
---|---|---|---|
nodename | 选取此节点的所有子节点 | bookstore | 选取bookstore下所有的子节点 |
/ | 如果实在最前面,代表从根节点选取。否则选取某节点下的某个节点 | /bookstore | 选取根元素下所有的bookstore节点 |
// | 从全局节点中选择节点,随便在哪个位置 | //book | 从全局节点中找到所有的book节点 |
@ | 选取某个节点的属性 | //book[@price] | 选取所有拥有price属性的book节点 |
- 谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于bookstore子元素的第一个book元素 |
/bookstore/book[last()] | 选取属于bookstore子元素的最后一个book元素 |
/bookstore/book[last()-1] | 选取属于bookstore子元素的倒数第二个book元素 |
/bokstore/book[position()< 3] | 选取最前面的两个属于bookstore元素的子元素的book元素 |
//title[@lang] | 选取所有拥有名为lang属性的title元素 |
//title[@lang=‘eng’] | 选取所有title元素,且这些元素拥有值为eng的lang属性 |
/bookstore/book[price>35.00] | 选取bookstore元素的所有book元素,且其中price元素的值要大于35.00 |
/booksore/book[preci>35.00]/title | 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值要大于35.00 |
- 通配符
*
表示通配符
通配符 | 描述 | 示例 | 结果 |
---|---|---|---|
* | 匹配任意节点 | /bookstore/* | 选取bookstore下的所有子元素 |
@* | 匹配节点中的任何属性 | //book[@*] | 选取所有带有属性的book元素 |
- 选取多个路径
通过在路径表达式中使用|
运算符,可以选取若干个路径
//bookstore/book | //book/title
#选取所有book元素以及book元素下所有的title元素
二、lxml库
lxml是一个HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML数据
我们可以利用它来解析HTML代码,并且在解析HTML代码的时候,若HTML代码不规范,会自动进行补全
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
#将字符串解析为html文档
html = etree.HTML(text)
#按字符串序列化html文档
result = etree.tostring(html)
print(result)
- 从文件中读取html代码:
除了直接使用字符串进行解析,lxml还支持从文件中读取内容
from lxml import etree
#读取外部文件 a.html
html = etree.parse('a.html')
result = etree.tostring(html, pretty_print = True)
print(result)
- 在lxml库中使用xpath语法
from lxml import etree
html = etree.parse('a.html')
获取所有li标签
result = html.xpath('//li')
print(result)
#得到的是对象
for i in result:
print(etree.tostring(i).decode('utf-8'))
获取所有li标签下的所有class属性的值
result = html.xpath('//li/@class')
print(result)
获取li标签下href为www.baidu.com的a标签
result = html.xpath('//li/a[@href="www.baidu.com"]')
print(result)
#若返回的是空,则html中没有这个内容或有输入错误
获取li标签下所有span标签
result = html.xpath('//li//span')
获取li标签下的a标签里的所有class
result = html.xpath('//li/a//@class')
print(result)
获取最有一个li的a的href属性对应的值:
result = html.xpath('//li[last()]/a/@href')
获取倒数第二个li元素的内容(内容通常指文字部分)
result = html.xpath('//li[last()-1]/a')
print(result[0].text)
获取倒数第二个li元素的内容的第二种方式
result = html.xpath('//li[last()-1]/a/text()')
print(result)
三、BeautifulSoup4库
和lxml一样,Beautiful Soup也是一个HTML/XML解析器,主要的功能也是如何解析和提取HTML/XML数据
lxml只会局部变量,而Beautiful Soup是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml
- 三大解析工具对比
解析工具 | 解析速度 | 使用难度 |
---|---|---|
BeautifulSoup | 最慢 | 最简单 |
lxml | 快 | 简单 |
正则 | 最快 | 最难 |
from bs4 import BeautifulSoup
#创建 BeautifulSoup对象
#使用lxml来进行解析
soup = BeautifulSoup(html, "lxml")
print(soup.prettify())
- 四个常用的对象
Beauti Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象。所有对象分为4种:
1.Tag
Tag通俗点讲就是HTML的一个个标签。我们可以利用soup加标签名轻松地获取这些标签的内容;这些对象的类型是bs4.elemeng.Tag
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, "lxml")
print(soup.body())
2.NavigableString
拿到标签后,还想获取标签中的内容,可以通过tag.string获取标签中的文字
- 输出p标签名
print(soup.p.name)
- 输出p标签所有属性
print(soup.p.attrs)
- 输出p标签属性值
print(soup.p['class'])
或print(soup.p.get('class'))
- 由于是字典,还可以修改属性值
soup.p['class'] = 'new'
print(soup.p)
3.BeautifulSoup
BeautifulSoup对象表示的是一个文档的所有内容。大部分时候,可以把它当做Tag对象,它支持遍历文档树和搜索文档树中表述的大部分的方法
(1)遍历文档树
a. contents和children
contents:返回所有子节点的列表
children:返回所有子节点的迭代器
b. strings和stripped_strings
strings:如果tag包含多个字符串,可以使用.strings
来循环获取所有字符串
stripped_strings:若输出的字符串中有很多空格或空行,用.stripped_strings
可去除空白内容
(2)搜索文档树
a. find和find_all方法
搜索文档树一般用的比较多的,有find和find_all。其中find方法是找到第一个满足条件的标签就立即返回,只返回一个元素;find_all方法是把所有满足条件的标签都选到,然后返回
soup = BeautifulSoup(html, 'lxml')
#获取第一个标签
print(soup.tr)
print(soup.find('tr'))
#获取所有标签
trs = soup.find_all('tr')
for tr in trs:
print(tr)
print('-'*50)
#获取第二个标签
tr = soup.find_all('tr', limit = 2)[1]
print(tr)
#获取所有class等于even的tr标签
trs = soup.find_all('tr', class_ = even) #由于直接class与内置函数重名,因此加一个_
for tr in trs:
print(tr)
print('-'*50)
#或
trs = soup.find_all('tr',attrs = {'class','even'})
#将所有id等于test,class也等于test的a标签提取出来
list = soup.find_all('a', id='test', class_='test')
for a in list:
print(a)
#获取所有a标签的href属性
alist = soup.find_all('a')
for a in alist:
href = a['href']
print(href)
#获取所有的职位信息(纯文本)
trs = soup.find_all('tr')[1:]
for tr in trs:
tds = tr.find_all('td')
name = tds[0].string
# print(name)
b. select方法
内容 | 代码 |
---|---|
通过标签名查找 | print(soup.select(‘a’)) |
通过类名查找 | print(soup.select(’.sister’)) |
通过id查找 | print(soup.select(’#link1’)) |
组合查找 | pring(soup.select(‘p #link1’)) |
通过属性查找 | print(soup.select(‘a[href=“http://www.baidu.com”]’)) |
获取内容 | print(soup.select(‘title’)[0].get_text()) |
4.Comment
Tag,NavigableString,BeautifulSoup几乎覆盖了html和xml中的所有内容,但是还是有一些特殊对象,容易让人担心的内容是文档的注释部分