第一百零一次闯荡江湖的无名游侠 - Python
图片水印生成介绍tk+python 实现的图片水印生成地址：https://gitee.com/cx-a/image-watermark-generation预览图
介绍图片转gif、gif分解成图片小工具 使用python+tkinter实现地址：https://gitee.com/cx-a/image-to-gif
socketio.exceptions.ConnectionError: OPEN packet not returned by server
报错信息 Exception in thread Thread-3: Traceback (most recent call last): File "/home/comz/anaconda3/envs/arcface/lib/python3.7/threading.py", line 926, in _bootstrap_inner self.run() File "/home/comz/anaconda3/envs/arcface/lib/python3.7/threading.py", line 870, in run self._target(*self._args, **self._kwargs) File "usb_camera.py", line 140, in <lambda> Process(target=lambda: asyncio.run(upload_loop())).start() File "/home/comz/anaconda3/envs/arcface/lib/python3.7/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/home/comz/anaconda3/envs/arcface/lib/python3.7/asyncio/base_events.py", line 587, in run_until_complete return future.result() File "usb_camera.py", line 51, in upload_loop await sio.connect(url) File "/home/comz/anaconda3/envs/arcface/lib/python3.7/site-packages/socketio/asyncio_client.py", line 117, in connect raise exceptions.ConnectionError(exc.args[0]) from None socketio.exceptions.ConnectionError: OPEN packet not returned by server解决办法：pip install python-engineio==3.14.2 python-socketio==4.6.0来源：https://blog.csdn.net/qq_41251963/article/details/122049787
python+tkinter实现按照农历/阴历生日推送消息提醒到钉钉群机器人的程序
更新：2024.12.111.日志可以直接查看2.配置可以直接修改2024.12.031.修改界面2.修复之前的bug3.列表可以进行增删改功能：1.计算农历/阴历生日并且推送消息提醒到钉钉群机器人的程序。2.一个定时访问一个链接。使用说明：生日需要修改sql数据库文件time_config.ini文件字段说明dingding_url #钉钉群机器人链接birthday_day #天 提前多少天提醒birthday_timea #开始计算的时间birthday_timeb #超过此次时间就不进行计算birthday_timec #清空时间区间abirthday_timed #清空时间区间breport_url #定时访问一个链接的地址user_agent #get的浏览器请求头，可不修改report_frequency #秒 多少秒计算和访问链接一次运行图：下载地址：gitee在发行版中选择打包好的成品。python打包使用的是Pyinstaller打包。链接：https://gitee.com/cx-a/birthday-dingtalk-remind
Python 报 “SSL: CERTIFICATE_VERIFY_FAILED" 错误Linuxexport SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt以上方式在 CentOS 中验证 OK。更新 certifi 包手动安装模块 pyperclip
导入相关模块import ssl import socket import time from datetime import datetime首先我们创建一个 domain.txt 用来存放要检查的域名和对应的 IP 地址www.baidu.com:180.101.50.242,180.101.50.188 www.bing.com:202.89.233.101,202.89.233.100我们读取该文件，把里面的域名和对应的每个 ip 取出来，并存放到字典 domains 里面domains = { } with open('domain.txt', 'r', encoding='utf-8') as file: for line in file: domain, ip_pool = line.strip().split(':') domains[domain] = ip_pool.split(',')取出来之后我们循环遍历字典，去获取每个域名对应的证书信息（ssl_connect 函数）def ssl_connect(domain, ip): # 设置socket的超时时间为5秒 socket.setdefaulttimeout(5) # 创建默认的SSL上下文 context = ssl.create_default_context() # 创建一个SSL套接字 skt = context.wrap_socket(socket.socket(), server_hostname=domain) try: # 建立SSL连接 skt.connect((ip, 443)) # 获取证书过期时间 end_date = skt.getpeercert()['notAfter'].strip(' GMT') # 创建一个字典，存储本次连接中的域名、IP 地址和证书过期时间信息 skt_info = { 'domain': domain, 'ip': ip, 'end_date': end_date} except ssl.CertificateError as e: cert = e except socket.timeout: cert = 'Connect refused' except ConnectionResetError as e: cert = 'Connect reset' + str(e) except socket.gaierror as e: cert = 'Connnect gaierror' finally: # 关闭SSL套接字 skt.close() return skt_infossl_connect 函数返回一个字典 skt_info，包含当前连接的域名、ip 地址和证书过期时间# skt_info 内容 { 'domain': 'www.baidu.com', 'ip': '180.101.50.242', 'end_date': 'Aug 6 01:51:05 2024'} { 'domain': 'www.baidu.com', 'ip': '180.101.50.188', 'end_date': 'Aug 6 01:51:05 2024'} { 'domain': 'www.bing.com', 'ip': '202.89.233.101', 'end_date': 'Aug 16 03:47:45 2023'} { 'domain': 'www.bing.com', 'ip': '202.89.233.100', 'end_date': 'Aug 16 03:47:45 2023'}然后我们调用 check_cert_time 函数进行证书有效期检查和提示info = [ssl_connect(domain, ip) for domain, ip_pool in domains.items() for ip in ip_pool] [check_cert_time(i) for i in info]check_cert_time 函数内容如下：def check_cert_time(info): # 获取当前时间戳 current_timestamp = int(time.time()) # 将证书过期时间转换成时间戳 date_object = datetime.strptime(info['end_date'], "%b %d %H:%M:%S %Y") end_timestamp = int(date_object.timestamp()) # 计算剩余天数 remain_day = (end_timestamp - current_timestamp) / 86400 # 打印域名、IP 地址和证书过期时间信息 print(f"域名：{info['domain']},ip 地址：{info['ip']},证书过期时间：{info['end_date']}") # 根据剩余天数进行不同的提示 # 如果证书过期时间减去当前时间的天数小于七天的话，则提示需要准备更换证书了 if 0 < remain_day < 7: print('剩余时间小于七天！请及时更换证书！') elif remain_day < 0: print('证书已过期！请及时更换证书！') else: print(f"剩余天数为:{remain_day:.2f}天\n")最后我们执行一下代码，看看结果如何完整代码import ssl import socket import time from datetime import datetime def ssl_connect(domain, ip): # 设置socket的超时时间为5秒 socket.setdefaulttimeout(5) # 创建默认的SSL上下文 context = ssl.create_default_context() # 创建一个SSL套接字 skt = context.wrap_socket(socket.socket(), server_hostname=domain) try: # 建立SSL连接 skt.connect((ip, 443)) # 获取证书过期时间 end_date = skt.getpeercert()['notAfter'].strip(' GMT') # 创建一个字典，存储本次连接中的域名、IP 地址和证书过期时间信息 skt_info = { 'domain': domain, 'ip': ip, 'end_date': end_date} except ssl.CertificateError as e: cert = e except socket.timeout: cert = 'Connect refused' except ConnectionResetError as e: cert = 'Connect reset' + str(e) except socket.gaierror as e: cert = 'Connnect gaierror' finally: # 关闭SSL套接字 skt.close() return skt_info def check_cert_time(info): # 获取当前时间戳 current_timestamp = int(time.time()) # 将证书过期时间转换成时间戳 date_object = datetime.strptime(info['end_date'], "%b %d %H:%M:%S %Y") end_timestamp = int(date_object.timestamp()) # 计算剩余天数 remain_day = (end_timestamp - current_timestamp) / 86400 # 打印域名、IP 地址和证书过期时间信息 print(f"域名：{info['domain']},ip 地址：{info['ip']},证书过期时间：{info['end_date']}") # 根据剩余天数进行不同的提示 # 如果证书过期时间减去当前时间的天数小于七天的话，则提示需要准备更换证书了 if 0 < remain_day < 7: print('剩余时间小于七天！请及时更换证书！') elif remain_day < 0: print('证书已过期！请及时更换证书！') else: print(f"剩余天数为:{remain_day:.2f}天\n") if __name__ == "__main__": domains = { } with open('domain.txt', 'r', encoding='utf-8') as file: for line in file: domain, ip_pool = line.strip().split(':') domains[domain] = ip_pool.split(',') info = [ssl_connect(domain, ip) for domain, ip_pool in domains.items() for ip in ip_pool] [check_cert_time(i) for i in info]小改动一下import ssl import socket import time from datetime import datetime def ssl_connect(domain, ip): if ip == None: return {'ok': 0, 'domain': domain, 'ip': '', 'end_date': '', 'msg': '获取ip失败！'} socket.setdefaulttimeout(5) context = ssl.create_default_context() skt = context.wrap_socket(socket.socket(), server_hostname=domain) ok = 0 end_date = '' cert = '' try: skt.connect((ip, 443)) end_date = skt.getpeercert()['notAfter'].removesuffix(' GMT') ok = 1 except ssl.CertificateError as e: cert = '证书错误：' + str(e) except socket.timeout: cert = '连接被拒绝。' except ConnectionResetError as e: cert = '连接重置：' + str(e) except socket.gaierror as e: cert = '连接错误：' + str(e) finally: skt.close() skt_info = {'ok':ok,'domain': domain, 'ip': ip, 'end_date': end_date, 'msg':cert} return skt_info def check_cert_time(info): if info['ok'] == 0: return [0,f"域名：{info['domain']}； ip 地址：--； 证书过期时间：--。\n出现错误，原因：{info['msg']}\n"] current_timestamp = int(time.time()) date_object = datetime.strptime(info['end_date'], "%b %d %H:%M:%S %Y") end_timestamp = int(date_object.timestamp()) remain_day = (end_timestamp - current_timestamp) / 86400 formatted_date = date_object.strftime("%Y-%m-%d %H:%M:%S") reStr = f"域名：{info['domain']}； ip 地址：{info['ip']}； 证书过期时间：{formatted_date}。\n" if 0 < remain_day < 7: reStr +="剩余时间小于七天！请及时更换证书！\n" t = 3 elif remain_day < 0: reStr +="证书已过期！请及时更换证书！\n" t = 2 else: t = 1 reStr +=f"剩余天数为:{remain_day:.2f}天\n" return [t,reStr] def resolve_domain(domain): try: ip_address = socket.gethostbyname(domain) return ip_address except socket.gaierror as e: return None if __name__ == "__main__": #需要检查的域名 queryDomain = [ "www.baidu.com", ] originalDomainName=[] for d in queryDomain: ip_address = resolve_domain(d) originalDomainName.append(f"{d}:{ip_address}") domains = {} for line in originalDomainName: domain, ip_pool = line.strip().split(':') domains[domain] = ip_pool.split(',') info = [ssl_connect(domain, ip) for domain, ip_pool in domains.items() for ip in ip_pool] finalResult = {} print("") for i in info: result = check_cert_time(i) print(result[1])来源：https://developer.aliyun.com/article/1294711
[python+tkinter]实现选择目录将目录下多个文件夹内的文件复制到一个文件夹中
缘由：朋友有一个这样的需求，有三个文件夹，文件夹A内含有很多txt文件，文件夹B中也内含有很多txt文件，文件夹C中还是含有很多txt文件。需要把txt文件复制到同个文件夹中功能：选择目录将目录下多个文件夹内的文件复制到一个文件夹中运行图：下载地址：包含打包好的成品和源代码。python打包使用的是Pyinstaller打包。链接：https://ide.lanzouv.com/b0foqio5i密码:ao4j更新：2024.08.27 v1.1.01.增加线程防止界面复制时候处于 未响应 状态2.增加进度条显示2024.08.27 v1.0.0初始版本源代码：一共有4个文件：main.py、ui.py、control.py、logo.icomain.py# 导入布局文件 from ui import Win as MainWin # 导入窗口控制器 from control import Controller as MainUIController # 将窗口控制器 传递给UI app = MainWin(MainUIController()) if __name__ == "__main__": # 启动 app.mainloop()ui.pyfrom tkinter import * from tkinter.ttk import * class WinGUI(Tk): def __init__(self): super().__init__() self.__win() self.tk_label_m0c6l6hz = self.__tk_label_m0c6l6hz(self) self.tk_input_m0c6lpcn = self.__tk_input_m0c6lpcn(self) self.tk_button_m0c6lvy7 = self.__tk_button_m0c6lvy7(self) self.tk_label_m0c6mm3a = self.__tk_label_m0c6mm3a(self) self.tk_input_m0c6n6vi = self.__tk_input_m0c6n6vi(self) self.tk_button_m0c6nb3j = self.__tk_button_m0c6nb3j(self) self.tk_button_m0c7djh2 = self.__tk_button_m0c7djh2(self) self.tk_progressbar_m0d8ueyu = self.__tk_progressbar_m0d8ueyu(self) self.tk_label_m0d8xhee = self.__tk_label_m0d8xhee(self) self.tk_label_m0dhb051 = self.__tk_label_m0dhb051(self) def __win(self): self.title("多个文件夹内的文件复制到一个文件夹中_v1.1.0") # 设置窗口大小、居中 width = 572 height = 263 screenwidth = self.winfo_screenwidth() screenheight = self.winfo_screenheight() geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2) self.geometry(geometry) self.resizable(width=False, height=False) def scrollbar_autohide(self, vbar, hbar, widget): """自动隐藏滚动条""" def show(): if vbar: vbar.lift(widget) if hbar: hbar.lift(widget) def hide(): if vbar: vbar.lower(widget) if hbar: hbar.lower(widget) hide() widget.bind("<Enter>", lambda e: show()) if vbar: vbar.bind("<Enter>", lambda e: show()) if vbar: vbar.bind("<Leave>", lambda e: hide()) if hbar: hbar.bind("<Enter>", lambda e: show()) if hbar: hbar.bind("<Leave>", lambda e: hide()) widget.bind("<Leave>", lambda e: hide()) def v_scrollbar(self, vbar, widget, x, y, w, h, pw, ph): widget.configure(yscrollcommand=vbar.set) vbar.config(command=widget.yview) vbar.place(relx=(w + x) / pw, rely=y / ph, relheight=h / ph, anchor='ne') def h_scrollbar(self, hbar, widget, x, y, w, h, pw, ph): widget.configure(xscrollcommand=hbar.set) hbar.config(command=widget.xview) hbar.place(relx=x / pw, rely=(y + h) / ph, relwidth=w / pw, anchor='sw') def create_bar(self, master, widget, is_vbar, is_hbar, x, y, w, h, pw, ph): vbar, hbar = None, None if is_vbar: vbar = Scrollbar(master) self.v_scrollbar(vbar, widget, x, y, w, h, pw, ph) if is_hbar: hbar = Scrollbar(master, orient="horizontal") self.h_scrollbar(hbar, widget, x, y, w, h, pw, ph) self.scrollbar_autohide(vbar, hbar, widget) def __tk_label_m0c6l6hz(self, parent): label = Label(parent, text="需要复制的文件夹：", anchor="center", ) label.place(x=14, y=8, width=116, height=30) return label def __tk_input_m0c6lpcn(self, parent): ipt = Entry(parent, ) ipt.place(x=14, y=40, width=454, height=30) return ipt def __tk_button_m0c6lvy7(self, parent): btn = Button(parent, text="选择路径", takefocus=False, ) btn.place(x=486, y=40, width=73, height=30) return btn def __tk_label_m0c6mm3a(self, parent): label = Label(parent, text="保存的路径：", anchor="center", ) label.place(x=14, y=80, width=81, height=30) return label def __tk_input_m0c6n6vi(self, parent): ipt = Entry(parent, ) ipt.place(x=14, y=112, width=454, height=30) return ipt def __tk_button_m0c6nb3j(self, parent): btn = Button(parent, text="选择路径", takefocus=False, ) btn.place(x=485, y=112, width=73, height=30) return btn def __tk_button_m0c7djh2(self, parent): btn = Button(parent, text="开始复制", takefocus=False, ) btn.place(x=302, y=178, width=224, height=71) return btn def __tk_progressbar_m0d8ueyu(self, parent): progressbar = Progressbar(parent, orient=HORIZONTAL, ) progressbar.place(x=14, y=184, width=195, height=30) return progressbar def __tk_label_m0d8xhee(self, parent): label = Label(parent, text="复制进度：", anchor="center", ) label.place(x=14, y=152, width=57, height=30) return label def __tk_label_m0dhb051(self, parent): label = Label(parent, text="", anchor="center", ) label.place(x=14, y=221, width=219, height=30) return label class Win(WinGUI): def __init__(self, controller): self.ctl = controller super().__init__() self.__event_bind() self.__style_config() self.ctl.init(self) def __event_bind(self): self.tk_button_m0c6lvy7.bind('<Button-1>', self.ctl.choosePatha) self.tk_button_m0c6nb3j.bind('<Button-1>', self.ctl.choosePathb) self.tk_button_m0c7djh2.bind('<Button-1>', self.ctl.startCopy) pass def __style_config(self): pass if __name__ == "__main__": win = WinGUI() win.mainloop()control.pyimport threading import os import shutil import tkinter as tk from tkinter import filedialog from tkinter.messagebox import showinfo class Controller: # 导入UI类后，替换以下的 object 类型，将获得 IDE 属性提示功能 ui: object def __init__(self): self.monitor_running = None def init(self, ui): self.ui = ui # TODO 组件初始化 赋值操作 def choosePatha(self,evt): if self.monitor_running == True: return False directory_path = filedialog.askdirectory( title="选择文件夹" ) # 显示选定的文件夹路径 if directory_path: # print("选择的文件夹:", directory_path) # 更新界面或其他逻辑 self.ui.tk_input_m0c6lpcn.delete(0, tk.END) self.ui.tk_input_m0c6lpcn.insert(0, directory_path) def choosePathb(self, evt): if self.monitor_running == True: return False directory_path = filedialog.askdirectory( title="选择文件夹" ) # 显示选定的文件夹路径 if directory_path: # print("选择的文件夹:", directory_path) # 更新界面或其他逻辑 self.ui.tk_input_m0c6n6vi.delete(0, tk.END) self.ui.tk_input_m0c6n6vi.insert(0, directory_path) def startCopy(self,evt): if self.monitor_running == True: return # 如果监控已经在运行，则不执行任何操作 fromPath = self.ui.tk_input_m0c6lpcn.get() savePath = self.ui.tk_input_m0c6n6vi.get() if fromPath == "" or savePath == "": showinfo(title="提示", message="两个路径都需要输入！") return False if fromPath in savePath: showinfo(title="提示", message="保存的路径不能选择到需要复制的文件夹内！") return False self.stop_event = threading.Event() self.thread = threading.Thread(target=self.update_counter, args=(fromPath,savePath)) self.thread.daemon = True self.thread.start() self.ui.tk_button_m0c6lvy7.config(state=tk.DISABLED) self.ui.tk_button_m0c6nb3j.config(state=tk.DISABLED) self.ui.tk_button_m0c7djh2.config(state=tk.DISABLED) self.ui.tk_input_m0c6lpcn.config(state="readonly") self.ui.tk_input_m0c6n6vi.config(state="readonly") self.monitor_running = True def update_counter(self, fromPath,savePath): self.CreateDir(savePath) fileNum = self.count_files_in_directory(fromPath) self.ui.tk_progressbar_m0d8ueyu.config(length=fileNum,maximum=fileNum) self.ui.tk_progressbar_m0d8ueyu["value"] = 0 self.ui.update() self.CopyFile(fromPath, savePath, fileNum) successfulText = "成功复制了" + str(fileNum) + "个文件" self.ui.tk_label_m0dhb051.config(text=successfulText) showinfo(title="提示", message="复制成功！") # 按钮启用 self.ui.tk_button_m0c6lvy7.config(state=tk.NORMAL) self.ui.tk_button_m0c6nb3j.config(state=tk.NORMAL) self.ui.tk_button_m0c7djh2.config(state=tk.NORMAL) self.ui.tk_input_m0c6lpcn.config(state="normal") self.ui.tk_input_m0c6n6vi.config(state="normal") self.monitor_running = False self.stop_event.set() # 设置停止事件 self.stop_event.clear() #置0 self.ui.tk_progressbar_m0d8ueyu["value"] = 0 self.ui.update() return True def count_files_in_directory(self,directory_path): # 初始化文件计数 file_count = 0 # 使用 os.walk 遍历文件夹及其子目录 for root, dirs, files in os.walk(directory_path): # 对于每个目录，累加文件数量 file_count += len(files) # 返回文件总数 return file_count def CopyFile(self,filepath, newPath, fileNum): # 获取当前路径下的文件名，返回List fileNames = os.listdir(filepath) for file in fileNames: # 将文件命加入到当前文件路径后面 newDir = filepath + '/' + file # 如果是文件 if os.path.isfile(newDir): newFile = newPath + '/'+ file shutil.copyfile(newDir, newFile) self.ui.tk_progressbar_m0d8ueyu["value"] += 1 self.ui.update() successfulText = "进度：" + str(self.ui.tk_progressbar_m0d8ueyu["value"]) + " / " + str(fileNum) self.ui.tk_label_m0dhb051.config(text=successfulText) # 如果不是文件，递归这个文件夹的路径 else: self.CopyFile(newDir, newPath, fileNum) def CreateDir(self,path): isExists = os.path.exists(path) # 判断结果 if not isExists: # 如果不存在则创建目录 os.makedirs(path) # print(path + ' 目录创建成功') # else: # 如果目录存在则不创建，并提示目录已存在 # print(path + ' 目录已存在')
我们常常会用到判断某个字符或者字符串是否包含中文，英文，特殊符号等等。这时候可以通过判断Unicode所在区间来确定某个字符所处类型。当然可以通过直接判断Unicode码，但是鉴于习惯，以下提供的是Unicode码对应的数字区间。毕竟字符的本质也就是通过二进制进行存储编码的而已。基本汉字：[0x4e00,0x9fa5](或十进制[19968,40869]) 数字：[0x 0030,0x0039](或十进制[48, 57]) 小写字母：[0x0061,0x007a](或十进制[97, 122]) 大写字母：[0x0041,0x005a](或十进制[65, 90]) Unicode都是两个字节，由于长度统一，所以比较适合程序处理。 再来一个比较完整的Unicode分布： 0000-007F：C0控制符及基本拉丁文 (C0 Control and Basic Latin) 0080-00FF：C1控制符及拉丁文补充-1 (C1 Control and Latin 1 Supplement) 0100-017F：拉丁文扩展-A (Latin Extended-A) 0180-024F：拉丁文扩展-B (Latin Extended-B) 0250-02AF：国际音标扩展 (IPA Extensions) 02B0-02FF：空白修饰字母 (Spacing Modifiers) 0300-036F：结合用读音符号 (Combining Diacritics Marks) 0370-03FF：希腊文及科普特文 (Greek and Coptic) 0400-04FF：西里尔字母 (Cyrillic) 0500-052F：西里尔字母补充 (Cyrillic Supplement) 0530-058F：亚美尼亚语 (Armenian) 0590-05FF：希伯来文 (Hebrew) 0600-06FF：阿拉伯文 (Arabic) 0700-074F：叙利亚文 (Syriac) 0750-077F：阿拉伯文补充 (Arabic Supplement) 0780-07BF：马尔代夫语 (Thaana) 07C0-077F：西非書面語言 (N'Ko) 0800-085F：阿维斯塔语及巴列维语 (Avestan and Pahlavi) 0860-087F：Mandaic 0880-08AF：撒马利亚语 (Samaritan) 0900-097F：天城文书 (Devanagari)来源：https://blog.csdn.net/weixin_34206263/article/details/11203186