网页解析 -- xpath

1. 概述

xpath在Python的爬虫学习中,起着举足轻重的地位。

对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但xpath明显比re具有优势,在网页分析上使re退居二线。

xpath 全称为**XML Path Language** 一种小型的**查询语言**

xpath的优点

● 可在XML中查找信息 
● 支持HTML的查找 
● 通过元素和属性进行导航


2. 使用xpath

python开发使用XPath条件: 由于XPath属于lxml库模块,所以首先要安装库lxml。

● 安装:pip install lxml

● 引用:from lxml import etree

● selector=etree.HTML(源码) #将源码转化为能被XPath匹配的格式
● selector.xpath(表达式) #返回为一列表

xpath的路径表达式

表达式描述实例解析
从根节点选取/body/div[1]选取根结点下的body下的第一个div标签(子查询)
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置//a

选取文档中所有的a标签

(全局子孙查询)

./当前节点再次进行xpath./a选取当前节点下的所有a标签
选取属性 //@calss选取所有的class属性

代码示例:

from lxml import etree

#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

select = etree.HTML(html_doc)  #select基于上面的html字符串文档
ret = select.xpath("//a")  #select基于文档中所有的a标签
print("ret:",ret)  #f返回的是所有a标签的Element对象

#基于所有a标签的Element对象,拿到具体信息:
for linkE in ret:
    name = linkE.xpath("./text()") #基于a标签,取出所有的文本内容
    href = linkE.xpath("./@href")  #基于a标签,取出所有的href属性值
    print("具体信息:",name,href)   #打印所有文本、href属性值

#输出:
ret: [<Element a at 0x1dc3e370b00>, <Element a at 0x1dc3e6cc8c0>, <Element a at 0x1dc3e6cd380>]
具体信息: ['Elsie'] ['http://example.com/elsie']
具体信息: ['Lacie'] ['http://example.com/lacie']
具体信息: ['Tillie'] ['http://example.com/tillie']

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

谓语被嵌在方括号中。

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式  结果  
/ul/li[1]   选取属于 ul子元素的第一个 li元素
/ul/li[last()]     选取属于 ul子元素的最后一个 li元素
/ul/li[last()-1] 选取属于 ul子元素的倒数第二个 li元素
 //ul/li[position()<3]选取最前面的两个属于 ul元素的子元素的 li元素
//a[@title] 选取所有拥有名为 title的属性的 a元素
//a[@title='xx'] 选取所有 a元素,且这些元素拥有值为 xx的 title属性

//a[@title>10]

> < >= <= !=

选取 a元素的所有 title元素,且其中的 title元素的值须大于 10
/body/div[@price>35.00] 选取body下price元素值大于35的div节点

以上路径表达式,同样适用于在F12中查找:

例如:打开淘宝网

tips:不知道怎么写xpath,也可以直接让f12帮我们找

只是找出的xapth路径会比较长,它是一个非常完整的xpath

选取未知节点

XPath 通配符可用来选取未知的 XML 元素

通配符 描述
*匹配任何元素节点
 @*匹配任何属性节点
node()匹配任何类型的节点

