【运维开发】使用Python+Selenium自动化配置海康威视监控设备OSD字符

最近在做一个监控项目,用Python+Selenium实现自动化提效的一次实战——面对3万+海康威视监控设备的OSD字符批量配置需求,原计划15天的工作量,3天就圆满完成,既省时间又省成本,做安防运维、自动化相关的朋友可以参考借鉴。

项目背景

最近接了一个区域派出所的监控项目支持,核心需求很明确:统一规范辖区内所有海康摄像头的OSD字符,包括监控点位名称、编号的校准,以及显示位置(如左下角、居中)的调整,硬性要求15天内完成所有设备的配置。

一开始拿到需求没觉得复杂,直到看到设备量级——整整3万多台,瞬间意识到难度不小。更关键的是,项目初期的方案的是安排外包人员,逐台登录摄像头后台手动操作,从登录、调整到保存,全程人工完成,典型的“手动搬砖”模式。

我现场观察了一会儿就发现,这种方式效率太低了:哪怕是熟练的操作人员,配置一台设备也需要5-8分钟,即便24小时轮班,15天能不能按期完成都是未知数。而且人工操作难免有疏漏,比如字符输错、位置调偏,后期排查整改反而更耗时,再加上外包成本不低,怎么算都不划算。

作为常年和自动化打交道的技术人,我第一反应就是:这种重复性强、操作固定的工作,完全可以用脚本来解决,既解放双手,又能保证效率和准确率。

示例图片(因为找不到截图了,现在也没有后台环境,网上随便找了一张标记一下):

示例图片

技术方案选型:Python+Selenium,精准解决批量操作痛点

结合需求,核心是实现“Web后台批量自动化操作”,最终选定Python+Selenium的技术组合,理由很简单:Python语法简洁、上手快,各类辅助库齐全,开发效率高;Selenium能完美模拟浏览器的所有手动操作,刚好适配海康威视摄像头的Web管理后台,无需额外开发复杂接口。

这里分享几个实操中踩过的坑和对应的解决办法,都是能直接用到的实战经验:

  1. 设备适配问题:海康不同型号的摄像头,后台界面和固件版本存在差异,部分设备的OSD配置入口路径不同,容易导致脚本报错。解决方式是先统计辖区内所有摄像头的型号和固件版本,针对性编写适配逻辑,让脚本自动识别页面元素,匹配对应配置入口,避免中断。

  2. 批量执行与异常处理:3万+台设备无法一次性执行完毕,且中途可能出现网络波动、设备离线、登录失败等问题。为此我在脚本中加入两个核心逻辑:一是读取设备IP清单,批量逐台执行,每完成一台自动记录日志,实时掌握进度;二是增加异常重试机制,登录、配置失败的设备自动重试3次,仍失败的单独标记,后续手动处理,不影响整体进度。

  3. 配置标准化:为确保所有设备的OSD配置统一,我将字符内容、显示位置、字体大小等参数,设置为可修改的变量,在脚本开头统一配置,执行时自动同步到所有设备,彻底避免人工操作带来的差异化错误。

项目实现: 环境安装及python脚本开发

环境要求:

  • Python 3.6+ 版本
  • Selenium 库
  • Chrome浏览器
  • Chrome浏览器驱动(如chromedriver.exe)

Chrome浏览器驱动相关参考:https://www.runoob.com/w3cnote/chromedriver-intro.html

主要功能:

  • 自动打开Chrome浏览器,访问监控设备的Web管理界面
  • 自动登录系统(用户名: admin ,密码: ******* )
  • 配置OSD显示区域(区域5、6、7)
  • 自动设置6层叠加内容:
    1. 时间 - 左上角显示
    2. 省份 - 广东(位置:左93,上71)
    3. 城市 - 深圳(位置:左93,上76)
    4. 区 - 龙华(位置:左93,上81)
    5. 街道 - 油松(位置:左93,上86)
    6. 监控信息 - 随机选择”动球外治东/西/南/北”之一

使用方式: 运行脚本后,输入监控设备的IP地址,脚本会自动完成所有OSD配置。

脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#coding=utf-8
from time import sleep
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
import json

def load_config(filename):
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)

def configure_osd(browser, config):
url = 'http://' + config['ip']
print('正在配置设备: {}'.format(url))

browser.get(url)
browser.switch_to_frame('banner')
sleep(1)

browser.find_element_by_id('userName').send_keys(config['username'])
browser.find_element_by_id("password").send_keys(config['password'])
browser.find_element_by_id('autoPlay').click()
browser.find_element_by_class_name("custom-btn-center").click()

browser.find_element_by_id('nav_config').click()
browser.find_element_by_id('Com_osdLink').click()

browser.find_element_by_id('osdAreaLabel4').click()
s5 = browser.find_element_by_id('Field4')
Select(s5).select_by_value('5')
browser.find_element_by_id('osdAreaLabel5').click()
s6 = browser.find_element_by_id('Field5')
Select(s6).select_by_value('6')
browser.find_element_by_id('osdAreaLabel6').click()
s7 = browser.find_element_by_id('Field6')
Select(s7).select_by_value('7')

browser.find_element_by_id('osdInfoLabel0').click()
browser.find_element_by_id('InfoLeft').click()
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD7)
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD4)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD3)

