爬虫 — Xpath 数据解析

2023-09-15 20:00:00

一、介绍

XPath(XML Path Language)是一种 XML 的查询语言,它能在 XML 树状结构中寻找节点。XPath 用于在 XML 文档中通过元素和属性进行导航

XML 是一种标记语法的文本格式,XPath 可以方便的定位 XML 中的元素和其中的属性值。

lxml 是 Python 中的一个第三方模块,包含了将 HTML 文本转换成 XML 对象和对对象执行 XPath 的功能。

二、使用

1、在终端输入命令:pip install lxml

2、导入:from lxml import etree

3、创建对象:tree = etree.HTML(网页源代码)

4、tree.xpath(XPath 语法)

三、语法

表达式描述
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
/从根节点选取,选择当前元素的下一级
@选取属性
/text当前元素的文本内容
[]、[@]指定元素的索引或者属性特性

网页数据

html = '''
<li>
    <div class="item">
        <div class="pic">
            <em class="">1</em>
            <a href="https://movie.douban.com/subject/1292052/">
                <img width="100" alt="肖申克的救赎" src="https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
            </a>
        </div>
        <div class="info">
            <div class="hd">
                <a href="https://movie.douban.com/subject/1292052/" class="">
                    <span>肖申克的救赎</span>
                    <span>&nbsp;/&nbsp;The Shawshank Redemption</span>
                    <span>&nbsp;/&nbsp;月黑高飞(港)  /  刺激1995(台)</span>
                </a>
                <span class="playable">[可播放]</span>
            </div>
            <div class="bd">
                <p class="">
                    导演: 弗兰克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 Tim Robbins /...<br>
                    1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情
                </p>                       
                <div class="star">
                        <span class="rating5-t"></span>
                        <span class="rating_num" property="v:average">9.7</span>
                        <span property="v:best" content="10.0"></span>
                        <span>2833257人评价</span>
                </div>
                <p class="quote">
                    <span class="inq">希望让人自由。</span>
                </p>
            </div>
        </div>
    </div>
</li>
'''
# 创建对象,参数是指网页源码
tree = etree.HTML(html)

1、//

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

# //:不需要考虑标签位置,直接定位
# 获取 span 标签
span = tree.xpath('//span')  # 返回的是列表,查找所有的 span 标签,并存放到列表当中
print(span)
# 返回值
# [<Element span at 0x2b5be217ec8>, <Element span at 0x2b5be217e88>, <Element span at 0x2b5be217f88>, <Element span at 0x2b5be217fc8>, <Element span at 0x2b5be22e048>, <Element span at 0x2b5be22e0c8>, <Element span at 0x2b5be22e108>, <Element span at 0x2b5be22e148>, <Element span at 0x2b5be22e188>]

# 获取 img 标签
img = tree.xpath('//img')
print(img)
# 返回值
# [<Element img at 0x188dab57ec8>]

2、/

从根节点选取,选择当前元素的下一级。

# /:从根节点选取,选择当前元素的下一级
# 获取 p 标签下的 span 标签
span = tree.xpath('//p/span')
print(span)
# 返回值
# [<Element span at 0x28404367f08>]

3、@

选取属性。

# @:选取属性
# 获取 img 标签的 src 属性值
img_url = tree.xpath('//img/@src')
print(img_url)
# 返回值
# ['https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp']

# 获取 img 标签的 alt 属性值
img_title = tree.xpath('//img/@alt')
print(img_title)
# 返回值
# ['肖申克的救赎']

4、/text

当前元素的文本内容。

# 获取标签里面的文本内容
rank = tree.xpath('//em/text()')
print(rank)
# 返回值
# ['1']

# 获取 class 为 rating_num 标签的内容
span = tree.xpath('//span[@class="rating_num"]/text()')
print(span)
# 返回值
# ['9.7']

5、[]、[@]

指定元素的索引或者属性特性。

title = tree.xpath('//div[@class="hd"]/a/span[3]/text()')
print(title)
# 返回值
# ['\xa0/\xa0月黑高飞(港)  /  刺激1995(台)']

四、练习

1、元组写入

# 导入模块
import csv
from lxml import etree

