抓取监控看板 - 爬虫系统实时监控与告警详解

📂 所属阶段:第六阶段 — 运维与监控(工程化篇)
🔗 相关章节:Scrapyd与ScrapydWeb · Docker容器化爬虫 · Scrapy-Redis分布式架构

目录

监控系统概述

如果把一个稳定运行的爬虫系统比作一艘远航的船,那么监控就是船长的「雷达」与「仪表盘」。它通过指标收集、日志聚合、可视化展示、自动化告警,把抽象的系统状态变成直观的数据图表,让你随时掌控爬虫的一举一动。

为什么工程化爬虫必须有监控?

小打小闹的单机脚本确实不需要监控,可是当爬虫走向分布式、长期运行、高可用时,每一分钟的异常都可能意味着:

  • 数据丢失:抓取任务悄悄崩溃,几天后才发现数据不全;
  • 资源浪费:内存泄漏导致服务器 OOM,或空跑任务占满带宽;
  • 排查困难:面对几百 G 原始日志,定位一个问题要花费半天时间。

好的监控能够从根本上解决这些痛点——事前预警、事中定位、事后复盘

极简可落地的架构

很多教程一上来就推荐 ELK + Prometheus + Grafana 全套全家桶,对中小型爬虫项目来说部署成本过高。这里推荐一套 轻量化三剑客架构,使用免费云服务,不需要自己安装任何服务端组件:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────────┐
│  Scrapy/Scrapyd │───▶│  指标/日志暴露  │───▶│  Grafana Cloud      │
│  (爬虫实例)     │    │  (中间件 + SDK) │    │  (托管 Prom / Loki) │
└─────────────────┘    └─────────────────┘    └─────────────────────┘
  • Grafana Cloud 提供免费额度,自带 Prometheus(指标)、Loki(日志)、Grafana(可视化),注册即用;
  • 爬虫端只需集成官方 prometheus_client 库,暴露少量核心指标即可对接;
  • 本地测试时,可用 ngrok 等内网穿透工具让云上的 Prometheus 抓到本地指标。

下面我们手把手把这套架子搭起来。


核心组件快速实践

1. 用 Prometheus Client 暴露爬虫指标

Scrapy 本身没有内建监控,我们可以通过一个下载器中间件来实现埋点。下面的中间件会在每个请求/响应/异常的生命周期里更新 Prometheus 计数器、仪表盘和直方图:

# scrapy_project/middlewares.py
from prometheus_client import start_http_server, Counter, Gauge, Histogram
import time

# ---------- 全局指标定义 ----------
REQUESTS = Counter(
    'scrapy_requests_total', '总请求数',
    ['spider', 'status']
)
ERRORS = Counter(
    'scrapy_errors_total', '总错误数',
    ['spider', 'error_type']
)
ACTIVE_CONCURRENCY = Gauge(
    'scrapy_active_concurrency', '当前活跃请求数',
    ['spider']
)
RESPONSE_TIME = Histogram(
    'scrapy_response_time_seconds', '响应时间分布',
    ['spider'],
    buckets=[0.1, 0.5, 1, 3, 10]
)

class PrometheusMetricsMiddleware:
    """
    Scrapy 下载器中间件,用于向 Prometheus 暴露抓取指标。
    启动一个 HTTP 服务,专门让 Prometheus server 拉取。
    """
    def __init__(self, settings):
        self.port = settings.getint('PROMETHEUS_METRICS_PORT', 8000)
        # 启动指标暴露服务(与 Scrapy 进程共享同一进程)
        start_http_server(self.port)

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings)

    def process_request(self, request, spider):
        ACTIVE_CONCURRENCY.labels(spider=spider.name).inc()
        request.meta['start_time'] = time.time()

    def process_response(self, request, response, spider):
        if 'start_time' in request.meta:
            duration = time.time() - request.meta['start_time']
            RESPONSE_TIME.labels(spider=spider.name).observe(duration)
        ACTIVE_CONCURRENCY.labels(spider=spider.name).dec()
        REQUESTS.labels(
            spider=spider.name, status=str(response.status)
        ).inc()
        return response

    def process_exception(self, request, exception, spider):
        ACTIVE_CONCURRENCY.labels(spider=spider.name).dec()
        error_type = type(exception).__name__
        ERRORS.labels(spider=spider.name, error_type=error_type).inc()

