文章目录
一、XML简介
昨天分享了爬虫的正则表达式,以此来获取数据,但是难度很高,有时候并不能完全掌握,处理HTML文档很累,那么有没有其他的办法呢?答案是肯定的,那就是XPath,我们可以:
- 先将HTML文件转换成XML文档
- 然后用XPath查找HTML节点或元素
(一)什么是XML
- XML 指可扩展标记语言(EXtensible Markup Language)。
- XML 是一种标记语言,很类似 HTML。
- XML 的设计宗旨是传输数据,而非显示数据。
- XML 的标签需要我们自行定义。
- XML 被设计为具有自我描述性。
- XML 是 W3C 的推荐标准。
W3School 官方文档http://www.w3school.com.cn/xml/index.asp
(二)XML和HTML的区别
他们两者都是用于操作数据或者结构数据,在结构上大致相同的,但他们在本质上却存 在着明显的区别。
数据格式 | 描述 | 设计目标 |
---|---|---|
XML | Extensible Markup Language ( 可扩展标记语言) | 被设计为传输和存储数据,其焦点 是数据的内容。 |
HTML | HyperText Markup Language(超文本标记语言) | 显示数据以及如何更好显示数据。 |
HTML DOM | Document Object Model for HTML(超文本标文档 对象模型) | 通过 HTML DOM,可以访问所有 的 HTML 元素, 连同它们所包含 的文本和属性。可以对其中的内容 进行修改和删除,同时也可以创建 新的元素。 |
二、XPATH
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文 档中对元素和属性进行遍历。
(一)选取节点
XPath使用路径表达式来选取XML文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。下面列出最常用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从节点选取 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置 |
. | 选取当前节点 |
·· | 选取当前节点的父节点 |
@ | 选取属性 |
(二)谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点,被嵌在方括号中。
在后面会举例说明。
(三)选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
(四)选取若干路径
通过在路径表达式中使用“|”运算符,可以选取若干个路径。在后面会举例说明。
三、lxml模块
(一)lxml 简介与安装
lxml 是一个 HTML/XML 的解析器,主要的功能是如何解析和提取 HTML/XML 数据。 我们可以利用之前学习的 XPath 语法,来快速的定位特定元素以及节点信息。
安装方法:pip install lxml
(二)lxml 初步使用
1. 导入方式
- 直接导入:from lxml import etree
注意:此种导入方式,可能会报错(etree下面会出现红色波浪线,不影响正常使用) - 标准导入:
from lxml import html
etree = html.etree
2. 内置方法及说明
下面的例子中使用这个 XML 文档:
str = '<bookstore>' \
'<book>' \
'<title lang="bng" src="https://www.baidu.com">Harry Potter</title>' \
'<price>29.99</price>' \
'</book>' \
'<book>' \
'<title class="t1" lang="ang">Learning XML</title>' \
'<price>39.95</price>' \
'</book>' \
'<book>' \
'<title lang="cng">西游记</title>' \
'<price>69.95</price>' \
'</book>' \
'<book>' \
'<title lang="dng" src="https://www.jdfgd.com">水浒传</title>' \
'<price>29.95</price>' \
'</book>' \
'<book>' \
'<title class="t1" lang="dng" src="https://www.jd.com">三国演义</title>' \
'<price>29.95</price>' \
'</book>' \
'</bookstore>'
- etree.HTML()
将字符串转换成HTML元素对象,可以自动添加缺失的元素
例:
html = etree.HTML(str)
print(html) # <Element html at 0x39dec88>
- tostring():查看转换之后的内容(二进制类型)
如果想要查看字符串类型,需要解码
如果想要显示汉字,需要先编码,再解码
例:
content = etree.tostring(html,encoding='utf-8')
print(content.decode())
- xpath()
作用:提取页面数据,返回值是一个列表
xpath的使用一定是建立在etree.HTML()之后的内容中的
问:xpath是如何来提取页面数据的?
答:使用的是路径表达式
xpath路径分为两种:
第一种:/
代表一层层的查找,如果/存在于开头,代表根路径
例如:查找bookstore标签
bookstore = html.xpath('/html/body/bookstore')
print(bookstore) # [<Element bookstore at 0x39d5d48>]
第二种://
任意路径 焦点在元素身上
例如:查找bookstore标签
bookstore = html.xpath('//bookstore')
print(bookstore) # [<Element bookstore at 0x39d5d88>]
第一种和第二种结合
例如:查找bookstore标签
book = html.xpath('//bookstore/book')
print(book) # [<Element book at 0x39d5d48>, <Element book at 0x39d5c48>, <Element book at 0x39d5d88>, <Element book at 0x39d5dc8>, <Element book at 0x39d5e08>]
- /text():获取标签之间的内容
例如:获取所有title标签的内容
步骤:
1.找到所有title标签
2.获取内容
title = html.xpath('//book/title/text()')
print(title) # ['Harry Potter', 'Learning XML', '西游记', '水浒传', '三国演义']
- 谓语使用:可以理解成条件
- [n] 代表获取第n个元素,n是数字,n>=1
例如:获取第二个title标签
title = html.xpath('//book[2]/title/text()')
print(title) # ['Learning XML']
- last():获取最后一个
同理:last()-1 获取倒数第二个
例如:获取最后一本书的title标签之间的内容
title = html.xpath('//book[last()]/title/text()')
print(title) # ['三国演义']
- position():位置,范围 支持 > / < / = / >= / <= / !=
例如:获取最后两本书的title标签之间的内容
步骤:
1.先获取后两本书
2.获取内容
title = html.xpath('//book[position()>3]/title/text()')
title = html.xpath('//book[position()>last()-2]/title/text()')
print(title) # ['水浒传', '三国演义']
- 获取属性值:@属性名
例如:获取lang属性值为cng的title标签的内容
title = html.xpath('//book/title[@lang="cng"]/text()')
print(title) # ['西游记']
- and :与
连接的是谓语(条件)
例如:获取lang=”dng”并且class=”t1″的title标签的内容
title = html.xpath('//book/title[@lang="dng" and @class="t1"]/text()')
title = html.xpath('//book/title[@lang="dng"][@class="t1"]/text()')
print(title) # ['三国演义']
- or:或
例如:查找lang=”cng”或者lang=”bng”的title标签的内容
title = html.xpath('//book/title[@lang="cng" or @lang="bng"]/text()')
print(title)
- | 使用 :连接路径
例如:获取所有title标签和price标签之间的内容
title_price = html.xpath('//title/text() | //price/text()')
print(title_price) # ['Harry Potter', '29.99', 'Learning XML', '39.95', '西游记', '69.95', '水浒传', '29.95', '三国演义', '29.95']
- parse():从文件中读取数据
注意:读取的文件,必须满足xml格式(不存在单标签,全部都是双标签)
content = etree.parse('Node.html')
print(content) # <lxml.etree._ElementTree object at 0x00000000039D5D48>
res = etree.tostring(content)
print(res)
四、案例:网易云音乐歌手信息爬取
需求:
获取每一个大分类下的每一个子分类下的所有歌手名
思路:
- 获取大分类的URL,并请求
- 获取小分类的URL,并请求
- 获取歌手名
import requests
from lxml import etree
# 定义请求函数
def get_requests(url):
response = requests.get(url=url,headers=headers)
return etree.HTML(response.text)
# 定义获取完整链接的函数
def get_full_href(url):
return 'https://music.163.com'+url
# 定义获取大分类url的函数
def get_big_type(url):
# 发起请求,接收响应
# response = requests.get(url=url,headers=headers)
# 转换成HTML元素对象
html = get_requests(url)
# 获取大分类的URL
big_type_list = html.xpath('//div[@class="blk"]/ul/li')
for big_type in big_type_list:
# 获取大标题链接
big_href = big_type.xpath('./a/@href')[0]
# 需要拼接成完整的链接
big_href_full = get_full_href(big_href)
get_small_type(big_href_full)
# print(big_href_full)
pass
# 定义获取小分类URL的函数
def get_small_type(big_href_full):
html = get_requests(big_href_full)
# 获取每一个小分类
small_li_list = html.xpath('//ul[@id="initial-selector"]/li')
for small_li in small_li_list:
# 获取小分类名字
small_name = small_li.xpath('./a/text()')[0]
# 获取小分类的链接
small_href = small_li.xpath('./a/@href')[0]
small_full_href = get_full_href(small_href)
# print(small_full_href)
get_singer(small_name,small_full_href)
...
# 定义获取歌手的函数
def get_singer(small_name,small_full_href):
html = get_requests(small_full_href)
# 获取歌手名
# 前五个://ul[@id="m-artist-box"]/li/p/a/text()
# 中间五个://ul[@id="m-artist-box"]/li[@class="line"]/p/a/text()
# 其它://ul[@id="m-artist-box"]/li[@class="sml"]/a/text()
# 最终得到:
# //ul[@id="m-artist-box"]/li//a/text()
singer = html.xpath('//ul[@id="m-artist-box"]/li//a/text()')
# 保存数据
write_to_txt(small_name,singer)
# print(singer)
...
# 定义保存数据函数
def write_to_txt(small_name,singer):
dic = {}
dic['小标题'] = small_name
dic['歌手'] = singer
with open('网易云歌手.txt','a',encoding='utf-8') as fp:
fp.write(str(dic)+'\n')
if __name__ == '__main__':
# 定义基础URL
base_url = 'https://music.163.com/discover/artist'
# 定义请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36',
}
get_big_type(base_url)
感谢浏览
版权声明:本文为Zachary579原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。