爬虫数据-xpath

146 阅读8分钟

声明:所有案例均仅供个人学习使用

为什么要学习XPATH和LXML类库

lxml是⼀款⾼性能的Python HTML/XML解析器,我们可以利⽤Xpath来快速的 定位特定元素以及获取节点信息

什么是XPATH

Xpath是⼀⻔在HTML/XML⽂档中查找信息的语⾔,可⽤来在HTML/XML⽂档 中对元素和属性进⾏遍历

文档:www.w3school.com.cn/xpath/index…

认识XML

数据格式描述设计目标
XML可扩展标记语言被设计为传输和存储数据,其焦点是数据的内容
HTML超文本标记语言显示数据以及如何更好显示

XML的节点关系

节点的概念:每个XML的标签我们都称之为节点

<book>
    <title>hello world<title>
    <author>yantong<author>
    <year>2024<year>
<book>

节点选择语法

XPath使⽤路径表达式来选取XML⽂档中的接待或者节点集。这些路径表达式 和我们在常规的电脑⽂件系统中看到的表达式⾮常相似。

表达式描述
/从根节点选取
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
.选取当前节点
..选取当前接到的父节点
@选取

查找某个特定的节点或者包含某个指定的值的节点

路径表达式结果
/bookstore/book[1]选取属于bookstore子元素的第一个book元素
/bookstore/book[last()]选取属于bookstore子元素的最后一个book元素
/bookstore/book[last()-1]选取属于 bookstore ⼦元素的倒数第二个book元素
/bookstore/book[position()<3]选取最前⾯的两个属于 bookstore 元素的⼦元素的 book 元素
//title[@lang]选取所有拥有名为 lang 的属性的title 元素
//title[@lang='eng']选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book元素,且其中的 price 元素的值须⼤于 35.00。

选择未知节点

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

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

列出了⼀些路径表达式,以及这些表达式的结果