然后在 settings.py 里激活中间件,并指定一个端口(如果有多个爬虫进程,每个使用不同端口):

DOWNLOADER_MIDDLEWARES = {
    'scrapy_project.middlewares.PrometheusMetricsMiddleware': 100,
}
PROMETHEUS_METRICS_PORT = 8001

启动爬虫后,访问 http://localhost:8001/metrics 就能看到类似下面的原始数据:

# HELP scrapy_requests_total 总请求数
# TYPE scrapy_requests_total counter
scrapy_requests_total{spider="example",status="200"} 152.0
scrapy_requests_total{spider="example",status="404"} 3.0
# TYPE scrapy_response_time_seconds histogram
scrapy_response_time_seconds_bucket{spider="example",le="0.1"} 12.0
...

这就是 Prometheus 的采集端点,下一步我们让 Grafana Cloud 来拉取这些数据。

2. 接入 Grafana Cloud 免费托管服务

为什么选 Grafana Cloud?
自带告警、高可用、多租户、永久免费版足够中小团队使用,免去自己搭建和维护的烦恼。

步骤如下:

  1. 注册账号
    访问 Grafana Cloud ,使用 GitHub/Google 快速注册。

  2. 创建 Prometheus 数据源连接
    进入左侧「Connections」→「Add new connection」,搜索 Prometheus,选择「Hosted Prometheus metrics」→「Create a Prometheus data source」。
    你会得到一个远程写入的 URL 和凭据(用户名/API Key),后续我们会用这些信息让本地的 Prometheus 指标推送到云端。更简单的做法是让云上的 Prometheus 直接拉取本地的 /metrics 端点。

  3. 配置远程拉取(需要公网可达的指标端口)
    如果是本地开发环境,可以使用 ngroklocalhost:8001 暴露到公网:

    ngrok http 8001

    执行后会得到一个类似 https://xxxx.ngrok.io 的公网地址。
    回到 Grafana Cloud,在数据源配置中,将 Scrape interval 保持默认 30 秒,在 Custom HTTP Headers 里忽略(因为我们暂时不需要认证),然后将 Prometheus scrape target 设置为你的 ngrok 地址即可。

    生产环境中,建议通过内网专线或 VPC Peering 确保安全,切勿将爬虫指标直接暴露在公网上。

  4. 一键导入看板模板
    Grafana 社区提供了大量现成模板。进入「Dashboards」→「New」→「Import」,输入看板模板 ID 763(示例),选择刚配置好的 Prometheus 数据源,即可得到好看的实时监控面板。你可以看到:

    • 总请求数曲线(成功/失败趋势)
    • 错误率 面板
    • 活跃并发数 实时数值
    • 响应时间分位数(P50/P95/P99)

这样,一个零运维、零成本的爬虫监控面板就完成了。


故障排查与诊断

监控面板展示出异常后,我们需要快速定位根因。下面给出一个轻量级的诊断工具箱,可以集成到爬虫项目中,随时执行健康检查。

诊断工具实现

# diagnostic_tools.py
import psutil
import requests
import socket
import time
from datetime import datetime
import logging
from typing import Dict, List, Optional
import json