browser.find_element_by_id('osdInfoLabel1').click()
val1 = browser.find_element_by_id('Value1')
val1.click()
val1.clear()
val1.send_keys(config['province'])
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD9)
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD3)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD7)
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD1)

browser.find_element_by_id('osdInfoLabel2').click()
val2 = browser.find_element_by_id('Value2')
val2.click()
addr = val2.get_attribute('defaultvalue')
print('原地址:', addr)
val2.clear()
val2.send_keys(config['city'])
xvalue = browser.find_element_by_id('InfoLeft').get_attribute('value')
print('当前X值:', xvalue)
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD9)
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD3)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD7)
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD6)

browser.find_element_by_id('osdInfoLabel3').click()
val3 = browser.find_element_by_id('Value3')
val3.click()
val3.clear()
val3.send_keys(config['district'])
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD9)
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD3)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD8)
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD1)

browser.find_element_by_id('infoTR4').click()
val4 = browser.find_element_by_id('Value4')
val4.click()
val4.clear()
val4.send_keys(config['street'])
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD9)
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD3)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD8)
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD6)

browser.find_element_by_id('infoTR5').click()
val5 = browser.find_element_by_id('Value5')
val5.click()
val5.clear()
val5.send_keys(addr)
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
xvalue_t = int(xvalue) - 1
print('新X值:', xvalue_t)
browser.find_element_by_id('InfoLeft').send_keys(xvalue_t)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD9)
browser.find_element_by_id('InfoTop').send_keys(Keys.NUMPAD1)

browser.find_element_by_id('infoTR6').click()
val6 = browser.find_element_by_id('Value6')
val6.click()
val6.clear()
val6.send_keys(config['monitor_info'])
browser.find_element_by_id('InfoLeft').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoLeft').send_keys(Keys.NUMPAD2)
browser.find_element_by_id('InfoTop').send_keys(Keys.CONTROL,'a')
browser.find_element_by_id('InfoTop').send_keys('91')
browser.find_element_by_id('nav_config').click()
sleep(1)
print('--------设备 {} 配置完毕--------'.format(config['ip']))

if __name__ == '__main__':
devices = load_config('devices_config.json')
print('共发现 {} 个设备需要配置'.format(len(devices)))

for i, device in enumerate(devices, 1):
print('\n========== 正在处理第 {}/{} 个设备 =========='.format(i, len(devices)))
browser = webdriver.Chrome()
try:
configure_osd(browser, device)
except Exception as e:
print('配置设备 {} 时出错: {}'.format(device['ip'], str(e)))
finally:
browser.close()

print('\n========== 所有设备配置完成 ==========')

运行方式:

  • 在脚本目录放置devices_config.json文件,文件内容为监控设备的IP地址清单。
  • 运行脚本:python configure_osd.py

devices_config.json文件内容示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"ip": "192.168.1.100",
"city": "北京",
"district": "东城区",
"street": "东城区",
"monitor_info": "动球外治东"
},
{
"ip": "192.168.1.101",
"city": "北京",
"district": "东城区",
"street": "东城区",
"monitor_info": "动球外治西"
}
]

项目实操:1天开发调试,3天全量落地

因为工期紧张,整个开发落地过程主打一个高效:先用1天时间完成脚本开发和调试,先选取10台不同型号的摄像头做测试,修正脚本适配问题,解决登录超时、元素定位失败等小bug;再用100台设备做小批量验证,确认配置准确率和执行效率达标后,正式启动全量执行。

脚本启动后完全无需人工干预:自动读取设备IP清单,逐台登录后台,自主定位OSD配置模块,完成字符输入、位置调整、保存配置等一系列操作,之后自动退出,切换至下一台设备,全程自动化运行。

实测下来,脚本配置一台设备仅需30秒左右,比人工操作快10倍以上,而且全程零差错,所有设备的OSD配置均符合项目要求。

最终效果超出预期:原计划15天的工作量,仅用3天就全部完成,不仅提前交付项目,还大幅节省了外包人工成本,得到了项目组和派出所相关负责人的认可。更实用的是,这个脚本可重复利用,后续新增摄像头、调整OSD配置时,只需修改相关参数,就能快速执行,进一步提升后续运维效率。

项目总结:自动化的核心,是用技术解决实际痛点

这次项目让我更深刻地体会到,自动化从来不是“炫技”,而是实实在在解决工作中的低效问题。很多时候,我们觉得工作繁琐、工期紧张,不是任务本身太难,而是没有找到合适的方法,把时间浪费在了重复的体力劳动上。

对于安防运维、设备批量配置这类重复性强、操作标准化的工作,Python+Selenium无疑是性价比极高的工具,不需要复杂的开发功底,只要摸清业务逻辑,就能用脚本替代重复操作,实现降本、提效、零差错的目标。

最后给大家一个小提醒:如果你们也需要做类似的海康设备自动化配置,一定要提前做好设备型号适配和异常处理,避免因个别设备差异导致脚本中断。如果有具体的技术疑问,欢迎联系我,我会尽力解答。

这里,希望能给正在被重复工作困扰的朋友一点启发,一起用技术提升效率,摆脱手动搬砖的困境~