十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本文转载自微信公众号「新钛云服」,作者徐磊。转载本文请联系新钛云服公众号。

在日常运维中,当你遇到每天邮件发出当天服务器的监控状态等这些类似需求时,你还在每天下午快下班的时候打开zabbix截图、发邮件......日复一日的手动处理吗?如果是,这篇干货将为您解放双手。
本文档主要介绍利用python脚本,指定主机名,自动获取zabbix监控图,下载到本地,形成word文档。
总体思路:先通过zabbix API获取auth;通过主机名获取host ID;通过host ID获取graph ID;再利用爬虫代码将图片下载到本地;通过python的docx 模块插入至word文档(本文zabbix的版本是5.0,python是3.8)。
首先,获取zabbix的auth ID,通过zabbix API获取主机id,graph ID等信息。打印出auth ID之后,保存下来,之后获取host ID需要用到:
- #获取authkey
 - ZABBIX_URL = 'https://www.zabbix.cn'
 - ZABBIX_USERNAME = "user"
 - ZABBIX_PASSWORD = "password"
 - url = "{}/api_jsonrpc.php".format(ZABBIX_URL)
 - header = {"Content-Type": "application/json"}
 - # auth user and password
 - data = {
 - "jsonrpc": "2.0",
 - "method": "user.login",
 - "params": {
 - "user": ZABBIX_USERNAME,
 - "password": ZABBIX_PASSWORD
 - },
 - "id": 1,
 - }
 - # 由于API接收的是json字符串,故需要转化一下
 - value = json.dumps(data).encode('utf-8')
 - # 对请求进行包装
 - req = request.Request(url, headers=header, data=value)
 - # 验证并获取Auth ID
 - try:
 - # 打开包装过的url
 - result = request.urlopen(req)
 - except Exception as e:
 - print("Auth Failed, Please Check Your Name And Password:", e)
 - else:
 - response = result.read()
 - # 上面获取的是bytes类型数据,故需要decode转化成字符串
 - page = response.decode('utf-8')
 - # 将此json字符串转化为python字典
 - page = json.loads(page)
 - result.close()
 - print("Auth Successful. The Auth ID Is: {}".format(page.get('result')))
 
变量hostname即是需要查询的主机名,通过主机名查询到host ID。当然也可以使用input函数做交互,实现动态查询。
- #获取hostid
 - data = {
 - "jsonrpc": "2.0",
 - "method": "host.get",
 - "params": {
 - "output": [
 - "hostid",
 - "host"
 - ],
 - "selectInterfaces": [
 - "interfaceid",
 - "ip"
 - ]
 - },
 - "auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", #这里的auth的值就是上文获取到的auth
 - "id": 2
 - }
 - r = requests.post(url, headers=header, data=json.dumps(data))
 - hostname = abc123
 - with open(hostname_txt,'r') as f:
 - tmp_list = [x.strip("\n") for x in f.readlines()]
 - for hostname in tmp_list:
 - if r.json().get('result'):
 - for i in r.json()['result']:
 - if i['host'] == hostname:
 - hostids = i['hostid']
 
