目录
difflib的使用
difflib简介
difflib
为
python
的标准库模块,无需安装。用于对比文本之间的差异。并且支持输出可读性比较强
的
HTML
文档,类似
Linux
系统的
diff
命令。
应用场景
:
代码和配置文件差异对比。
准备工作
:
1.
两个不同时间备份的
Nginx
配置文件
2. Pycharm
集成化编程工具
配置文件内容差异对比的实现
实现代码及详解:
import difflib
# 1. 需要分析两个配置文件的不同,需要读取文件的内容
# 如果是windows系统, 目录路径的分隔符是\, 一定要转义
# 如果是Linux系统, 目录路径的分隔符是/, 不需要转义
filename1 = '/root/PycharmProjects/Devops/input/nginx_backup_20200725.conf'
filename2 = '/root/PycharmProjects/Devops/input/nginx_backup_20200726.conf'
with open(filename1) as f:
# readlines读取文件的所有内容,以列表的数据格式存储,每个元素是一行内容。
content1 = f.readlines()
with open(filename2) as f:
content2 = f.readlines()
# 2. 通过difflib模块对文件内容进行对比
# # 2-1). we instantiate a Differ object
diff = difflib.Differ()
# # 2-2). Finally, we compare the two: result = list(d.compare(text1, text2))
results = diff.compare(content1, content2)
# for result in results:
# # result是字符串, result.strip方法是删除字符串前面和后面的空格(广义的空格\n, \t,' ')
# print(result.strip())
# 2-3). 以更美观的方式对文件差异性对比进行展示
hdiff = difflib.HtmlDiff()
result = hdiff.make_file(content1, content2) # 会禅城一个html字符串
# 将产生的字符串存储到html文件中
# 文件存储的方法1:python 04_配置文件差异性对比.py > output/diff.html
# print(result)
# 文件存储的方法2: 通过python方法写入文件
filename='/root/PycharmProjects/Devops/output/diff.html'
with open(filename,'w')as f:
f.write(result)
print("写入html文件[%s]成功" % (filename))
执行效果:用浏览器打开输出文件diff.html
文件一致性检查的实现
需求: 当进行代码审计或者校验备份结果时,往往需要检查原始与目标文件一致性
代码实现及详解:
import difflib
import hashlib
"""
集成文件差异性对比的功能到Sysinfo项目中:
"""
def find_file_different(filename1, filename2, output_filename):
# 1. 需要分析两个配置文件的不同,需要读取文件的内容
# 如果是windows系统, 目录路径的分隔符是\, 一定要转义
# 如果是Linux系统, 目录路径的分隔符是/, 不需要转义
with open(filename1) as f:
# readlines读取文件的所有内容,以列表的数据格式存储,每个元素是一行内容。
content1 = f.readlines()
with open(filename2) as f:
content2 = f.readlines()
# 2. 通过difflib模块对文件内容进行对比
# # 2-1). we instantiate a Differ object
# diff = difflib.Differ()
# # 2-2). Finally, we compare the two: result = list(d.compare(text1, text2))
# results = diff.compare(content1, content2)
# for result in results:
# # result是字符串, result.strip方法是删除字符串前面和后面的空格(广义的空格\n, \t,' ')
# print(result.strip())
# 2-3). 以更美观的方式对文件差异性对比进行展示
hdiff = difflib.HtmlDiff()
result = hdiff.make_file(content1, content2) # 会禅城一个html字符串
# 将产生的字符串存储到html文件中
# 文件存储的方法1:python 04_配置文件差异性对比.py > output/diff.html
# print(result)
# 文件存储的方法2: 通过python方法写入文件
with open(output_filename, 'w') as f:
f.write(result)
print("写入html文件[%s]成功" % (output_filename))
def test_fun1():
# 我的代码是Windows系统
filename1 = 'input/nginx_backup_20200725.conf'
filename2 = 'input/nginx_backup_20200726.conf'
output_filename = 'output/diff.html'
find_file_different(filename1, filename2, output_filename)
def is_same_file(filename1, filename2):
"""
1. hashlib模块
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
2. 摘要算法
摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
3. 摘要算法的有效性:
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,
但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
"""
with open(filename1) as f:
# 读取文件的所有内容,以列表的数据格式存储,每个元素是一行内容。
content1 = f.read()
# 第一种摘要算法的实现方式
# 注意: python3中所有的字符串默认是unicode编码格式,如果需要utf-8编码的时候,使用encode方法进行编码操作
"""
ASCII编码:只能存储英文信息(字母,数字,符号),一个字符占用一个字节,1字节=8位 2^8-1
Unicode编码:是可以支持多种语言,一个字符占用2个字节,2字节=16位, 2^16-1
utf-8编码:如果是中文,占用3个字节,如果是英文占用一个字节,从而节省空间
GBK 2312编码:针对于汉字的编码格式
"""
md1 = hashlib.md5()
md1.update(content1.encode('utf-8'))
hex_content1 = md1.hexdigest()
with open(filename2) as f:
content2 = f.read()
# 第二种摘要算法的实现方式
md1 = hashlib.md5(content2.encode('utf-8'))
hex_content2 = md1.hexdigest()
# print("1: ", hex_content1)
# print("2: ", hex_content2)
return hex_content1 == hex_content2
"""
1. if __name__=="__main__"是什么含义?
__name__ 是当前模块名,
1). 当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,if 以下代码块将被运行。
2). 当模块是被导入时,代码块不被运行。
"""
if __name__ == '__main__':
filename1 = 'input/nginx_backup_20200725.conf'
filename2 = 'input/nginx_backup_20200726.conf'
output_filename = 'output/diff.html'
if is_same_file(filename1, filename2):
print("[*] 文件内容一致, 不需要进行差异性对比")
else:
print("[*] 文件内容不一致, 差异性对比结果请查看: ", output_filename)
find_file_different(filename1, filename2, output_filename)
运行结果:
将文件差异性对比加入项目
编写子路由文件:
from django.urls import path, include
from . import views
urlpatterns = [
# 子路由配置,有对应的视图函数.
path('', views.index, name='index'),
path('disk/',views.disk,name='disk'),
path('users/',views.users,name='users'),
path('diff/',views.diff,name='diff')
]
编写视图函数
import difflib
from django.shortcuts import render
from django.http import HttpResponse
import os
import platform
from datetime import datetime
import time
import psutil
# Create your views here.
# 需求1: 用户访问http://127.0.0.1:8000,返回主机的详情信息
from host.tools import get_md5
def index(request):
try:
# 如果是Linux系统,执行下面内容
# os.uname在windows系统中不能执行
system_info = os.uname()
node = system_info.nodename
system = system_info.sysname
except Exception as e:
# 如果是Windows系统,执行下面内容
system_info = platform.uname()
node = system_info.node
system = system_info.system
boot_time = psutil.boot_time()
boot_time = datetime.fromtimestamp(boot_time)
now_time = datetime.fromtimestamp(time.time())
info = {
'node':node,
'system': system,
'kernal_name': system,
'release': system_info.release,
'version': system_info.version,
'machine': system_info.machine,
'now_time': now_time,
'boot_time': boot_time,
'boot_delta': now_time-boot_time
}
return render(request,'host/index.html',{'info': info})
# 需求2:用户访问http://ip/disk/,返回磁盘分区的详细信息
def disk(request):
# 获取系统所有的磁盘分区
parts = psutil.disk_partitions()
disks = []
# 依次遍历获取每个分区的详细信息
for part in parts:
# 查看当前磁盘分区的使用率
usage = psutil.disk_usage(part.device)
# 每个分区的详细信息存储到列表中
disk = {
'device':part.device,
'mountpoint':part.mountpoint,
'fstype':part.fstype,
'opts':part.opts,
'total':usage.total,
'percent':usage.percent,
}
disks.append(disk)
# 返回html页面信息
return render(request,'host/disk.html',{'disks':disks})
# 需求3:用户访问http://ip/users/,返回当前登录用户的详细信息
def users(request):
all_users = []
users = psutil.users()
for user in users:
one_user = {
'name':user.name,
'host':user.host,
'started':datetime.fromtimestamp(user.started)
}
all_users.append(one_user)
return render(request,'host/users.html',{'users':all_users})
# 需求4:用户访问http://ip/, diff/,返回html页面,可以让用户上传文件
def diff(request):
"""
HTTP请求方法有哪些?
- GET: 一般情况下Get方法用于获取html页面内容
- POST: 一般情况下用于上传数据信息和上传文件信息
"""
print("客户端请求的方法: ", request.method)
if request.method == 'POST':
# 获取用户前端上传的文件
files = request.FILES
# 获取第一个和第二个文件对象, 通过read读取文件的内容
content1 = files.get('filename1').read()
content2 = files.get('filename2').read()
# 对于文件进行差异性对比
# 判断md5加密是否相同, 如果相同,则文件一致,否则,显示差异性对比
# 如何自动导入模块? Alt+Enter
if get_md5(content1) == get_md5(content2):
return HttpResponse("文件内容一致")
else:
hdiff = difflib.HtmlDiff()
content1 = content1.decode('utf-8').splitlines()
content2 = content2.decode('utf-8').splitlines()
# print(content1)
# make_file传入的是列表类型的文件内容
result = hdiff.make_file(content1, content2) # 会生成一个html字符串
return HttpResponse(result)
return render(request, 'host/diff.html')
编写加密文件
#/host/tools.py
import hashlib
def get_md5(content):
"""对于字符串进行md5加密"""
md1 = hashlib.md5(content)
return md1.hexdigest()
编写模板文件
#templates/host/diff.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>配置文件差异性对比</title>
{# 导入Bootstrap帮我们设置好的CSS样式和JS的动效 #}
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
{% include 'host/nav.html' %}
<div class="container">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<h1>配置文件差异性对比</h1>
{#form表单需要指定的信息: 1). 提交的方式method=post 2). 提交给哪一个路由处理? /diff/对应的视图函数是diff函数#}
{# form表单上传文件时,一定要设置 enctype="multipart/form-data" #}
<form role="form" action="/diff/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label for="inputfile">第一个配置文件:</label>
<input type="file" id="inputfile" name="filename1">
</div>
<div class="form-group">
<label for="inputfile">第二个配置文件:</label>
<input type="file" id="inputfile" name="filename2">
</div>
<button type="submit" class="btn btn-success">上传文件</button>
</form>
{#<form action="#">#}
{# 第一个配置文件: <input type="file" name="filename1"><br/>#}
{# 第二个配置文件: <input type="file" name="filename2"><br/>#}
{# <input type="submit" value="上传">#}
{#</form>#}
</div>
</div>
</div>
</body>
</html>
运行效果
至此,本项目告一段落!下个项目CMDB见!