Python使用xpath来解析html响应

一、XPath

XPath即为XML路径语言,它是一种用来确定XML(标准通用标记语言的子集)文档中某部分位置的语言。XPath基于XML的树状结构,有不同类型的节点,包括元素节点,属性节点和文本节点,提供在数据结构树中找寻节点的能力。起初 XPath 的提出的初衷是将其作为一个通用的、介于XPointer与XSLT间的语法模型。但是 XPath 很快的被开发者采用来当作小型查询语言。

二、XPath常用规则

表达式

描述

nodename

选中该元素。

/

从根节点选取、或者是元素和元素间的过渡。

//

从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

.

选取当前节点。

..

选取当前节点的父节点。

@

选取属性。

text()

选取文本。

三、lxml的安装使用

在前面介绍了xpath的语法,那么在python爬虫代码中我们如何使用xpath呢? 对应的我们需要lxml

  • 安装
pip install lxml
3.1 lxml模块的入门使用

假设我们现有如下的html字符串,尝试对它进行操作。

<div> <ul> 
<li class="item-1"><a href="link1.html">first itema>li> 
<li class="item-1"><a href="link2.html">second itema>li> 
<li class="item-inactive"><a href="link3.html">third itema>li> 
<li class="item-1"><a href="link4.html">fourth itema>li> 
<li class="item-0"><a href="link5.html">fifth itema> # 注意,此处缺少一个 li> 闭合标签 
ul> div>

下面是一个简单的例子

from lxml import etree
text = ''' <div> <ul> 
        <li class="item-1"><a href="link1.html">first itema>li> 
        <li class="item-1"><a href="link2.html">second itema>li> 
        <li class="item-inactive"><a href="link3.html">third itema>li> 
        <li class="item-1"><a href="link4.html">fourth itema>li> 
        <li class="item-0"><a href="link5.html">fifth itema> 
        ul> div> '''

html = etree.HTML(text)
print(type(html)) 

handeled_html_str = etree.tostring(html).decode()
print(handeled_html_str)

输出为

<class 'lxml.etree._Element'>
<html><body><div> <ul> 
        <li class="item-1"><a href="link1.html">first itema>li> 
        <li class="item-1"><a href="link2.html">second itema>li> 
        <li class="item-inactive"><a href="link3.html">third itema>li> 
        <li class="item-1"><a href="link4.html">fourth itema>li> 
        <li class="item-0"><a href="link5.html">fifth itema> 
        li>ul> div> body>html>

可以发现,lxml确实能够把确实的标签补充完成,但是请注意lxml是人工写的,很多时候由于网页不够规范,或者是lxml的bug,即使参考url地址对应的响应去提取数据,仍然获取不到,这个时候我们需要使用etree.tostring的方法,观察etree到底把html转化成了什么样子,即根据转化后的html字符串去进行数据的提取。

3.2 lxml的深入练习

接下来我们继续操作,假设每个class为item-1的li标签是1条新闻数据,如何把这条新闻数据组成一个字典。

from lxml import etree
text = ''' <div> <ul> 
        <li class="item-1"><a href="link1.html">first itema>li> 
        <li class="item-1"><a href="link2.html">second itema>li> 
        <li class="item-inactive"><a href="link3.html">third itema>li> 
        <li class="item-1"><a href="link4.html">fourth itema>li> 
        <li class="item-0"><a href="link5.html">fifth itema> 
        ul> div> '''

html = etree.HTML(text)

#获取href的列表和title的列表
href_list = html.xpath("//li[@class='item-1']/a/@href")
title_list = html.xpath("//li[@class='item-1']/a/text()")

#组装成字典
for href in href_list:
    item = {}
    item["href"] = href
    item["title"] = title_list[href_list.index(href)]
    print(item)

输出为:

{'href': 'link1.html', 'title': 'first item'}
{'href': 'link2.html', 'title': 'second item'}
{'href': 'link4.html', 'title': 'fourth item'}

假设在某种情况下,某个新闻的href没有,那么会怎样呢?
结果是:

{'href': 'link2.html', 'title': 'first item'}
{'href': 'link4.html', 'title': 'second item'}

四、获取豆瓣电影top250

获取电影的名称评分以及简介

import requestsfrom lxml import etreeimport time#分析网址规律start_url = "https://movie.douban.com/top250?start="num = 1page = 0headers = {    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36",}while page <250:    url = start_url + str(page)    response = requests.get(url,headers=headers)    html = etree.HTML(response.text)    # 获取class为info下所有的节点信息    nodes = html.xpath("//div[@class='info']")    for node in nodes:        dic_data = {}        dic_data["id"] = num        # 获取电影名称        dic_data["name"] = "".join(node.xpath('./div/a/span[1]/text()'))        # 获取电影评分        dic_data["score"] = node.xpath('.//span[@]/text()')[0]        # 获取电影简介,如果没有输出nothing        if node.xpath('.//span[@]/text()'):            dic_data["intro"] = node.xpath('.//span[@]/text()')[0]        else:            dic_data["intro"] ='nothing'        print(dic_data)        num+=1    #每页只有25条数据    page = page + 25    #爬完一页休息1秒    time.sleep(1)