Python爬虫系列教程之第十三篇:构建高可用爬虫系统 —— 混合架构与自动化监控

news2025/2/22 5:55:10

大家好,欢迎继续关注本系列爬虫教程!随着爬虫项目规模的不断扩大和业务需求的提升,单一技术方案往往难以满足实际应用中对高可用性稳定性自动化监控的要求。如何构建一个既能应对多种反爬策略,又能在异常情况下自动恢复、实时监控运行状态的高可用爬虫系统,成为每个爬虫工程师必须面对的问题。

在本篇博客中,我们将从以下几个方面详细讲解如何构建高可用爬虫系统:

  • 系统架构设计:规划整体模块划分和技术组合
  • 混合爬虫技术整合:如何将 Scrapy、Selenium、异步请求等技术有机结合
  • 性能优化与资源管理:如何提升爬虫抓取效率并降低资源消耗
  • 自动化监控与报警:利用日志、定时任务和报警机制实时掌握爬虫状态
  • 容错与自动重启机制:保证在异常情况下系统能够自动恢复

接下来,我们将逐步展开讲解。


1. 系统架构设计

构建一个高可用爬虫系统,需要合理设计各个模块,使各部分功能清晰、职责明确。通常,一个高可用爬虫系统可以划分为以下模块:

  • 任务调度模块:管理 URL 队列、任务分发和节点协调(如使用 Redis、RabbitMQ 等实现分布式队列)。
  • 数据采集模块:根据目标页面特点,采用不同的抓取方式。对于静态页面可用 Scrapy/requests,对于动态页面可使用 Selenium 或 Puppeteer。
  • 数据解析与存储模块:对抓取到的原始数据进行解析、清洗,并存储到数据库或文件中(如 MySQL、MongoDB、Elasticsearch)。
  • 日志与异常监控模块:实时记录爬虫运行日志,捕获异常信息,便于问题排查和系统维护。
  • 自动化监控与报警模块:利用监控脚本或第三方工具,定时检测爬虫健康状态,当出现异常时通过邮件、短信或微信等方式报警。
  • 容错与重启模块:通过 Supervisor、systemd、Docker 等工具实现进程监控,确保爬虫在异常退出后能够自动重启,保证任务不中断。

这样的模块划分不仅使系统结构清晰,还便于后续的扩展和维护。


2. 混合爬虫技术整合

实际应用中,目标网站可能会有不同的页面类型和反爬策略。一个高可用的爬虫系统往往需要根据不同情况选择合适的抓取方式。下面提供一个混合爬虫的示例,展示如何根据 URL 特征选择使用传统 requests 异步请求或 Selenium 模拟浏览器抓取动态内容。

2.1 混合爬虫示例代码

下面代码展示了一个简单的混合爬虫框架:

  • 对于静态页面,采用 aiohttp 异步请求加速抓取;
  • 对于需要动态渲染的页面,调用 Selenium 方法进行抓取;

整个爬虫在每个请求中都集成了异常处理与日志记录。

import asyncio
import aiohttp
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import logging
import time

# ---------------------------
# 日志配置:所有运行信息写入hybrid_crawler.log文件
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='hybrid_crawler.log',
    filemode='a'
)

# ---------------------------
# Selenium配置:用于处理动态页面
def init_selenium():
    chrome_options = Options()
    chrome_options.add_argument("--headless")        # 无头模式,不弹出浏览器窗口
    chrome_options.add_argument("--disable-gpu")       # 禁用GPU加速
    chrome_options.add_argument("--no-sandbox")        # 解决权限问题
    driver = webdriver.Chrome(executable_path="path/to/chromedriver", options=chrome_options)
    return driver

def fetch_dynamic_page(url, driver):
    """
    使用Selenium抓取动态页面
    :param url: 目标网页URL
    :param driver: Selenium WebDriver对象
    :return: 页面HTML内容
    """
    try:
        driver.get(url)
        # 根据页面复杂程度,等待足够时间
        time.sleep(3)
        html = driver.page_source
        logging.info(f"Selenium 成功抓取动态页面: {url}")
        return html
    except Exception as e:
        logging.error(f"Selenium 抓取失败: {url} - {e}")
        return None

