Xpath解析实战

133 阅读5分钟

Xpath介绍

xpath是XML路径语言,它可以用来确定xml文档中的元素位置,通过元素路径来完成对元素的查找。xpath是一种非常强大的定位方式。

  • xml是一种可扩展标记语法的文本格式,xpath可以方便的定位xml中的元素和其他属性值。lxml是python中的一个第三方模块,它包含了将html文本转成xml对象,和对对象执行xpath的功能
  • Xpath(XML Path Language)是一种xml的查询语言,他能在xml树状结构中寻找节点。Xpath用于在xml文档中通过元素和属性进行导航

HTML树状结构图

image.png

HMTL的结构就是树形结构,HTML是根节点,所有的其他元素节点都是从根节点发出的。其他的元素都是这棵树上的节点,每个节点还可能有属性和文本。而路径就是指某个节点到另一个节点的路线

节点之间的关系

  • 父节点(Parent) :HTML是body和head节点的父节点;
  • 子节点(Child) :head和body是HTML的子节点;
  • 兄弟节点(Sibling) :拥有相同的父节点,head和body就是兄弟节点。title和div不是兄弟节点,因为他们不是同一个父节点;
  • 祖先节点(Ancestor) :body是form的祖先节点,爷爷辈及以上;
  • 后代节点(Descendant) :form是HTML的后代节点,孙子辈及以下。

Xpath中的绝对路径与相对路径

Xpath中的绝对路径从HTML根节点开始算,相对路径任意节点开始。通过开发者工具,我们可以拷贝到 Xpath 的绝对路径和相对路径代码:

image.png

虽然绝对路径相比之下看起来更加直观,清楚,但是相对路径却更加简便

绝对路径

Xpath 中最直观的定位策略就是绝对路径。

以百度中的输入框和按钮为例,通过拷贝出来的 full Xpath:

/html/body/div[2]/div[1]/div[5]/div/div/form/span[1]/input

绝对路径是从根节点/html开始往下,一层层的表示出来直到需要的节点为止.如果页面元素层级较多,绝对路径明显不是一个明智的选择。

相对路径

除了绝对路径,Xpath 中更常用的方式是相对路径定位方法,以“//”开头。相对路径可以从任意节点开始,一般我们会选取一个可以唯一定位到的元素开始写,可以增加查找的准确性。

//*[@id="kw"]

相对路径定位语法

表达式说明举例
/从根节点开始选取/html/div/span
//从任意节点开始选取//input
.选取当前节点
..选取当前节点的父节点//input/.. 会选取 input 的父节点
@选取属性或者根据属性选取//input[@data] 选取具备 data 属性的 input 元素 //@data 选取所有 data 属性
*通配符,表示任意节点或任意属性

Xpath定位表达式

image.png

1.根据层级进行定位

# 从根节点开始定位
/html/body/form/div

# 从任意节点开始选取
//form/div

2.根据属性进行定位

# 属性定位是通过 @ 符号指定需要使用的属性。
# class属性定位
//form/span[@class="soutu"]

# ID属性定位
//input[@id="su"]

3.使用谓语定位

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语嵌在中括号里[]

主要有数字下标、last()、position()。

注意:Xpath 中的下标从1开始

# 元素下标定位
//form[@id="form"]/span[2]
定位到form标签下的第二个span标签

//form[@id="form"]/span[last()]
定位到form标签下的最后一个span标签

//form[@id="form"]/span[last()-1]
定位到form下的倒数第二个span标签

//form[@id="form"]/span[position()=2]
定位到from 下第二个span标签

//form[@id="form"]/span[position()<2]
定位到form下的 下标大于 2span标签

4.使用部分匹配函数定位(类似于模糊查询)

函数说明举例
contains选取属性或者文本包含某些字符//div[contains(@id, 'data')] 选取 id 属性包含 data 的 div 元素
starts-with选取属性或者文本以某些字符开头//div[starts-with(@id, 'data')] 选取 id 属性以 data 开头的 div 元素
ends-with选取属性或者文本以某些字符结尾//div[ends-with(@id, 'require')] 选取 id 属性以 require 结尾的 div 元素

Xpath实战

import requests
from lxml import etree



def get_response(url):
    headers = {
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    }
    response = requests.get(url,headers=headers)

    return response.text


# with open('链家.html','w',encoding='utf-8')as f:
#     f.write(response)
#
# data = open('链家.html','r',encoding='utf-8').read()

# 根据响应的网页 解析数据
def get_data(data):
    # 获取树形结构  把html转换成etree对象
    html = etree.HTML(data)
    # {title:标题,position:位置,info:信息,price:总价}
    #  [{},{},{}]
    list_info = []

    for horse in html.xpath('//div[@class="info clear"]'):
        dict_info = {}
        # 标题
        title = horse.xpath('./div[1]/a/text()')[0]
        # 位置
        position = '-'.join(horse.xpath('./div[2]/div/a/text()')) 
        # 信息
        info = horse.xpath('./div[3]/div/text()')[0]
        # 总价
        price = horse.xpath('./div[6]/div[1]/span/text()')[0] + '万'
        # 单价
        unitPrice = horse.xpath('./div[6]/div[2]/span/text()')[0]
        # print(unitPrice)

        dict_info['title'] = title
        dict_info['position'] = position
        dict_info['info'] = info
        dict_info['price'] = price
        dict_info['unitPrice'] = unitPrice

        list_info.append(dict_info)
    return list_info

def sava_data(list_info,i):
    with open(f'第{i}页.txt','w',encoding='utf-8')as f:
        f.write(str(list_info))


if __name__ == '__main__':
    num = 10
    for i in range(1,num+1):
        print('-------------------di{}页数据'.format(i))
        url = 'https://cs.lianjia.com/ershoufang/pg{}/'.format(i)
        data = get_response(url)
        list_info = get_data(data)
        # 存储
        sava_data(list_info,i)