# 数据
wb_data = """
        <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 = etree.HTML(wb_data)

# 找到所有的标签,并且存放在列表当中
lis = html.xpath('//li')

# 创建列表
lst = []

# 循环获取 li
for li in lis:
    # 链接
    href = li.xpath('./a/@href')[0]  # .代表当前节点的下一级
    # 标题
    texts = li.xpath('./a/text()')[0]
    # 创建元组数据
    tu = (href, texts)
    # 在列表里添加元组数据
    lst.append(tu)
# 打印列表
print(lst)

# 创建表头
head = ('链接', '值')

# 创建文件对象
with open('data1.csv', 'w', encoding='utf-8-sig', newline='') as f:
    # 创建 csv 写入对象
    writer = csv.writer(f)
    # 写入表头
    writer.writerow(head)
    # 写入数据
    writer.writerows(lst)

2、对象写入

# 导入模块
import csv
from lxml import etree

# 数据
wb_data = """
        <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 = etree.HTML(wb_data)

# 找到所有的标签,并且存放在列表当中
lis = html.xpath('//li')

# 创建列表
lst = []

# 循环获取 li
for li in lis:
    # 创建字典对象
    dic = {}
    dic['href'] = li.xpath('./a/@href')[0]  # .代表当前节点的下一级
    dic['texts'] = li.xpath('./a/text()')[0]
    lst.append(dic)

# 打印列表
print(lst)

# 创建表头,key 必须要跟表头保持一致
head = ('href', 'texts')

# 创建文件对象
with open('data2.csv', 'w', encoding='utf-8-sig', newline='') as f:
    # 创建 csv 字典写入对象  fieldnames 固定参数,不可修改
    writer = csv.DictWriter(f, fieldnames=head)  # 设置表头
    # 写入表头
    writer.writeheader()
    # 写入数据
    writer.writerows(lst)

元组写入和对象写入区别:

元组写入:可以自定义表头文字。

对象写入:表头数据必须与对象 key 值保持一致。

五、豆瓣电影信息爬取

目标网站:https://movie.douban.com/top250

需求:获取该网站所有的电影(包括翻页)标题,副标题,电影类型,评分,引言并将数据保存到 csv 表格当中。

页面分析

翻页爬取:先获取第一页数据

1、确定 url:https://movie.douban.com/top250

2、发请求,获取响应:返回网页源代码 — XPath 解析

3、每一组数据 <div class="info"></div> 包含了所需的数据

4、遍历拿详情的数据

电影名称:div[@class=“hd”]/a//text() a 标签下的子孙文本都能获取到

电影类型:div[@class=“bd”]/p/text() 获取到文本内容之后还需要处理

电影评分:div[@class=“bd”]/div[@class=“star”]/span[2]/text()

电影引言:div[@class=“bd”]/p[@class=“quote”]/span/text()

代码实现

# 导入模块
import requests
from lxml import etree

# 第一页目标 url
url = "https://movie.douban.com/top250"

# 请求头
head = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}

# 发请求,获取响应
res = requests.get(url, headers=head)

# 创建对象
html = etree.HTML(res.text)

# 数据解析
divs = html.xpath('//div[@class="info"]')

# 循环遍历拿到每一组数据
for div in divs:  # 每一组数据的获取
    # 标题
    title = div.xpath('./div[@class="hd"]/a//text()')
    titls = ''.join(title).replace(' ', '').replace('\n', '')
    # 电影类型
    types = div.xpath('./div[@class="bd"]/p/text()')[1].split('/')[-1].strip()
    # 电影评分
    star = div.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()')[0]
    # 引言,没有获取到数据,返回是一个空列表
    quote = div.xpath('./div[@class="bd"]/p[@class="quote"]/span/text()')
    # 打印标题,电影类型,电影评分,引言
    print(titls, types, star, quote)

页面分析

翻页

第一页:https://movie.douban.com/top250?start=0&filter=

第二页:https://movie.douban.com/top250?start=25&filter=

第三页:https://movie.douban.com/top250?start=50&filter=

start 参数发生变化

第一页:(1-1)* 25 = 0

第二页:(2-1)* 25 = 25

第三页:(3-1)* 25 = 50

第 n 页:(n-1)* 25

page:(page-1) * 25

f’https://movie.douban.com/top250?start={(page-1) * 25}&filter=’

代码实现

# 导入模块
import requests
from lxml import etree

# 请求头
head = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}