使用host ID查询到graph ID,并保存到本地;
value传入的值,from和to这两个key决定了获取监控图的监控时间段,本例设置的是一个月;
value中"profileIdx": "web.graphics.filter"是zabbix 5.0 API的新特性,如果不传入,获取的监控时段是一个小时且无法更改。
- #获取graphid列表和字典
 - class ZabbixGraph(object):
 - def __init__(self,url,name,password):
 - self.url=url
 - self.name=name
 - self.password=password
 - #初始化的时候生成cookies
 - cookiejar = http.cookiejar.CookieJar()
 - #cookiejar = cookielib.CookieJar()
 - urlOpener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookiejar))
 - #urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
 - values = {"name":self.name,'password':self.password,'autologin':1,"enter":'Sign in'}
 - data = parse.urlencode(values).encode("utf-8")
 - #data = urllib.urlencode(values)
 - request = urllib.request.Request(url, data)
 - try:
 - urlOpener.open(request,timeout=10)
 - self.urlOpener=urlOpener
 - except ValueError:
 - print("网页打开失败")
 - def GetGraph(self,url,values,p_w_picpath_dir):
 - key=values.keys()
 - print(values)#显示最底下value传入的值
 - if "graphid" not in key:
 - print("请确认是否输入graphid")
 - sys.exit(1)
 - #以下if 是给定默认值
 - if "period" not in key:
 - #默认获取一天的数据,单位为秒
 - values["period"]=2626560
 - if "stime" not in key:
 - #默认为当前时间开始
 - values["stime"]=datetime.datetime.now().strftime('%Y%m%d%H%M%S')
 - if "width" not in key:
 - values["width"]=800
 - if "height" not in key:
 - values["height"]=200
 - data=parse.urlencode(values).encode("utf-8")
 - #data=urllib.urlencode(values)
 - request = urllib.request.Request(url,data)
 - url = self.urlOpener.open(request)
 - p_w_picpath = url.read()
 - p_w_picpathname="%s/%s.png" % (p_w_picpath_dir, values["hostname"]+"_images"+values["name"])
 - f=open(p_w_picpathname,'wb+')
 - f.write(p_w_picpath)
 - f.close()
 - graph_id_data = {
 - "jsonrpc": "2.0",
 - "method": "graph.get",
 - "params": {
 - "output": "extend",
 - "hostids": hostids,
 - "sortfield": "name"},
 - "auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
 - "id": 1}
 - graph_data = requests.post(url, headers=header, data=json.dumps(graph_id_data))
 - graph_list_x = []
 - graph_dict_name = {}
 - if graph_data.json().get('result'):
 - for i in graph_data.json()['result']:
 - graph_id = i['graphid']
 - graph_name_x = i['name']
 - graph_name=graph_name_x.replace("/","_")
 - graph_dict_name[graph_id]=graph_name
 - graph_list_x.append(graph_id)
 - #==========================================================
 - #此url是获取图片是的,请注意饼图的URL 和此URL不一样,请仔细观察!
 - gr_url="https://www.zabbix.cn/chart2.php"
 - #登陆URL
 - indexURL="https://www.zabbix.cn/index.php"
 - username="user"
 - password="password"
 - #用于图片存放的目录
 - p_w_picpath_dir="/picture"
 - #图片的参数,该字典至少传入graphid。
 - for key, value in graph_dict_name.items():
 - values = {"graphid":key,"name":value,"hostname":hostname,"width":800,"height":200,"from":"now-30d","to":"now","profileIdx": "web.graphics.filter"}
 - b=ZabbixGraph(indexURL,username,password)
 - b.GetGraph(gr_url,values,p_w_picpath_dir)
 
写入word:
- from docx import Document
 - from docx.shared import Inches
 - import os
 - from PIL import Image
 - # 要插入的图片所在的文件夹
 - pwd = os.getcwd()
 - fold='picture'
 - fold_DS = fold + '/' + '.DS_Store'
 - word = pwd + "/" + "word文档"
 - try:
 - os.mkdir(pwd+'/'+fold,755)
 - except:
 - print("目录已存在!无须创建")
 - # os.walk(fold)没有返回值,所以这么做显然没有结果,是错的
 - # pics=list(os.walk(fold)[3])
 - # # pics.pop()
 - # print(pics)
 - # pics是图片的名字
 - # root是string类型, dirs和pics是list类型
 - try:
 - os.remove(fold_DS)
 - except:
 - print("no .DS_Store文件")
 - for root, dirs, pics in os.walk(fold):
 - doc=Document()
 - for i in range(0,len(pics)):
 - filepath = root + '/' + str(pics[i])
 - string = str(pics[i].strip(".png"))
 - #print(string)
 - try:
 - doc.add_paragraph(string)
 - doc.add_picture(filepath,width=Inches(6),height=Inches(3))
 - except Exception:
 - pic_tmp=Image.open(filepath)
 - # 如果格式有问题,就用save转换成默认的jpg格式
 - pic_tmp.save(pic_tmp)
 - # 把处理后的图片放进Document变量doc中
 - doc.add_picture(filepath, width=Inches(6),height=Inches(3))
 - # 把Document变量doc保存到指定路径的docx文件中
 - doc.save(word)
 - #输出保存成功的标志
 - print("pic", string, "successfully added.")
 
本次使用到的模块,在运行之前检查主机是否安装本次执行需要的模块。
一般需要单独安装的模块有四个urllib3,requests,Pillow,python-docx。
- import json
 - import requests
 - from urllib import request, parse
 - from urllib.request import urlopen
 - import sys
 - import datetime
 - import urllib.request
 - import http.cookiejar
 - import os
 - import ssl
 - from docx import Document
 - from docx.shared import Inches
 
如果有些同学的zabbix使用的是https的访问方式,在代理里需要跳过证书验证。
ssl._create_default_https_context = ssl._create_unverified_context
在获取到的图片打开的时候提示文件已损坏,使用其他代码编辑器打开查看代码,发现是登陆失败的问题,需要检查登陆账户和方式是否正确。