python二进制截图,图片存到本地
项目中截取的函数片段,仅供参考
def send_img_data(self):
Message.info('作废截图上传')
try:
for index, item in enumerate(self.screen_shot_list):
if index == 0:
self.client.upload_cancel_img_to_backend(
self.declare_id,
self.client_info.get('customerid'),
item['tax_num'],
item['filename'],
item['file'],
is_first=1 # 如果图片is_first都是1,图片会被覆盖只剩1张
)
else:
self.client.upload_cancel_img_to_backend(
self.declare_id,
self.client_info.get('customerid'),
item['tax_num'],
item['filename'],
item['file'],
is_first=0
)
except Exception as e:
Message.error(str(e) + '发生图片信息发生异常,对任务结果不影响,当前异常原因:{}'.format(traceback.format_exc()))
def save_screen_shot_img(self, filename="error", tax_num='', message='', is_first=0):
"""
截图保存,产品要求,图片做一定处理,打水印
:param is_first: 上传平台,如果有多张图片,多张图片时,都可以设为0,如果是1,表示图片放在第一位,不能同时设为1,会被覆盖
:param message: 水印信息:申报作废
:param tax_num: 税种类型:60,605
:param filename: 图片名称
:return:
"""
try:
binary_content = self.screenshot_png_bytes_client()
# binary_content = self.driver.get_screenshot_as_png()
file_binary_content = self.add_text_for_image_new(binary_content, message)
self.save_bytes_img_to_file(file_binary_content, filename)
time_name = time.strftime('%H:%M:%S', time.localtime())
if tax_num in ['50', '51', '52', '500']:
self.screen_shot_list.append({
'filename': time_name + filename + '.png',
'file': file_binary_content,
'tax_num': '500',
'is_first': is_first
})
else:
self.screen_shot_list.append({
'filename': time_name + filename + '.png',
'file': file_binary_content,
'tax_num': tax_num,
'is_first': is_first
})
Message.info(f"截图成功:税号:{tax_num}====> 文件名{filename}")
except Exception as e:
Message.error(str(e) + '截图发生异常,但不影响程序申报、核查结果,当前异常原因:{}'.format(traceback.format_exc()))
def save_bytes_img_to_file(self, binary_content, filename):
"""
保存二进制图片数据为图片,存到本地
:return:
"""
content = binary_content
byte_stream = io.BytesIO(content) # 请求数据转化字节流
roiImg = Image.open(byte_stream) # Image打开二进制流Byte字节流数据
imgByteArr = io.BytesIO() # 创建一个空的Bytes对象
roiImg.save(imgByteArr, format='PNG') # PNG就是图片格式
imgByteArr = imgByteArr.getvalue() # 保存的二进制流
# 生成文件目录
file_path = os.path.join(os.getcwd(), 'images')
if not os.path.exists(file_path):
os.makedirs(file_path)
filename = f"{filename}.png"
# 创建图片
with open(f"images/{filename}", "wb+") as f:
f.write(imgByteArr)
@retry(stop_max_attempt_number=3, retry_on_exception=is_stop_exception)
def screenshot_png_bytes_client(self):
"""截取图片并转换为二进制流-客户端用"""
# return self.driver.get_screenshot_as_png()
im = ImageGrab.grab()
img_byte = io.BytesIO()
im.save(img_byte, 'PNG')
binary_content = img_byte.getvalue()
return binary_content
class CrawlerException(Exception):
"""
基础异常类
"""
def __init__(self, msg=None, task_state=TaskCode.UnknownError, value=None, node_msg=None):
"""
:param msg:错误信息,传值会覆盖异常枚举类中的提示信息,不传值使用异常枚举类中的提示信息与value进行拼接合成提示信息
:param task_state:异常枚举类
:param value:==>tuple()
:param node_msg:异常节点信息
"""
self.error_info = msg
# 异常枚举类
self.task_state = task_state
# 报错信息
self.set_error_info(value)
# 错误码
self.task_code = task_state.task_code
# 是否建单
self.need_redmine = self.get_redmine_config()
# 发生异常的节点
self.node_msg = node_msg
def __str__(self):
return str(self.error_info)
def set_error_info(self, value):
if not self.error_info:
if value:
try:
self.error_info = self.task_state.prompt.replace('%s', '{}').format(*value)
except:
Message.error('参数数量错误,报错枚举类:{}'.format(self.task_state))
self.error_info = self.task_state.prompt.replace('%s', '')
else:
if self.task_state == TaskCode.UnknownError:
# 临时处理
self.error_info = UNDEFINED_ERROR_TIPS
exec_str = format_exc()
# 将网络异常从未知异常中排除出来,并修改为网络异常
for sub in NETWORK_EXCEPTIONS:
if exec_str.count(sub):
self.error_info = NETWORK_ERROR_TIPS
break
else:
self.error_info = self.task_state.prompt.replace('%s', '')
def get_redmine_config(self):
# 成功场景不建单,特殊场景不建单
try:
# SDK异常枚举类提供建单配置字段后:
return self.task_state.need_redmine
except:
Message.info('SDK异常枚举类暂未提供建单配置字段')
# 临时方法,SDK异常枚举类暂未提供建单配置字段
return self.task_code.startswith('0302') and self.task_state not in no_redmine_list
def get_error_info(self):
if self.error_info in [NETWORK_ERROR_TIPS, UNDEFINED_ERROR_TIPS]:
self.error_info += f'**{self.node_msg}**'
return self.error_info
def get_status_code(self):
return self.task_code
def set_node_msg(self, node_msg):
if self.node_msg is None and node_msg:
self.node_msg = node_msg
def is_stop_exception(exception):
"""
判断异常是否需要重试
@param exception: 异常类
@return:
"""
Message.info(f"判断异常是否需要重试-is_stop_exception----{exception}")
if isinstance(exception, CrawlerRetryException):
# 指定重试异常
return True
elif isinstance(exception, CrawlerException):
# 可控异常,根据需要是否选择,如果需要重试抛CrawlerRetryException
if exception.get_status_code() == '030209':
# 避免装饰器之后,变成 TaskCode.UnknownError的情况,无法重试
return True
else:
return False
else:
# 非可控异常,都重试
return True
class CrawlerRetryException(CrawlerException):
"""
重试异常,与retrying模块的retry装饰器
"""
description = "CrawlerRetryError"
def __init__(self, msg, task_state=None):
super().__init__()
self.error_info = msg
self.task_state = task_state or TaskCode.UnknownError
# self.task_state = TaskCode.UnknownError
self.task_code = self.task_state.task_code
self.need_redmine = False
def __str__(self):
return str(self.error_info)
``