# 翻页
for page in range(1, 11):
    # 目标 url
    url = f'https://movie.douban.com/top250?start={(page-1) * 25}&filter='

    # 发请求,获取响应
    res = requests.get(url, headers=head)

    # 创建对象
    html = etree.HTML(res.text)

    # 数据解析
    divs = html.xpath('//div[@class="info"]')

    # 循环遍历拿到每一组数据
    for div in divs:  # 每一组数据的获取
        # 标题
        title = div.xpath('./div[@class="hd"]/a//text()')
        titls = ''.join(title).replace(' ', '').replace('\n', '')
        # 电影类型
        types = div.xpath('./div[@class="bd"]/p/text()')[1].split('/')[-1].strip()
        # 电影评分
        star = div.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()')[0]
        # 引言,没有获取到数据,返回是一个空列表
        quote = div.xpath('./div[@class="bd"]/p[@class="quote"]/span/text()')
        # 判断是否为空列表
        if quote: # 如果不是就直接取下标值
            quote = quote[0]
        else: # 如果是的,就赋值为空字符串
            quote = ''
        # 打印标题,电影类型,电影评分,引言
        print(titls, types, star, quote)

页面分析

数据已经全部取出

使用 csv 保存数据到表格

代码实现

1、元组写入

# 导入模块
import requests
from lxml import etree
import csv

# 请求头
head = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}

# 数据列表
lst = []

# 翻页
for page in range(1, 11):
    # 目标 url
    url = f'https://movie.douban.com/top250?start={(page-1) * 25}&filter='

    # 发请求,获取响应
    res = requests.get(url, headers=head)

    # 创建对象
    html = etree.HTML(res.text)

    # 数据解析
    divs = html.xpath('//div[@class="info"]')

    # 循环遍历拿到每一组数据
    for div in divs:  # 每一组数据的获取
        # 标题
        title = div.xpath('./div[@class="hd"]/a//text()')
        titls = ''.join(title).replace(' ', '').replace('\n', '')
        # 电影类型
        types = div.xpath('./div[@class="bd"]/p/text()')[1].split('/')[-1].strip()
        # 电影评分
        star = div.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()')[0]
        # 引言,没有获取到数据,返回是一个空列表
        quote = div.xpath('./div[@class="bd"]/p[@class="quote"]/span/text()')
        # 判断是否为空列表
        if quote: # 如果不是就直接取下标值
            quote = quote[0]
        else: # 如果是的,就赋值为空字符串
            quote = ''
        # 标题,电影类型,电影评分,引言组成元组数据
        l = (titls, types, star, quote)
        # 在列表数据中增加元组数据
        lst.append(l)

# 设置表头
head = ('标题', '电影类型', '电影评分', '引言')

# 创建文件对象
with open('douban.csv','w',encoding='utf-8-sig',newline='') as f:
    # 创建 csv 写入对象
    writer = csv.writer(f)
    # 写入表头
    writer.writerow(head)
    # 写入数据
    writer.writerows(lst)

2、对象写入

# 导入模块
import requests
from lxml import etree
import csv

# 请求头
head = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'
}

# 数据列表
lst = []

# 翻页
for page in range(1, 11):
    # 目标 url
    url = f'https://movie.douban.com/top250?start={(page-1) * 25}&filter='

    # 发请求,获取响应
    res = requests.get(url, headers=head)

    # 创建对象
    html = etree.HTML(res.text)

    # 数据解析
    divs = html.xpath('//div[@class="info"]')

    # 循环遍历拿到每一组数据
    for div in divs:  # 每一组数据的获取
        # 创建对象
        dic = {}
        # 标题
        title = div.xpath('./div[@class="hd"]/a//text()')
        dic['titls'] = ''.join(title).replace(' ', '').replace('\n', '')
        # 电影类型
        dic['types'] = div.xpath('./div[@class="bd"]/p/text()')[1].split('/')[-1].strip()
        # 电影评分
        dic['star'] = div.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()')[0]
        # 引言,没有获取到数据,返回是一个空列表
        quote = div.xpath('./div[@class="bd"]/p[@class="quote"]/span/text()')
        # 判断是否为空列表
        if quote: # 如果不是就直接取下标值
            dic['quote'] = quote[0]
        else: # 如果是的,就赋值为空字符串
            dic['quote'] = ''
        # 在列表数据中增加对象数据
        lst.append(dic)