在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/ul/*   选取 bookstore 元素的所有子元素
//*  选取文档中的所有元素
//title[@*] 选取所有带有属性的 title 元素
//node()获取所有节点

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径

在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:

路径表达式 结果
//book/title \| //book/price 选取 book 元素的所有 title 和 price 元素
//title \| //price   选取文档中的所有 title 和 price 元素
/bookstore/book/title \| //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素

逻辑运算

示例:

//div[@id="head" and @class="s_down"] :

        查找所有id属性等于head并且class属性等于s_down的div标签
//title | //price :

        选取文档中的所有 title 和 price 元素,“|”两边必须是完整的xpath路径

属性查询

示例:

//div[@id] :找所有包含id属性的div节点
//div[@id="maincontent"]  :查找所有id属性等于maincontent的div标签
//@class
//li[@name="xx"]//text()  :获取li标签name为xx的里面的文本内容

>

★ @在()里是查询属性,@在[]里是

使用示例如下: 

from lxml import etree

#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

select = etree.HTML(html_doc)  #select基于上面的html字符串文档
ret = select.xpath("//a/text()")  #查询所有a标签的文本属性值
print("ret:",ret)

ret1 = select.xpath("//a[1]/text") #查询第一个a标签的文本属性值
print("ret1:",ret1)

ret2 = select.xpath("//a/@href") #查询所有a标签的链接属性值
print("ret2:",ret2)

ret3 = select.xpath("//a[1]/@href") #查询第一个a标签的链接属性值
print("ret3:",ret3)

ret4 = select.xpath("//a/@id | //a/@class") #查询所有a标签的id属性值和class属性值,不支持用and
print("ret4:",ret4)

#输出:
ret: ['Elsie', 'Lacie', 'Tillie']
ret1: []
ret2: ['http://example.com/elsie', 'http://example.com/lacie', 'http://example.com/tillie']
ret3: ['http://example.com/elsie']
ret4: ['sister', 'link1', 'sister', 'link2', 'sister', 'link3']

获取标签

示例:索引从1开始

 tree.xpath('//li[1]/a/text()')  : 获取第一个
 tree.xpath('//li[last()]/a/text()')  : 获取最后一个
 tree.xpath('//li[last()-1]/a/text()')  :获取倒数第二个

模糊查询

  //div[contains(@id, "he")]  :查询所有id属性中包含he的div标签
  //div[starts-with(@id, "he")] :查询所有id属性中包以he开头的div标签
  //div/h1/text() : 查找所有div标签下的直接子节点h1的内容
  //div/a/@href:获取a里面的href属性值 
  //* :获取所有
  //*[@class="xx"] :获取所有class为xx的标签

使用示例:

from lxml import etree

#爱丽丝文档
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister c1" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister c2" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

select = etree.HTML(html_doc)  #select基于上面的html字符串文档

'''
查询所有class属性值为"sister"的a标签
注意:class属性值必须和html文档一致,是双引号

可以看出,还是使用@class="sister"时,查结果只有一个完全匹配的
所以,需要使用匹配contains、或者starts_with(但必须要以指定的class属性值为开头)
'''
ret = select.xpath('//a[@class="sister"]')
print("ret:",ret)

ret1 = select.xpath('//a[contains(@class,"sister")]')
print("ret1:",ret1)

ret2 = select.xpath('//a[starts-with(@class,"sister")]')
print("ret2:",ret2)


#输出:
ret: [<Element a at 0x1b3e3b7cd00>]
ret1: [<Element a at 0x1b3e3b7cd00>, <Element a at 0x1b3e3b7c840>, <Element a at 0x1b3e3b7cf40>]
ret2: [<Element a at 0x1b3e3b7cd00>, <Element a at 0x1b3e3b7c840>, <Element a at 0x1b3e3b7cf40>]

3. xpath使用案例

以豆瓣电影top250为例,使用xpath进行电影信息提取

网址:豆瓣电影TOP250(含下榜片)

>

初级水平,简单处理步骤:

1. 将所有的返回html内容,黏贴在html文件中(后续涉及了直接网页爬取再进行整体优化)

2. 新建一个py文件,进行导入html文件,然后开始电影名提取

from lxml import etree

with open("douban250.html",encoding="utf-8") as f:
    data = f.read()

select = etree.HTML(data)
movies = select.xpath('//div[@class="bd doulist-subject"]')

movie_list = []

for movie in movies:
    movie_dict = {}

    title = movie.xpath('.//div[@class="title"]//a/text()')
    movie_dict['电影名'] = title[0].strip() if title else ""

    rating = movie.xpath('.//div[@class="rating"]//span[@class="rating_nums"]/text()')
    movie_dict['评分'] = rating[0].strip() if rating else ""

    director = movie.xpath('.//div[@class="abstract"]/text()[normalize-space() and contains(., "导演:")]')
    movie_dict['导演'] = director[0].strip().replace("导演:", "") if director else ""

    movie_list.append(movie_dict)

for movie in movie_list:
    print("电影名:", movie['电影名'])
    print("评分:", movie['评分'])
    print("导演:", movie['导演'])
    print("-------------")

#输出:
电影名: 
评分: 9.7
导演:  弗兰克·德拉邦特
-------------
电影名: 
评分: 9.6
导演:  陈凯歌
-------------
电影名: 
评分: 9.5
导演:  罗伯特·泽米吉斯
-------------
电影名: 
评分: 9.5
导演:  詹姆斯·卡梅隆
-------------
电影名: 
评分: 9.4
导演:  吕克·贝松
-------------
电影名: 
评分: 9.5
导演:  罗伯托·贝尼尼
-------------
电影名: 千与千寻 千と千尋の神隠し
评分: 9.4
导演:  宫崎骏
-------------
电影名: 
评分: 9.5
导演:  史蒂文·斯皮尔伯格
-------------
电影名: 
评分: 9.4
导演:  克里斯托弗·诺兰
-------------
电影名: 忠犬八公的故事 Hachi: A Dog's Tale
评分: 9.4
导演:  拉斯·霍尔斯道姆
-------------
……至第25个

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值