# ---------------------------
# 异步请求:用于处理静态页面
async def fetch_static_page(url, session):
    """
    使用aiohttp异步请求获取页面内容
    :param url: 目标网页URL
    :param session: aiohttp ClientSession对象
    :return: 页面HTML内容或None
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)"
    }
    try:
        async with session.get(url, headers=headers, timeout=10) as response:
            response.raise_for_status()
            html = await response.text()
            logging.info(f"aiohttp 成功抓取静态页面: {url}")
            return html
    except Exception as e:
        logging.error(f"aiohttp 抓取失败: {url} - {e}")
        return None

# ---------------------------
# 根据URL判断采用哪种抓取方式
async def fetch_page(url, driver, session):
    """
    根据URL特征判断采用动态或静态抓取方式
    :param url: 目标网页URL
    :param driver: Selenium WebDriver对象,用于动态抓取
    :param session: aiohttp ClientSession对象,用于静态抓取
    :return: 页面HTML内容或None
    """
    # 假设包含"dynamic"的URL需要动态渲染,否则使用静态请求
    if "dynamic" in url:
        logging.info(f"使用Selenium抓取: {url}")
        return fetch_dynamic_page(url, driver)
    else:
        logging.info(f"使用aiohttp抓取: {url}")
        return await fetch_static_page(url, session)

# ---------------------------
# 主函数:整合混合爬虫逻辑
async def main():
    # 示例URL列表:部分为静态页面,部分为动态页面(模拟)
    urls = [
        "https://www.example.com/static/page1",
        "https://www.example.com/static/page2",
        "https://www.example.com/dynamic/page1",  # 模拟需要动态渲染的页面
        "https://www.example.com/static/page3",
        "https://www.example.com/dynamic/page2"
    ]
    
    # 初始化Selenium WebDriver,用于动态页面抓取
    driver = init_selenium()
    
    # 创建aiohttp ClientSession,用于异步静态页面抓取
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_page(url, driver, session) for url in urls]
        pages = await asyncio.gather(*tasks)
        
    # 关闭Selenium浏览器
    driver.quit()
    
    # 对抓取结果进行简单处理(例如打印页面标题)
    for idx, html in enumerate(pages):
        if html:
            # 这里只是示例,实际项目中可使用BeautifulSoup等进一步解析
            logging.info(f"第 {idx+1} 个页面抓取成功,长度:{len(html)}")
            print(f"页面 {idx+1} 抓取成功,内容长度:{len(html)}")
        else:
            logging.warning(f"第 {idx+1} 个页面抓取失败")
            print(f"页面 {idx+1} 抓取失败")

# ---------------------------
# 启动混合爬虫
if __name__ == '__main__':
    try:
        asyncio.run(main())
        logging.info("混合爬虫任务全部完成")
    except Exception as e:
        logging.critical(f"混合爬虫系统崩溃: {e}")

2.2 代码说明

  • 混合抓取策略:函数 fetch_page 根据 URL 中是否包含关键字 "dynamic" 决定采用 Selenium 或 aiohttp 的抓取方式。实际项目中,可以根据 URL 正则匹配或页面特征进行判断。
  • 异步抓取:利用 asyncio.gather 同时启动多个异步任务,提高静态页面的抓取速度。
  • 异常处理与日志记录:在每个请求和抓取过程中,都集成了 try...except 结构,并使用 logging 模块记录详细信息,确保出错时可以快速定位问题。

3. 性能优化与资源管理

构建高可用爬虫系统时,性能优化与资源管理同样至关重要。下面介绍几种常见的优化策略:

3.1 限制并发数和请求频率

  • 异步请求的并发控制:使用 asyncio.Semaphore 限制同时运行的请求数,避免因过多并发导致系统内存和带宽压力过大。

    semaphore = asyncio.Semaphore(10)  # 限制同时最多10个并发请求
    
    async def limited_fetch(url, driver, session):
        async with semaphore:
            return await fetch_page(url, driver, session)
    
  • 请求间隔:在抓取过程中加入延时,防止目标网站因请求频率过高而封禁 IP。

3.2 内存与资源泄露检测

  • 定期监控 Python 进程的内存占用情况,使用工具如 psutil 或通过日志记录进行分析。
  • 在使用 Selenium 或数据库连接后,务必确保资源释放(调用 driver.quit()、关闭数据库连接等)。

3.3 缓存与去重

  • 缓存策略:对已抓取页面进行缓存,避免重复请求,提高爬虫整体效率。
  • URL 去重:使用 Redis 或 Bloom Filter 等技术,对任务队列中的 URL 进行去重,防止重复抓取。

4. 自动化监控与报警

高可用爬虫系统必须具备自动监控和报警功能,以便在系统异常或任务失败时能够及时通知运维人员。以下是两种常见的实现方法:

4.1 基于日志的监控

利用前面集成的 logging 模块,将所有关键信息写入日志文件。再通过定时任务(如 crontab)定期扫描日志文件,分析错误和警告信息。当错误次数超过一定阈值时,自动发送报警邮件或短信。

例如,利用 Python 的 smtplib 发送报警邮件:

import smtplib
from email.mime.text import MIMEText

def send_alert_email(subject, content):
    sender = "your_email@example.com"
    receivers = ["admin@example.com"]
    msg = MIMEText(content, "plain", "utf-8")
    msg["Subject"] = subject
    msg["From"] = sender
    msg["To"] = ", ".join(receivers)
    
    try:
        smtp = smtplib.SMTP("smtp.example.com", 25)
        smtp.login("your_email@example.com", "your_email_password")
        smtp.sendmail(sender, receivers, msg.as_string())
        smtp.quit()
        logging.info("报警邮件发送成功")
    except Exception as e:
        logging.error(f"报警邮件发送失败: {e}")

可以将此函数集成到日志分析脚本中,当检测到错误日志异常增多时自动调用发送报警邮件。

4.2 第三方监控平台

使用成熟的监控平台(如 Prometheus、Grafana、ELK Stack)对爬虫服务器进行监控:

  • Prometheus + Grafana:收集 CPU、内存、网络等系统指标,并通过 Grafana 展示实时仪表盘。
  • ELK Stack:利用 Logstash 和 Kibana 对日志数据进行集中管理和分析,及时发现异常。

5. 容错与自动重启机制

为了保证系统在出现异常时能够持续运行,高可用爬虫系统通常需要具备容错和自动重启能力。常见的实现方法包括:

  • 使用 Supervisor 或 systemd:在 Linux 环境下,利用 Supervisor 或 systemd 配置爬虫进程监控,当进程意外退出时自动重启。
  • Docker 容器化部署:将爬虫打包成 Docker 镜像,利用 Docker 的重启策略(如 --restart=always)保证容器异常退出后自动恢复。
  • 分布式任务调度系统:采用分布式任务调度系统(如 Celery、RQ),当某个任务失败时自动重新分配,确保全局任务完成率。

6. 小结

在本篇博客中,我们详细介绍了如何构建一个高可用爬虫系统,内容涵盖了系统架构设计、混合爬虫技术整合、性能优化、自动化监控与报警以及容错自动重启机制。主要要点包括:

  • 模块化设计:将任务调度、数据采集、数据解析、日志监控等模块进行划分,各司其职,确保系统的灵活性和扩展性。
  • 混合技术整合:根据目标页面特点选择合适的抓取方式,利用异步请求和 Selenium 模拟浏览器相结合,提高数据采集效率。
  • 性能优化:采用并发控制、请求间隔、缓存与去重等策略,降低资源消耗并提高系统响应速度。
  • 自动化监控与报警:利用日志记录、定时任务和第三方监控平台,对系统运行状态进行实时监控,并在异常时及时报警。
  • 容错与自动重启:使用进程监控工具和容器化部署,实现爬虫在异常情况下的自动恢复,确保任务不中断。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2303238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解 JVM 的栈帧结构

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…

[oeasy]python068_异常处理之后做些什么_try语句的完全体_最终_finally

068_异常处理之后做些什么_finally 异常处理之后做些什么_try语句的完全体_最终_finally 回忆上次内容 我们了解了 try 的细节 except 可以 捕获到异常 但报错比较简单 添加图片注释,不超过 140 字(可选) 游乐场里面的 报错 更全 更丰富 …

PySide6学习专栏(四):用多线程完成复杂计算任务

如果计程序中要处理一个非常庞大的数据集中的数据,且数据处理计算很复杂,造成数据处理占用大量时间和CPU资源,如果不用多线程,仅在主进程中来处理数据,将会使整个程序卡死,必须采用多线程来处理这些数据是唯…

神经网络八股(1)

1.什么是有监督学习,无监督学习 有监督学习是带有标签的,无监督学习是没有标签的,简单来说就是有监督学习的输入输出都是固定的,已知的,无监督学习输入是已知的,输出是不固定的,无监督学习是通…

深度学习每周学习总结Y1(Yolov5 调用官方权重进行检测 )

🍨 本文为🔗365天深度学习训练营 中的学习记录博客Y1中的内容 🍖 原作者:K同学啊 | 接辅导、项目定制 ** 注意该训练营出现故意不退押金,恶意揣测偷懒用假的结果冒充真实打卡记录,在提出能够拿到视频录像…

计算机视觉基础|从 OpenCV 到频域分析

一、引言 在当今数字化时代,图像处理已渗透到我们生活的方方面面,从日常使用的智能手机拍照美化,到医学领域的精准诊断,再到自动驾驶中的环境感知,其重要性不言而喻。在图像处理领域中,OpenCV 和频域分析&…

74. 搜索二维矩阵(LeetCode 热题 100)

题目来源; 74. 搜索二维矩阵 - 力扣(LeetCode) 题目内容: 给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &am…

netcore libreoffice word转pdf中文乱码

一、效果 解决: cd /usr/share/fonts/ mkdir zhFont cd zhFont #windows系统C:\Windows\Fonts 中复制/usr/share/fonts/zhFont sudo apt update sudo apt install xfonts-utils mkfontscale mkfontdir #刷新字体缓存 fc-cache -fv #查看已安装的字体列表 fc-list :…

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过…

OpenGL 01--构建GLFW、创建第一个工程、配置GLAD

一、OpenGL介绍 一般它被认为是一个API(Application Programming Interface, 应用程序编程接口),包含了一系列可以操作图形、图像的函数。然而,OpenGL本身并不是一个API,它仅仅是一个由Khronos组织制定并维护的规范(Specification)。 OpenGL规…

【时时三省】(C语言基础)求多项式1-1/2+1/3-1/4+...+1/99-1/100的值 用C语言表示

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 示例: 输出结果是 0.688172

kafka-集群缩容

一. 简述: 当业务增加时,服务瓶颈,我们需要进行扩容。当业务量下降时,为成本考虑。自然也会涉及到缩容。假设集群有 15 台机器,预计缩到 10 台机器,那么需要做 5 次缩容操作,每次将一个节点下线…

Ubuntu22.04 - etcd的安装和使用

目录 介绍安装Etcd安装etcd的客户端使用 介绍 Etcd 是一个 golang 编写的分布式、高可用的一致性键值存储系统,用于配置共享和服务发现等。它使用 Raft 一致性算法来保持集群数据的一致性,且客户端通过长连接watch 功能,能够及时收到数据变化…

排查JVM的一些命令

查看JVM相关信息的方法 环境&#xff1a; Win10, jdk17 查看端口的Pid netstat -ano | findstr <端口号>列出当前运行的JVM进程 ## 用于输出JVM中运行的进程状态信息。通过jps&#xff0c;可以快速获取Java进程的PID&#xff08;进程标识符&#xff09;&#xff0c; …

Arduino 第十六章:pir红外人体传感器练习

Arduino 第十六章&#xff1a;PIR 传感器练习 一、引言 在 Arduino 的众多有趣项目中&#xff0c;传感器的应用是非常重要的一部分。今天我们要学习的主角是 PIR&#xff08;被动红外&#xff09;传感器。PIR 传感器能够检测人体发出的红外线&#xff0c;常用于安防系统、自动…

自动化之ansible(二)

一、ansible中playbook&#xff08;剧本&#xff09; 官方文档&#xff1a; Ansible playbooks — Ansible Community Documentation 1、playbook的基本结构 一个基本的playbook由以下几个主要部分组成 hosts: 定义要执行任务的主机组或主机。 become: 是否需要使用超级用户…

QSNCTF-WEB做题记录

第一题&#xff0c;文章管理系统 来自 <天狩CTF竞赛平台> 描述&#xff1a;这是我们的文章管理系统&#xff0c;快来看看有什么漏洞可以拿到FLAG吧&#xff1f;注意&#xff1a;可能有个假FLAG哦 1&#xff0c;首先观察题目网站的结构和特征 这个一个文件管理系统&#x…

Ruoyi-Vue 3.8.7集成积木报表JmReport和积木大屏JimuBI

Ruoyi-Vue 3.8.7集成积木报表JmReport和积木大屏JimuBI 一、版本 RuoYi-Vue版本&#xff1a;v3.8.7 JMreport报表版本&#xff1a; v1.9.4 JimuBI大屏版本&#xff1a;V1.9.4 二、数据库 积木数据库sql 下载后&#xff0c;使用数据库管理工具执行sql脚本&#xff0c;将需…

亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!

作者&#xff1a;程序员 Hollis 之前介绍过在IDEA中使用DeepSeek的方案&#xff0c;但是很多人表示还是用的不够爽&#xff0c;比如用CodeChat的方案&#xff0c;只支持V3版本&#xff0c;不支持带推理的R1。想要配置R1的话有特别的麻烦。 那么&#xff0c;今天&#xff0c;给…

jvm中各个参数的理解

MEMORY - MANAGERS 定义 MEMORY - MANAGERS即内存管理器&#xff0c;它是操作系统或软件系统中负责管理计算机内存资源的组件。从本质上来说&#xff0c;它是一种软件机制&#xff0c;旨在协调计算机系统中内存的分配、使用和回收等操作&#xff0c;确保系统能够高效、稳定地…