# 设置表头,与对象中的 key 值相同
head = ('titls', 'types', 'star', 'quote')

# 创建文件对象
with open('douban.csv','w',encoding='utf-8-sig',newline='') as f:
    # 创建 csv 写入对象
    writer = csv.DictWriter(f, fieldnames=head)
    # 写入表头
    writer.writeheader()
    # 写入数据
    writer.writerows(lst)

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

更多推荐

Spark 框架概述

目录一、Spark是什么1.1统一分析引擎?二、Spark风雨十年​三、SparkVSHadoop(MapReduce)3.1面试题:Hadoop的基于进程的计算和Spark基于线程方式优缺点?四、Spark四大特点​4.1速度快4.2易于使用4.3通用性强​4.4运行方式五、Spark框架模块5.1介绍5.2Spar

Python+Selenium定位不到元素常见原因及解决办法(报:NoSuchElementException)

这篇文章主要介绍了Python+Selenium定位不到元素常见原因及解决办法(报:NoSuchElementException),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧在做web应用的自动化测试时,定位元素是必不可少的,这个过程经常会碰到定

虹科分享 | 软件供应链攻击如何工作?如何评估软件供应链安全?

说到应用程序和软件,关键词是“更多”。在数字经济需求的推动下,从简化业务运营到创造创新的新收入机会,企业越来越依赖应用程序。云本地应用程序开发更是火上浇油。然而,情况是双向的:这些应用程序通常更复杂,使用的开放源代码比以往任何时候都包含更多的漏洞。此外,威胁行为者正在创造和使用更多的攻击方法和技术,通常是组合在一起的。

华为云云耀云服务器L实例评测|部署功能强大的监控和可视化工具Grafana

应用场景Grafana介绍Grafana是一个功能强大的监控和可视化工具,适用于各种行业和应用场景,如IT运维监控、网络监控、能源管理、金融市场分析等。它提供了灵活的数据源支持、强大的可视化功能和告警机制,以及注释和过滤功能,使得用户能够更好地理解和分析实时数据。下面的监控查询面板都是使用Grafana制作的。一个用于

Rust的注释与文档

rust中//!和///有什么区别?在Rust中,//!和///是特殊注释语法,用于文档注释(DocumentationComments)。它们用于编写文档,并生成Rust代码的API文档。//!用于编写模块级别的文档注释,通常放置在模块的开头。它允许您编写与整个模块相关的文档。这些注释会被Rust编译器解析,生成与模

zookeeper集群

一,zookeeper定义Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。二,zookeeper工作机制Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zooke

论文笔记 DETR

detr摘要和引言2020论文facebook不需要proposal,不需要基于anchor的先验知识(比如预训练的模型),也不需要NMS进行筛选,直接端到端不需要后处理利用transformer的全局建模能力,看成集合预测问题,不会输出很多冗余的框,直接端到端,不需要NMS,简化了训练和部署NMS:非极大值抑制,抑制

Web3.0时代什么时候到来,Web3.0有什么机会?

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。🎉欢迎👍点赞✍评论⭐收藏文章目录🚀一、前言🚀二、Web1.0-Web3.0🔎2.1Web1.0:信息的获

穿越时空的创新:解析云原生与Web3.0的奇妙渊源

🌷🍁博主猫头虎带您GotoNewWorld.✨🍁🦄博客首页——猫头虎的博客🎐🐳《面试题大全专栏》文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大

Web3.0实战(02)-联盟链入门讲解

联盟链是介于公有链和私有链之间,具备部分去中心化的特性。联盟链是由若干机构联合发起,由盟友共同来维护,它只针对特定某个群体的成员和有限的第三方开放。8.1部分去中心化联盟链只属于联盟内部的成员所有,联盟链的节点数是有限所以容易达成共识。8.2可控性较强公有链是一旦区块链形成,将不可篡改,这主要源于公有链的节点一般是海量

Linux CentOS7 history命令

linux查看历史命令可以使用history命令,该命令可以列出所有已键入的命令。这个命令的作用可以让用户或其他有权限人员,进行审计,查看已录入的命令。用户所键入的命令作为应保存的信息将记录在文件中,这个文件就是家目录中的一个隐藏文件~/.bash_history。了解历史命令存放的位置,对后面的各项讨论十分重要。我们

热文推荐