首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >手撕代码:如何用Python通过SNMP协议批量抓取机房温湿度记录仪数据

手撕代码:如何用Python通过SNMP协议批量抓取机房温湿度记录仪数据

原创
作者头像
BJ盛世宏博小程
发布于 2026-10-26 15:25:09
发布于 2026-10-26 15:25:09
650
举报

手撕代码:如何用Python通过SNMP协议批量抓取机房温湿度记录仪数据

物联网・盛世宏博・SNMP、Python、机房动环、温湿度记录仪、批量采集

关键词:Python SNMP、机房动环监控、温湿度记录仪、批量采集、pysnmp、SNMP v2c、OID、机房温湿度采集、动环开发、物联网采集脚本 标签:Python开发、SNMP采集、机房动环、温湿度传感器、批量轮询、运维脚本、动环监控开发

一、方案概述

在机房、档案馆库房、配电室动环项目中,大量温湿度记录仪、RJ45网口温湿度传感器普遍支持SNMP v2c协议。很多项目需要快速做采集原型、对接自有平台、做本地日志存储,不想直接采购成套动环采集软件。

SNMP(简单网络管理协议)是工业传感器、动环设备非常通用的采集协议,大部分国产以太网温湿度记录仪默认开启SNMP v2c,配置好共同体字(community)、设备IP,给定温度OID、湿度OID即可读取实时数值。

本文手撕Python完整采集代码,基于pysnmp实现批量多IP并发轮询,读取温湿度,支持超时控制、异常捕获、结果输出、写入本地日志,适合快速验证设备、做采集Demo、二次集成到机房动环、智慧档案监控系统。

适用设备:支持SNMP v2c的RJ45温湿度记录仪(很多动环传感器默认OID:温度.1.3.6.1.4.1.xxxx.1.1.1,湿度.1.3.6.1.4.1.xxxx.1.1.2,不同厂商OID有差异,需要看设备手册) 不适用:仅支持Modbus TCP、RS485的传感器。

二、环境准备

2.1 依赖库安装

我们使用 pysnmp 库,支持SNMP v1/v2c/v3,代码兼容Python3.7~3.11

代码语言:javascript
复制
pip install pysnmp

注意:pysnmp版本建议使用 4.4.12 稳定版,新版本API改动较大。

代码语言:javascript
复制
pip install pysnmp==4.4.12

2.2 前置配置(非常关键)

  1. 温湿度记录仪和运行脚本的电脑/服务器同一网段;
  2. 设备Web后台开启SNMP,设置读共同体(默认一般是 public);
  3. 关闭防火墙,放行UDP 161端口(SNMP默认端口UDP:161);
  4. 拿到厂商提供的OID:温度OID、湿度OID;
  5. 整理所有传感器IP清单。

常见坑:SNMP是UDP协议,不是TCP,ping通不代表161端口通;交换机ACL、设备白名单会直接导致超时无返回。

2.3 先确认OID是否正确

可以用 snmpwalk 命令先行验证(需要安装net-snmp工具)

代码语言:javascript
复制
snmpwalk -v2c -c public 192.168.1.100 .1.3.6.1.4.1.xxx

能看到返回温度、湿度数值,说明OID和网络正常,再跑Python脚本。

三、单设备SNMP读取基础函数(手撕基础版)

这是底层封装函数,输入IP、community、oid,返回原始值,增加超时、重试、异常捕获。

代码语言:javascript
复制
from pysnmp.hlapi import *
import time

def snmp_get(ip: str, community: str, oid: str, timeout=1, retries=1):
    """
    SNMP v2c GET 单OID读取
    :param ip: 设备IP
    :param community: 读共同体,一般public
    :param oid: 对象标识符字符串,如 ".1.3.6.1.4.1.xx.1.1.1"
    :param timeout: 超时时间 单位s
    :param retries: 重试次数
    :return: 成功返回数值,失败返回None
    """
    errorIndication, errorStatus, errorIndex, varBinds = next(
        getCmd(SnmpEngine(),
               CommunityData(community, mpModel=1),  # mpModel=1 → SNMPv2c
               UdpTransportTarget((ip, 161), timeout=timeout, retries=retries),
               ContextData(),
               ObjectType(ObjectIdentity(oid)))
    )

    if errorIndication:
        # 超时、不可达、共同体错误都会走到这里
        print(f"[SNMP ERROR] {ip} | {errorIndication}")
        return None
    elif errorStatus:
        print(f"[SNMP ERROR] {ip} | {errorStatus.prettyPrint()} @ {errorIndex}")
        return None
    else:
        for varBind in varBinds:
            raw_val = varBind[1].prettyPrint()
            return raw_val

