# -*- coding: utf-8 -*-
"""
Created with PyCharm
@Author Jcsim
@Date 2021-1-25 14:44
@File utils.py
@Description
"""
import datetime
import hashlib
import json
import configparser
import os
import random
import sys
import time
import traceback
import uuid
from decimal import Decimal
from filecmp import cmp
from io import BytesIO
from urllib.parse import quote
import xlwt as xlwt
from django.http import JsonResponse, HttpResponse
from PIL import Image, ImageDraw, ImageFont
from ERP.log import logger
class Arguments:
@staticmethod
def get_arguments(request):
"""
获取请求参数
:param request:
:return:
"""
if request.method == 'GET':
logger.info("收到GET请求")
arguments = dict(request.GET)
for arg in arguments:
if type(arguments[arg]) == type([]):
arguments[arg] = arguments[arg][0]
else:
logger.info("收到POST请求")
logger.info("post-body")
logger.info(request.body.decode())
if "form" in request.content_type:
arguments = request.POST
else:
arguments = json.loads(request.body.decode())
return arguments
# 表单验证类
class VerifyUtil:
@staticmethod
def verify_phone(phone):
# 手机号码格式的正则表达式
# reg = '^1(3[0-9]|4[5,7]|5[0,1,2,3,5,6,7,8,9]|6[2,5,6,7]|7[0,1,7,8]|8[0-9]|9[1,8,9])\d{8}$'
# reg = '^[0-9]{1,13}$'
length = len(phone)
if 0 < length <= 13:
return True
else:
return False
# return re.match(reg, phone)
@staticmethod
def is_number(num):
s = str(num)
if s.count('.') == 1: # 小数
new_s = s.split('.')
left_num = new_s[0]
right_num = new_s[1]
if right_num.isdigit():
if left_num.isdigit():
return True
elif left_num.count('-') == 1 and left_num.startswith('-'): # 负小数
tmp_num = left_num.split('-')[-1]
if tmp_num.isdigit():
return True
elif s.count(".") == 0: # 整数
if s.isdigit():
return True
elif s.count('-') == 1 and s.startswith('-'): # 负整数
ss = s.split('-')[-1]
if ss.isdigit():
return True
return False
@staticmethod
def is_id_number(ID):
"""
身份证校验
:param ID:
:return:
"""
VERIFYMATRIX = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
VERIFYCODE = '10X98765432'
if type(ID) != str:
return False
if len(ID) != 18:
return False
if 1900 < int(ID[6:10]) < 2100:
pass
else:
return False
if not ID[:-1].isdigit():
return False
if ID[-1].isdigit() or ID[-1] == 'X':
pass
else:
return False
try:
time.strptime(ID[6:14], '%Y%m%d')
except:
return False
data = [VERIFYMATRIX[i] * int(ID[i]) for i in range(17)]
verify_code = sum(data) % 11
verify_code = VERIFYCODE[verify_code]
if verify_code != ID[-1]:
return False
else:
return True
# return True
@staticmethod
def get_gender(id_num):
"""
15位身份证,最后一位为性别位
18位身份证,倒数第二位为性别位
:param id_num:
:return:
"""
sex_no = None
if len(id_num) == 15:
sex_no = id_num[-1]
elif len(id_num) == 18:
sex_no = id_num[-2]
if sex_no:
sex_no = int(sex_no)
gender = sex_no % 2
if gender == 1:
return "男"
else:
return "女"
return "未能识别,请检查身份证号码是否有错"
class DateEncoderToStr(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, datetime.date):
return obj.strftime("%Y-%m-%d")
elif isinstance(obj, Decimal):
return str(obj)
else:
return json.JSONEncoder.default(self, obj)
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return int(obj.timestamp())
else:
return str(obj)
# IP工具类
class IpUtil:
# X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。
@staticmethod
def get_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0] # 这里是真实的ip
else:
ip = request.META.get('REMOTE_ADDR') # 这里获得代理ip
return ip
# 分页 current_page--当前页码 per_page---每页条数
class PageUtil(object):
def __init__(self, current_page, per_page):
try:
self.current_page = int(current_page)
except Exception as e:
self.current_page = 1
self.per_page = per_page
# 1,0-10
# 2,10-20
# 3,20-30
def start(self):
return (self.current_page - 1) * self.per_page
def end(self):
return self.current_page * self.per_page
# 读取配置文件
class ReadConfig:
"""定义一个读取配置文件的类"""
def __init__(self, filepath=None):
if filepath:
config_path = filepath
else:
from MR_ERP import settings
if 'linux' == sys.platform:
config_path = os.path.join(settings.BASE_DIR, "config", "config.ini")
else:
config_path = os.path.join(settings.BASE_DIR, "config", "test_config.ini")
self.cf = configparser.ConfigParser()
self.cf.read(config_path, encoding="utf-8")
def get_system(self, param):
self.cf = configparser.RawConfigParser()
from MR_ERP import settings
if 'linux' == sys.platform:
config_path = os.path.join(settings.BASE_DIR, "config", "config.ini")
else:
config_path = os.path.join(settings.BASE_DIR, "config", "test_config.ini")
self.cf.read(config_path, encoding="utf-8")
value = self.cf.get("SYSTEM", param)
return value
def get_redis(self, param):
value = self.cf.get("REDIS", param)
return value
def get_db(self, param):
value = self.cf.get("DB", param)
return value
# 日期工具类
class DateUtil:
@staticmethod
def seconds_to_day_hour_minute_second(seconds):
s = str(seconds)
if not s.isdigit():
return "格式错误,请输入整数"
elif type(seconds) == str:
seconds = int(seconds)
day = seconds // 86400
hour = seconds // 3600 - day * 24
minute = seconds // 60 - day * 1440 - hour * 60
sec = seconds - day * 86400 - hour * 3600 - minute * 60
result = ""
if day != 0:
result += str(day) + "天"
if hour != 0:
result += str(hour) + "小时"
if minute != 0:
result += str(minute) + "分"
if sec != 0:
result += str(sec) + "秒"
if day == 0 and hour == 0 and minute == 0 and sec == 0:
result = 0
return result
@staticmethod
def get_now_datetime():
"""
获取当前时间
:return:
"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@staticmethod
def get_today_date():
"""
获取今天的日期
:return:
"""
return datetime.datetime.now().strftime("%Y-%m-%d")
@staticmethod
def datetime_string():
"""
返回时间格式的字符串
:return:
"""
return "%Y-%m-%d %H:%M:%S"
@staticmethod
def string_datetime(str, time=True):
"""
日期字符串转为时间格式
:param str: 日期字符串
:param time: 是否包括时间
:return:
"""
if time:
return datetime.datetime.strptime(str, "%Y-%m-%d %H:%M:%S")
else:
return datetime.datetime.strptime(str, "%Y-%m-%d")
@staticmethod
def date_time_to_string(date_, dt=True):
"""
时间转字符串
:param date_:
:param dt: 是否为日期时间格式
:return:
"""
if dt:
return date_.strftime("%Y-%m-%d %H:%M:%S")
else:
return date_.strftime("%Y-%m-%d")
@staticmethod
def judge_is_date(value):
try:
datetime.datetime.strptime(value, '%Y-%m-%d')
return True
except ValueError:
return False
# 数字工具类
class NumUtil:
@staticmethod
def get_random_num(length):
"""
随机生成一个length长度的随机数,从sources里面提取
:param length:
:return:
"""
sources = "0123xcrfvbgtyhn456zjumi789qweasdklop"
data = ""
for i in range(length):
# random.randint(begin, end) 返回begin<= i <=end 左闭右闭区间
data += sources[random.randint(0, 35)]
return data
@staticmethod
def judge_is_price(value):
count = str(value).count(".")
price_left = str(value).split(".")[0]
if count == 0:
price_right = "00"
else:
price_right = str(value).split(".")[1]
count__ = str(price_left).count("-")
if count__ > 1 or str(price_right).count("-") > 0:
return False
elif count__ == 1:
try:
price_left = str(-1 * int(price_left))
except ValueError:
return False
if count > 1 or not price_left.isdigit() or len(price_left) < 0:
return False
if price_right and (
not price_right.isdigit() or int(price_right) < 0 or len(price_right) < 0 or len(price_right) > 2):
return False
return True
@staticmethod
def new_round(_float, _len):
"""
Parameters
----------
_float: float
_len: int, 指定四舍五入需要保留的小数点后几位数为_len
Returns
-------
type ==> float, 返回四舍五入后的值
"""
if isinstance(_float, float):
if str(_float)[::-1].find('.') <= _len:
return _float
if str(_float)[-1] == '5':
return round(float(str(_float)[:-1] + '6'), _len)
else:
return round(_float, _len)
else:
return round(_float, _len)
# 加密、解密
class EncryptionUtil:
@staticmethod
def md5(string):
"""
md5加密
:param string:
:return:
"""
# 创建md5对象
hl = hashlib.md5()
hl.update(string.encode(encoding='utf-8'))
return hl.hexdigest()
# 返回值
class ResultUtil:
@staticmethod
def error_500_result():
"""
返回500错误
:return:
"""
return JsonResponse({"status": 500, "msg": "系统异常!请联系管理员", "errMsg": traceback.format_exc()},
json_dumps_params={'ensure_ascii': False})
@staticmethod
def result(status, msg="success", data=None):
result_dict = {
"status": status,
"msg": msg,
"data": data
}
return JsonResponse(result_dict, json_dumps_params={'ensure_ascii': False})
@staticmethod
def page_result(status, msg, data=None, count=0):
result = {
'count': count, # 总数据量
'data': json.dumps(data, cls=DateEncoderToStr), # 当前页显示的数据
'status': status,
'msg': msg
}
return JsonResponse(result, json_dumps_params={'ensure_ascii': False})
class UpDownloadUtil:
# 上传图片
@staticmethod
def upload(request):
if request.method != "POST":
result = {
"status": 201,
"msg": "请使用post请求",
"data": None
}
return result
try:
result = {
"status": 200,
"msg": "success",
"data": None
}
classify = request.POST['classify']
# 接收文件
file = request.FILES['file'] # 上传的文件
# 判断是否有 file
if not file:
result["status"] = 201
result["msg"] = "请上传文件"
return result
if not classify:
result["status"] = 202
result["msg"] = "请填写文件分类"
return result
# print(classify)
# 限制文件大小
if file.size > 5 * 1024 * 1024:
result["status"] = 203
result["msg"] = "请上传5MB以内的文件"
return result
# 设置允许上传的文件格式
ALLOW_IMG_EXTENSIONS = ['png', 'jpg', 'jpeg']
ALLOW_VIDEO_EXTENSIONS = ['mp4']
# 默认文件类型
file_type = "file"
# 判断文件格式
if file.name.rsplit('.', 1)[1].lower() in ALLOW_IMG_EXTENSIONS:
file_type = "img"
elif file.name.rsplit('.', 1)[1].lower() in ALLOW_VIDEO_EXTENSIONS:
file_type = "video"
else:
result["status"] = 204
result["msg"] = "文件格式错误,图片请上传[png, jpg, jpeg]格式,视频请上传[mp4]格式。"
return result
# 图片路径
# path = os.path.join(settings.BASE_DIR, "static/img/",
# classify) if 'linux' == sys.platform else os.path.join(settings.BASE_DIR,
# "static\\img", classify)
from MR_ERP import settings
path = os.path.join(settings.BASE_DIR, "static", file_type, classify)
# 假如文件夹不存在,创建文件夹
if not os.path.exists(path):
os.makedirs(path)
# 文件名
if classify == "user_profile_photo":
# 检验是否登录 这里由调用方法去判断,只有登录了才能进入此上传方法,故token应是有值的
token = request.META.get("HTTP_X_AUTH0_TOKEN")
# 如果是用户头像,则将用户id作为文件名
filename = token[32:] + '.' + file.name.split(".")[-1]
else:
# uuid4 基于随机数
filename = ''.join(str(uuid.uuid4()).split('-')) + str(time.time()).split(".")[0] + '.' + \
file.name.split(".")[-1]
path_filename = path + "/" + filename if 'linux' == sys.platform else path + "\\" + filename
# 如果上传的是用户头像,且该用户有旧头像,则将旧的删除。确保用户头像文件夹下一个用户只有一张图片
if os.path.exists(path_filename) and classify == "user_profile_photo":
os.remove(path_filename)
with open(path_filename, 'wb') as f:
# data = file.file.read() # 文件字节流数据 从文件中读取整个上传的数据。小心整个方法:如果这个文件很大,你把它读到内存中会弄慢你的系统。
# f.write(data)
for buf in file.chunks(): # 如果上传的文件足够大需要分块就返回真。默认的这个值是2.5M,当然这个值是可以调节的
f.write(buf)
data = {
"url": "/static/" + file_type + "/" + classify + '/' + filename,
"absolute_url": path_filename,
}
result["data"] = data
return result
except Exception as e:
traceback.print_exc()
result = {
"status": 500,
"msg": "系统出错,请联系管理员",
"errMsg": traceback.format_exc(),
"data": None
}
return result
class ImageCode:
# 通过数字获取ascii表中的对应字母
@staticmethod
def get_char(length):
sources = "0123xcrfvbgtyhn456zjumi789qweasdklopQAZXSWEDCFVRTGYBHNJUMIKOLP"
data = ""
for i in range(length):
# random.randint(begin, end) 返回begin<= i <=end 左闭右闭区间
data += sources[random.randint(0, 61)]
return data
# return chr(random.randint(65, 90))
# 获取随机颜色
@staticmethod
def get_color(*args):
if args == ():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
while True:
text_color = ImageCode.get_color()
aberration = 3 * (text_color[0] - args[0][0]) ** 2 + 4 * (text_color[0] - args[0][1]) ** 2 + 2 * (
text_color[0] - args[0][2]) ** 2
# 设置字体颜色与背景色差,值越大,字体越清晰,机器人就更容易识别,相对应的代价就是需要随机更多次的RGB值。
# 极端情况验证码字体背景rgb均为128,aberration最小的最大值为147456
rgb2 = 100000
if aberration > rgb2:
return text_color
@staticmethod
def get_img(char_code_length):
"""
获取验证码图片
:param char_code_length: 验证码长度
:return:
"""
# 创建图片对象
img_back = ImageCode.get_color()
img_width = 120
img_height = 50
img = Image.new(mode='RGB', size=(img_width, img_height), color=img_back)
# 创建画笔对象
draw = ImageDraw.Draw(img, mode='RGB')
# 噪点 xy:基于图片的坐标,fill表示点颜色
for i in range(100):
draw.point([random.randint(0, img_width), random.randint(0, img_height)], fill=ImageCode.get_color())
# 噪线 xy:(起点坐标,终点坐标) fill:颜色 width:线宽
# draw.line((50, 30, 100, 60),fill='purple', width=5)
for i in range(20):
draw.line([random.randint(0, img_width), random.randint(0, img_height), random.randint(0, img_width),
random.randint(0, img_height)],
fill=ImageCode.get_color())
# 划圆或弧线
for i in range(30):
x = random.randint(0, img_width)
y = random.randint(0, img_height)
x2 = x + 4
y2 = y + 4
draw.arc((x, y, x2, y2), 0, 90, fill=ImageCode.get_color())
from MR_ERP import settings
# 字体文件地址
path = os.path.join(settings.BASE_DIR, "static", "fonts", "setofont.ttf")
font = ImageFont.truetype(path, 30)
# 用来拼接验证码字符的
char_code = ImageCode.get_char(char_code_length)
for i in range(char_code_length):
char = char_code[i]
height = random.randint(10, 15)
draw.text([18 * (i + 1), height], char, ImageCode.get_color(img_back), font=font)
# 模糊效果和边缘增强效果
# img = img.filter(ImageFilter.BLUR)
# img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
# img.show()
# print(char_code)
return img, char_code
class ExportUtil:
# 导出表格
def write_excel(self, filename, tabletitle, bodytitle, datalist, sheetName):
"""
:param sheetName:
:param filename: 输出的文件名
:param tabletitle: 表标题
:param bodytitle: 表头 格式如: ["名字", "课程名称", "课程编号"]
:param datalist: 表格数据 格式二维数组如:
[
["学号1234", "张三", "语文", "course001"],
["学号1235", "李四", "语文", "course001"],
["学号1236", "王五", "语文", "course001"]
]
:return:
"""
# 设置字体
font = xlwt.Font()
font.bold = True # 加粗
font.name = "微软雅黑"
"""
# May be: NO_LINE = 无线, THIN = 实线, MEDIUM = 中, DASHED = 虚线, DOTTED = 点虚线, THICK = 厚,
DOUBLE, HAIR, MEDIUM_DASHED,
THIN_DASH_DOTTED, MEDIUM_DASH_DOTTED, THIN_DASH_DOT_DOTTED, MEDIUM_DASH_DOT_DOTTED,
SLANTED_MEDIUM_DASH_DOTTED
"""
# 设置边框
borders = xlwt.Borders()
borders.left = xlwt.Borders.THIN
borders.right = xlwt.Borders.THIN
borders.top = xlwt.Borders.THIN
borders.bottom = xlwt.Borders.THIN
# 设置居中(左右上下居中)
alignment = xlwt.Alignment()
alignment.horz = xlwt.Alignment.HORZ_CENTER # 水平方向
alignment.vert = xlwt.Alignment.HORZ_CENTER # 垂直方向
"""
0 = Black, 1 = White, 2 = Red, 3 = Green, 4 = Blue, 5 = Yellow,
6 = Magenta, 7 = Cyan, 16 = Maroon, 17 = Dark Green, 18 = Dark Blue, 19 = Dark Yellow , almost brown),
20 = Dark Magenta, 21 = Teal, 22 = Light Gray, 23 = Dark Gray
"""
# 设置背景颜色
pattern = xlwt.Pattern()
pattern.pattern = xlwt.Pattern.SOLID_PATTERN
pattern.pattern_fore_colour = 7 # 背景颜色
# 定义不同的excel style
style_tabletitle = xlwt.XFStyle()
style_tabletitle.font = font
style_tabletitle.borders = borders
style_tabletitle.alignment = alignment
style2 = xlwt.XFStyle()
style2.borders = borders
style2.alignment = alignment
style_data = xlwt.XFStyle()
style_data.alignment = alignment
style_body_title = xlwt.XFStyle()
style_body_title.borders = borders
style_body_title.font = font
style_body_title.pattern = pattern
style_body_title.alignment = alignment
wb = xlwt.Workbook(encoding='utf-8')
worksheet = wb.add_sheet(sheetName)
# 合并单元格并设置样式
worksheet.row(0).height_mismatch = True
worksheet.row(0).height = 20 * 25
# 合并单元格并设置样式
worksheet.write_merge(0, 0, 0, len(bodytitle), tabletitle, style=style_tabletitle)
# worksheet.write_merge(1, 1, 0, len(bodytitle), remark)
# 确定栏位宽度
col_width = []
for i in range(len(datalist)):
for j in range(len(datalist[i])):
if i == 0:
col_width.append(self.len_byte(datalist[i][j]))
else:
if len(col_width) <= j:
col_width.append(self.len_byte(datalist[i][j]))
else:
if col_width[j] < self.len_byte(str(datalist[i][j])):
col_width[j] = self.len_byte(datalist[i][j])
# 设置栏位宽度,栏位宽度小于10时候采用默认宽度
for i in range(len(col_width)):
if col_width[i] > 10:
worksheet.col(i).width = 256 * (col_width[i] + 1)
# 设置栏位高度
# tall_style = xlwt.easyxf('font:height 720;') #设置字体高度
# row0 = worksheet.row(0)
# row0.set_style(tall_style)
for i in range(0, len(bodytitle)):
worksheet.write(1, i, bodytitle[i], style=style_body_title)
# excel内容写入
for i in range(len(datalist)):
for j in range(len(datalist[i])):
worksheet.write(i + 2, j, datalist[i][j], style=style_data)
sio = BytesIO()
# 将excel文件保存到字节流里面
wb.save(sio)
sio.seek(0)
# # 设置响应参数
# filename = "表格.xls"
response = HttpResponse(sio.getvalue(), content_type='application/vnd.ms-excel; charset=UTF-8')
basename = os.path.basename(filename)
utf_filename = quote(basename.encode("utf-8"))
response["Content-Disposition"] = "attachment;filename*=utf-8''{}".format(utf_filename)
response.write(sio.getvalue())
return response
# 获取字符串长度,一个中文的长度为2
def len_byte(self, value):
if value is None or value == "":
return 10
if type(value) != int:
length = len(value)
utf8_length = len(value.encode('utf-8'))
length = (utf8_length - length) / 2 + length
else:
length = len(str(value))
return int(length)
# linux系统 pip install pycrypto
# Windows pip3 install -i https://pypi.douban.com/simple pycryptodome 找到文件所在目录 将文件夹名"crypto" 修改为"Crypto"
class AesCbc:
__instance = None
# 单例模式
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super().__new__(cls, *args, **kwargs)
return cls.__instance
def __init__(self):
# key:秘钥,大小为16byte/24byte/32byte----AES-128/AES-192/AES-256/
self.key = 'xiwenr&dstaTGpRw'.encode('utf-8')
# vi:初始向量,大小为16byte
self.iv = b'xiwenr&drhGW5hN3'
self.mode = AES.MODE_CBC
# 如果text不足16位的倍数就用空格补足为16位
@staticmethod
def add_to_16(text):
if len(text.encode('utf-8')) % 16:
add = 16 - (len(text.encode('utf-8')) % 16)
else:
add = 0
text = text + ('\0' * add)
return text.encode('utf-8')
# 加密函数
def encrypt(self, text):
text = self.add_to_16(text)
cryptos = AES.new(self.key, self.mode, self.iv)
cipher_text = cryptos.encrypt(text)
# 因为AES加密后的字符串不一定是ascii字符集的,输出保存可能存在问题,所以这里转为16进制字符串
return b2a_hex(cipher_text).decode()
# 解密后,去掉补足的空格用strip() 去掉
def decrypt(self, text):
cryptos = AES.new(self.key, self.mode, self.iv)
plain_text = cryptos.decrypt(a2b_hex(text))
return bytes.decode(plain_text).rstrip('\0')
if __name__ == '__main__':
pass
# print(DateUtil.string_datetime("2020-09-14", False))
# for i in range(1000):
# print(NumUtil.get_random_num(4))
# password = EncryptionUtil.md5("123456")
# print(password)
# print(EncryptionUtil.md5("3mae" + password))
# print(settings.BASE_DIR)
# root_dir = os.path.dirname(os.path.abspath('.'))
# config_path = os.path.join(settings.BASE_DIR, "config", "config.ini")
# print(root_dir)
# print(config_path)
# cf = configparser.ConfigParser()
# cf.read(config_path, encoding="utf-8")
# print(cf.get("system", "ip"))
#
# print(ReadConfig().get_redis("PORT"))
# print("dasd123123,./l,;';".isalnum())
# 图片路径
# classify = "ppppp"
# path = os.path.join(settings.BASE_DIR, "static", "img",
# classify) if 'linux' == sys.platform else os.path.join(settings.BASE_DIR,
# "static", "img", classify)
# # 图片路径
# path2 = os.path.join(settings.BASE_DIR, "static/img/",
# classify) if 'linux' == sys.platform else os.path.join(settings.BASE_DIR,
# "static\\img", classify)
# print(path)
# print(path2)
# for i in range(100):
# print(''.join(str(uuid.uuid4()).split('-')) + str(time.time()).split(".")[0])
# now = time.time()
# print(now)
# timeArray = time.localtime(44303.520833333336)
# otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
# print(otherStyleTime)
# print("18112341234;18512341235".split(";") in "13412341234;18512341235".split(";"))
# for i in "18112341234;18512341235".split(";"):
# if i in "13412341234;18512341235".split(";"):
# print(i)
# list_ = "18112341234".split(";", 1)
# print(list_[0])
# print(list_[-1])
site = {'name': '菜鸟教程', 'alexa': 10000, 'url': 'www.runoob.com'}
pop_obj = site.pop('name')
print(site)