路径表达式结果
/bookstore/*选取bookstore元素的所有子元素
//*选取文档中的所有元素
html/node()/meta/@*选择html下面任意节点下的meta节点的所有属性
//title[@*]选取所有带有属性的title元素

xpath的更多语法:learn.microsoft.com/zh-cn/previ…

lxml库

  • lxml 是 ⼀个HTML/XML的解析器,主要的功能是如何解析和提取HTML/XML 数据
  • ⽤etree.HTML,将字符串转化为Element对象
  • lxml python 官⽅⽂档:lxml.de/index.html
  • 可使⽤ pip 安装: pip install lxml (或通过wheel⽅式安装)
  • lxml 可以⾃动修正 html 代码
# -*- coding: UTF-8 -*-
#    @Project :网络爬虫
#    @File    :lxml练习.py
#    @IDE     :PyCharm
#    @Author  :艳烔
#    @Date    :2024/10/25 17:06

import requests
from lxml import etree


class LxmlPractice():
    def __init__(self, url):
        self.url = url
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
        }

        # 获取页面源码

    def get_html(self):
        try:
            response = requests.get(self.url, headers=self.headers)
            response.raise_for_status()  # 检查请求是否成功
            return response.text
        except requests.RequestException as e:
            print(f"请求失败: {e}")
            return None

            # 解析页面源码

    def parse_html(self, html):
        if html is not None:
            etree_html = etree.HTML(html)
            return etree_html
        else:
            return None

    def get_data(self, parse_html):

        data = []
        # 获取电影标题 上映时间
        for i in range(1,11):
            data_dict = {}
            xpath_expression_title = f'/html/body/div/div[2]/div[1]/div[1]/div[{i}]/div/div/div[2]/a/h2'
            xpath_expression_time = f'/html/body/div/div[2]/div[1]/div[1]/div[{i}]/div/div/div[2]/div[3]/span'
            h2_elements = parse_html.xpath(xpath_expression_title)
            time_elements = parse_html.xpath(xpath_expression_time)
            try:
                h2_content =h2_elements[0].text.strip()
                time_content = time_elements[0].text.strip()
            except:
                h2_content = ''
                time_content = ''
            # print(time_content)
            # print(h2_content)
            data_dict['title'] = h2_content
            data_dict['time'] = time_content.replace(' 上映','')
            data.append(data_dict)
        return data


    def run(self):
        html = self.get_html()
        if html:
            parse_html = self.parse_html(html)
            data = self.get_data(parse_html)
            print(data)
        else:
            print("未能获取页面源码")


if __name__ == '__main__':
    url = 'https://ssr1.scrape.center/page/1'  # 注意:这个URL可能需要替换为实际存在的页面
    lxmlPractice = LxmlPractice(url)
    lxmlPractice.run()

Beautliful Soup

Beautilful Soup的简介

Beautiful Soup是python的⼀个库,最主要的功能是从⽹⻚抓取数据,官⽅解释如下:

  • Beautiful Soup提供⼀些简单的、python式的函数⽤来处理导航、搜索、 修改分析树等功能。它是⼀个⼯具箱,通过解析⽂档为⽤户提供需要抓取 的数据,因为简单,所以不需要多少代码就可以写出⼀个完整的应⽤程 序。
  • Beautiful Soup⾃动将输⼊⽂档转换为Unicode编码,输出⽂档转换为 utf-8编码。你不需要考虑编码⽅式,除⾮⽂档没有指定⼀个编码⽅式,这 时,Beautiful Soup就不能⾃动识别编码⽅式了。然后,你仅仅需要说明 ⼀下原始编码⽅式就可以了。
  • Beautiful Soup已成为和lxml⼀样出⾊的python解释器,为⽤户灵活地提 供不同的解析策略或强劲的速度。

GitHub地址:beautifulsoup.readthedocs.io/zh_CN/v4.4.…

和lxml⼀样,BeautifulSoup也是⼀个HTML/XML的解析器,主要功能也是如何解析和提取HTML/XML数据。

lxml只会局部遍历,⽽BeautifulSoup是基于HTML DOM的,会载⼊整个⽂ 档,解析整个DOM树,因此时间和内存开销都会很⼤,所以性能要低于lxml BeautifulSoup⽤来解析HTML⽐较简单,⽀持CSS选择器,Python标准库中 的HTML解析器,也⽀持lxml的XML解析器。

BeautifulSoup安装 pip install bs4

解析工具对比

抓取工具速度使用难度
正则最快困难
lxml简单
Beautiful Soup最简单
# -*- coding: UTF-8 -*-  
# @Project :网络爬虫  
# @File :BeautifulSoup的基本使用_01.py  
# @IDE :PyCharm  
# @Author :艳烔  
# @Date :2024/10/26 9:03  
  
from bs4 import BeautifulSoup  
  
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  
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,  
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>  
<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>  
"""  
# 创建BeautifulSoup对象  
soup = BeautifulSoup(html_doc, 'html.parser')  
  
# 格式化输出 soup 对象的内容  
print(soup.prettify())

解析器

图片.png

搜索文档树 Beautiful Soup定义了很多搜索⽅法,这⾥着重介绍2个: find() 和 find_all() .其 它⽅法的参数和⽤法类似,

# -*- coding: UTF-8 -*-
#    @Project :网络爬虫 
#    @File    :BeautifulSoup搜索文档树_02.py
#    @IDE     :PyCharm 
#    @Author  :艳烔
#    @Date    :2024/10/26 9:11

from bs4 import BeautifulSoup

html_doc = '''
<table class="tablelist" cellspacing="0" cellpadding="0">
    <tbody>
        <tr class="h">
            <td class="1" width="374">职位名称</td>
            <td>职位类别</td>
            <td>⼈数</td>
            <td>地点</td>
            <td>发布时间</td>
        </tr>
        <tr class="even">
            <td class="l"><a href="https://www.baidu.com">区块链⾼级研发⼯
            <td class="l">技术类</td>
            <td class="l">1</td>
            <td class="l">深圳</td>
            <td class="l">2018-11-25</td>
        </tr>
        <tr>
            <td><a href="https://www.qq.com">⾦融云⾼级后台开发</a></td>
            <td>技术类</td>
            <td>2</td>
            <td>深圳</td>
            <td>2018-11-24</td>
        </tr>
        <tr>
            <td><a href="https://www.juran.com">⾼级研发⼯程师</a></td>
            <td>技术类</td>
            <td>2</td>
            <td>深圳</td>
            <td>2018-11-24</td>
        </tr>
        <tr>
            <td><a href="https://www.python.com">⾼级图像算法⼯程师</a></t
            <td>技术类</td>
            <td>2</td>
            <td>深圳</td>
            <td>2018-11-24</td>
        </tr>
        <tr>
            <td><a href="https://www.lg.com" id="test" class="test">⾼级
            <td>技术类</td>
            <td>2</td>
            <td>深圳</td>
            <td>2018-11-24</td>
        </tr>
    </tbody>
</table>
'''

soup = BeautifulSoup(html_doc, 'lxml')

# 格式化输出
# print(soup.prettify())
# 1. 获取所有tr标签
tr_find = soup.find_all('tr')
print(tr_find)
for tr in tr_find:
    print(tr)
    print("="*30)

# 2. 获取第二个tr标签
tr_find_2 = soup.find_all('tr',limit=2)[1]
print(tr_find_2)

# 3. 获取所有class等于even的tr标签
tr_class_even = soup.find_all('tr', class_='even')
print(tr_class_even)
# 4. 将所有id等于test,class也等于test的a标签提取出来
trs = soup.find_all('a', attrs={'id': "test", 'class': "test"})
for tr in trs:
    print(tr)
    print("=" * 30)

# 5. 将所有的a标签href属性
hrefs = soup.find_all('a')
for a in hrefs:
    # print(a)
    # 通过下标的方式获取
    # print(a['href'])
    print(a.get('href'))

    # 通过attrs属性的方式
    # print(a.attrs['href'])
    print("=" * 30)


# 6. 获取所有的职位信息(纯文本)
trs = soup.find_all('tr')[1:]
for tr in trs:
    # print(tr)
    # tds = tr.find_all('td')
    # print(tds)
    # title = tds[0]
    # print(title.string)

    # 获取tr标签的所有文本
    # infos = list(tr.strings)
    # print(infos[1])

    # 去除空格去纯文本
    infos = list(tr.stripped_strings)
    print(infos)

    pass

CSS常用选择器介绍

# 通过标签名查找  
print(soup.select('a'))  
  
print('+'*30)  
  
# 通过类名查找  
print(soup.select('.even'))  
  
print('+'*30)  
  
# 通过id查找  
print(soup.select("#test"))  
  
print('+'*30)  
  
# 组合查找  
print(soup.select("tr #link"))  
print("="*30)  
# 直接子标签查找,则使用 > 分隔  
print(soup.select("tr > td"))  
  
print('+'*30)  
  
# 通过属性查找  
print(soup.select('a[href="https://www.baidu.com"]'))

# 1.获取所有tr标签  
print(soup.select('tr'))  
print("-"*30)  
# 2.获取第2个tr标签  
print(soup.select('tr')[1])  
print("-"*30)  
  
# 3.获取所有class等于even的tr标签  
print(soup.select("tr.even"))  
print("-"*30)  
  
# 4.获取所有的a标签的href属性  
alist = soup.select("a")  
for a in alist:  
    print(a.get('href'))  
print("-"*30)  
  
# 5.获取所有的职位信息(纯⽂本)  
trs = soup.select('tr')  
  
for tr in trs:  
infos = list(tr.stripped_strings)  
print(infos)

案例一 抓取图片

# -*- coding: UTF-8 -*-
#    @Project :网络爬虫 
#    @File    :爬取 爱二次元 网 图片.py
#    @IDE     :PyCharm 
#    @Author  :艳烔
#    @Date    :2024/10/26 10:35
from bs4 import BeautifulSoup
import requests
import re
from retrying import retry
from time import sleep


class AiErCiYuan():
    def __init__(self, url):
        self.url = url
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
        }
        self.originUrl = 'https://a2cy.com'

    @retry(stop_max_attempt_number=3)
    def get_html(self):
        """
        获取网页源码
        :return: 返回网页源码
        """
        response = requests.get(self.url, headers=self.headers)
        print(response.status_code)
        assert response.status_code == 200
        response.encoding = 'UTF-8'
        html = response.text
        return html

    def save_img(self, url, title):
        """
        根据 图片 URL 保存到本地
        :param url: 图片 URL
        :return:
        """
        response = requests.get(url, title, headers=self.headers)
        sleep(0.05)
        with open(f'E:/Study/code/Python/图片爬取/爱二次元/{title}.jpg', 'wb') as f:
            content = response.content
            try:
                f.write(content)
                print(title + " 保存成功 url :" + url)
            except:
                print(title + "\033[31m{}\033[0m".format("保存失败!!!") + " url:" + url)

    def get_img_info(self):
        """
        获取图片的链接和标题
        :return:
        """
        html_info = self.get_html()
        soup = BeautifulSoup(html_info, 'lxml')
        divs = soup.select("div.showImg")[6:]
        for div in divs:
            img = div.select("img")[0]
            # 获取图片名称
            sub_res = re.sub(r'【.*?】', '', img.get('alt'))
            pattern = re.compile('[\u4e00-\u9fa5a-zA-Z]+')
            img_title = ''.join(pattern.findall(sub_res))
            # print(img_title)
            # 获取图片链接
            img_url = self.originUrl + img.get('data-loadsrc')

            # 保存图片到本地
            self.save_img(img_url, img_title)

    def run(self):
        try:
            print("爬虫程序启动...  爬取网站:" + self.url)
            self.get_img_info()
        except Exception as e:
            print(f"出错了:{e}")
        finally:
            print("爬虫程序关闭!")


if __name__ == '__main__':
    # https://a2cy.com/acg/cos/index_2.html
    url = 'https://a2cy.com/acg/cos/'
    for i in range(1, 112):
        if i != 1:
            url += 'index_{}.html'.format(i)
        aierciyuan = AiErCiYuan(url)
        aierciyuan.run()
        url = 'https://a2cy.com/acg/cos/'