测试调用示例:

代码语言:javascript
复制
if __name__ == "__main__":
    temp_oid = ".1.3.6.1.4.1.2021.10.1.1"
    hum_oid  = ".1.3.6.1.4.1.2021.10.1.2"
    ip_addr  = "192.168.1.100"
    com = "public"
    t_val = snmp_get(ip_addr, com, temp_oid)
    h_val = snmp_get(ip_addr, com, hum_oid)
    print(f"IP:{ip_addr} 温度:{t_val} 湿度:{h_val}")

很多国产温湿度传感器返回值是放大10倍的整数,例如返回256代表25.6℃,后续代码里会增加换算逻辑。

四、批量并发采集完整版(工程可用版本)

串行轮询大量设备速度很慢,这里使用concurrent.futures.ThreadPoolExecutor多线程并发批量抓取,适合几十台机房温湿度记录仪同时采集;增加IP清单配置、数值换算、结果打印、日志写入本地csv。

说明:SNMP本身是IO密集型任务,多线程非常适合,不要开超大并发,建议线程池大小20以内,防止UDP报文风暴。

代码语言:javascript
复制
from pysnmp.hlapi import *
from concurrent.futures import ThreadPoolExecutor, as_completed
import csv
import time
from datetime import datetime

# ====================== 【配置区,根据现场修改】 ======================
COMMUNITY = "public"
TEMP_OID  = ".1.3.6.1.4.1.37244.1.1.1"   # 替换为你设备温度OID
HUM_OID   = ".1.3.6.1.4.1.37244.1.1.2"   # 替换为你设备湿度OID
SCALE = 10.0   # 很多传感器数值放大10倍,除以10得到真实值;如果是直接浮点返回设置为1
TIMEOUT = 1.2
MAX_WORKERS = 15  # 并发线程数量
# 批量设备IP清单,在这里录入所有机房温湿度记录仪IP列表
DEVICE_LIST = [
    {"ip":"192.168.1.100","name":"机房A-01"},
    {"ip":"192.168.1.101","name":"机房A-02"},
    {"ip":"192.168.1.102","name":"配电室-01"},
    {"ip":"192.168.1.103","name":"库房-01"},
]
LOG_FILE = "snmp_temp_hum_log.csv"
# ====================================================================

def snmp_get_one_oid(ip, community, oid):
    errorIndication, errorStatus, errorIndex, varBinds = next(
        getCmd(SnmpEngine(),
               CommunityData(community, mpModel=1),
               UdpTransportTarget((ip, 161), timeout=TIMEOUT, retries=1),
               ContextData(),
               ObjectType(ObjectIdentity(oid)))
    )
    if errorIndication or errorStatus:
        return None
    for varBind in varBinds:
        return varBind[1].prettyPrint()

def task_read_device(dev):
    """单个设备采集任务:读取温度、湿度,换算、返回字典"""
    ip = dev["ip"]
    dev_name = dev["name"]
    t_raw = snmp_get_one_oid(ip, COMMUNITY, TEMP_OID)
    h_raw = snmp_get_one_oid(ip, COMMUNITY, HUM_OID)
    ret = {
        "name": dev_name,
        "ip": ip,
        "temp": None,
        "hum": None,
        "ts": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    }
    try:
        if t_raw is not None:
            ret["temp"] = round(float(t_raw)/SCALE, 2)
        if h_raw is not None:
            ret["hum"] = round(float(h_raw)/SCALE, 2)
    except Exception as e:
        print(f"数据解析异常 {ip}: {str(e)}")
    return ret

def batch_collect():
    """批量并发采集入口"""
    result_list = []
    print(f"==== 开始批量SNMP采集,设备总数:{len(DEVICE_LIST)} ====")
    start = time.time()
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        future_map = {executor.submit(task_read_device, dev): dev for dev in DEVICE_LIST}
        for future in as_completed(future_map):
            res = future.result()
            result_list.append(res)
    cost = time.time() - start
    print(f"==== 采集完成,耗时 {cost:.2f}s ====")
    return result_list