class DiagnosticTools:
    """爬虫故障诊断工具(轻量版)"""

    def __init__(self):
        self.logger = logging.getLogger(__name__)

    def check_system_resources(self) -> Dict:
        """检查系统资源,超过 85% 自动标记告警"""
        cpu = psutil.cpu_percent(interval=1)
        mem = psutil.virtual_memory().percent
        disk = psutil.disk_usage('/').percent
        procs = len(psutil.pids())

        warnings = []
        if cpu > 85:
            warnings.append('CPU 使用率过高')
        if mem > 85:
            warnings.append('内存使用率过高')
        if disk > 85:
            warnings.append('磁盘空间不足')

        return {
            'cpu_percent': cpu,
            'memory_percent': mem,
            'disk_percent': disk,
            'process_count': procs,
            'warnings': warnings
        }

    def check_network_connectivity(self, urls: List[str]) -> Dict:
        """测试目标站点或中间件的网络可达性"""
        results = {}
        for url in urls:
            try:
                start = time.time()
                resp = requests.head(url, timeout=5, allow_redirects=True)
                results[url] = {
                    'status': 'success',
                    'code': resp.status_code,
                    'latency_seconds': round(time.time() - start, 3)
                }
            except Exception as e:
                results[url] = {
                    'status': 'failed',
                    'reason': str(e)
                }
        return results

    def check_port_availability(self, host: str, ports: List[int]) -> Dict:
        """检查关键端口(Scrapyd 6800,Redis 6379,指标 8001 等)"""
        status = {}
        for port in ports:
            sock = None
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(2)
                sock.connect((host, port))
                status[port] = 'open'
            except Exception:
                status[port] = 'closed'
            finally:
                if sock:
                    sock.close()
        return status

    def run_full_diagnosis(self, spider_hosts: List[str] = None) -> str:
        """生成完整的可读诊断报告"""
        report = [f"# 爬虫诊断报告 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"]
        report.append("\n## 系统资源")
        sys_res = self.check_system_resources()
        report.append(json.dumps(sys_res, indent=2, ensure_ascii=False))
        report.append("\n## 关键端口(本地)")
        port_res = self.check_port_availability('localhost', [6800, 6379, 8001])
        report.append(json.dumps(port_res, indent=2))
        return "\n".join(report)

# 快速命令行自检
if __name__ == "__main__":
    diag = DiagnosticTools()
    print(diag.run_full_diagnosis())

这份报告可以直接发送到企业微信/钉钉告警通道,实现自动化诊断

常见故障速查表

现象排查步骤
抓取成功率骤降1. 查看 Prometheus 指标 scrapy_requests_total 按状态码分组的曲线,尤其关注 5xx/4xx 比例。2. 运行 check_network_connectivity 测试目标站。3. 检查是否触发反爬(验证码、IP 封禁)。
爬虫莫名停止运行1. 检查系统资源面板,确认内存/CPU 是否顶满。2. 查看 Scrapyd 的任务日志(logs/项目/蜘蛛/...)查找异常堆栈。3. 检查是否有未捕获的异常导致 reactor 退出。
Grafana 无数据1. 在本机访问 http://localhost:8001/metrics 确认端点正常。2. 检查 Grafana Cloud 的 scrape 配置是否正确。3. 若使用 ngrok,检查隧道是否稳定(有时需要重启)。

监控最佳实践

  1. 安全第一,内网为先
    生产环境不要让指标端口暴露到公网。使用 Prometheus 的 remote write 功能将数据推送到云端,或者通过 VPC 私网通道。本地调试用 ngrok 只能作为临时方案。

  2. 合理的告警规则

    • 错误率:连续 5 分钟错误率超过 5% 才告警,避免瞬时波动误报。
    • 资源:内存使用率超过 90% 且持续 3 分钟才触发。
    • 心跳:每个爬虫应有定期心跳指标(例如每分钟上报一次 up 值),若丢失超过 2 分钟则告警。
  3. 分层监控面板
    建议搭建三级看板:

    • 总览层:面向运维/负责人,展示全平台 QPS、错误率、系统健康。
    • 项目层:面向开发具体抓取任务的同事,针对单个蜘蛛的请求量、延迟、数据量等。
    • 单任务层:排查问题时使用,包含详细的日志流、最近一次运行参数、依赖服务状态。
  4. 定期清理数据
    Grafana Cloud 免费版有对应的日志和指标保留时间(Prometheus 13个月,Loki 30天)。注意不要无限制往里面灌测试数据,可在 Prometheus 的 scrape_config 里设置 job_name 做区分,方便管理。

  5. 逐步演进
    团队从小做起时,上述“轻量三剑客”完全够用。随着爬虫规模变大,可以考虑升级到自建 Prometheus + Grafana,并用 Loki 替代 ELK 统一日志,用 Tempo 补充分布式追踪,平滑过渡,避免过度设计。

监控不是一成不变的摆设,它应该随着业务不断迭代。希望这篇教程能帮你快速搭建起一个实用、低成本的爬虫监控体系,让你的抓取任务从此告别盲飞!