def save_to_csv(data_list):
    """追加写入CSV日志,用于历史归档,对接后续数据分析"""
    header = ["ts","name","ip","temp","hum"]
    with open(LOG_FILE, "a", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=header)
        # 判断文件是否为空,为空则写表头
        f.seek(0,2)
        if f.tell() == 0:
            writer.writeheader()
        for item in data_list:
            writer.writerow(item)

if __name__ == "__main__":
    while True:
        collect_result = batch_collect()
        # 控制台打印结果
        for item in collect_result:
            print(f"[{item['ts']}] {item['name']:>10} | {item['ip']:>15} | 温度:{item['temp']} ℃ | 湿度:{item['hum']} %RH")
        save_to_csv(collect_result)
        time.sleep(30)  # 采集间隔30s,按需修改

五、代码功能说明

  1. 并发批量轮询:多线程,几十台传感器快速一轮采集;
  2. 数值缩放适配:SCALE 参数适配“放大10倍整型返回”的国产温湿度记录仪,是这类设备最常见的格式;
  3. 持久化日志:自动写入csv,带时间戳,可直接导入Excel、或者给数据库入库做过渡;
  4. 循环定时采集:while循环 + sleep,做成常驻采集脚本;
  5. 完善异常捕获:超时、OID错误、解析失败不会导致整个脚本崩溃;
  6. 设备用name+ip管理,方便机房、档案库房、配电室按点位命名。

六、常见踩坑清单(项目高频问题)

  1. SNMP是UDP 161:TCP端口连通性测试无效;服务器防火墙、交换机ACL、设备SNMP白名单是头号故障点;
  2. OID不统一:不同厂商动环温湿度记录仪私有OID完全不一样,没有通用OID,必须看设备手册;
  3. 共同体字错误:很多项目现场修改过community,不再是public;
  4. 返回值是放大整数:255 → 25.5℃,忘记除以10,温度直接显示255℃;
  5. 并发开太大:max_workers设置100+,UDP报文拥塞,大面积超时;
  6. pysnmp在Windows部分环境报错:优先在Linux(CentOS/Ubuntu)跑采集服务,稳定性更高;
  7. SNMP v3设备:上面代码只支持v2c,v3需要额外配置用户名、认证加密。

七、扩展方向(对接动环平台)

这份脚本是采集底座,可以按需二次开发:

  1. 把采集结果推送到MQTT Broker,接入物联网平台、智慧档案馆监控平台;
  2. 增加阈值判断:温度>26℃或者湿度>60%RH触发告警,调用企业微信/钉钉webhook推送告警;
  3. 替换存储:把csv入库到InfluxDB、MySQL,做时序曲线;
  4. 增加SNMP Trap监听:接收设备主动上送的告警(和GET轮询是两条路线);
  5. 封装成FastAPI接口,提供HTTP接口给上层业务系统调用;
  6. 增加设备在线状态统计,做离线告警。

八、方案优势总结

  1. 轻量无依赖:纯Python脚本,不需要部署复杂采集网关软件,快速验证SNMP型温湿度记录仪;
  2. 批量采集高效,适合机房、档案库房大量RJ45温湿度传感器巡检;
  3. 可直接作为动环、智慧档案项目前期原型,验证设备通信,后期可以迁移到边缘网关C采集程序;
  4. 代码透明可控,自主可控,适合国产化项目、信创服务器上部署采集服务;
  5. 自带日志记录,方便排查传感器离线、数值漂移等现场问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 手撕代码:如何用Python通过SNMP协议批量抓取机房温湿度记录仪数据
    • 一、方案概述
    • 二、环境准备
      • 2.1 依赖库安装
      • 2.2 前置配置(非常关键)
      • 2.3 先确认OID是否正确
    • 三、单设备SNMP读取基础函数(手撕基础版)
    • 四、批量并发采集完整版(工程可用版本)
    • 五、代码功能说明
    • 六、常见踩坑清单(项目高频问题)
    • 七、扩展方向(对接动环平台)
    • 八、方案优势总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档