小野（Hirono）博客的个人网站，主要讲述关于小野的一些陈芝麻烂谷子事。网络、生活、我的主张，也是一个自留地
把多余的VPS小鸡，拿来搭建一个专属图床用于博客配图、日常存图，又不希望将它开放为公共平台推荐EasyImage2.0 (简单图床) + PicGo图床下载地址：{cat_download name="EasyImages2.0" url="https://github.com/icret/EasyImages2.0" password=""}开启个人使用安装成功后，登录后台，按照截图开启相应功能即可。“登录上传” —— 防止游客上传，仅限个人使用，保护仅有的小硬盘“开启 API 上传” —— 搭配 PicGo 使用，无须打开网页上传图片Picgo 自定义上传API下载PicGo：{cat_download name="PicGo开源地址" url="https://github.com/Molunerfinn/PicGo" password=""}安装后在插件设置中搜索：web-uploader 1.1.1并安装。图床设置 - 自定义 Web 图床中按照如下方式填写，然后点击确定并设置为默认图床登录图床后台 --> 图床安全 --> 高级设置 --> 开启 API 上传自定义 Web 图床参数API 地址：https://你的域名/api/index.php # 输入你的网站 api 地址 后台API设置有提示地址 POST 参数名: image JSON 路径: url 自定义 Body: {"token":"1xxxxxx3cb5e131"} # 这里输入你网站生成的 token上传成功截图
萝卜PHP探针 - 轻巧好用的服务器检测工具-开源版
简单介绍单文件，不占资源功能实在，没有花哨截图<?php // index.php - PHP探针 error_reporting(0); @ini_set('display_errors', 0); // 1. 基础信息获取（完全安全） function getBasicInfo() { $info = []; // 使用安全的获取方式 $info['服务器名称'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '未知'; $info['服务器软件'] = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '未知'; $info['PHP版本'] = PHP_VERSION; $info['操作系统'] = @php_uname('s') . ' ' . @php_uname('r'); $info['服务器时间'] = date('Y-m-d H:i:s'); $info['运行模式'] = PHP_SAPI; // 内存信息（安全获取） $info['内存使用'] = formatBytes(@memory_get_usage(true)); $info['内存峰值'] = formatBytes(@memory_get_peak_usage(true)); return $info; } // 2. 安全的性能测试（简化版） function getPerformance() { $results = []; // 整数运算测试 $start = microtime(true); $result = 0; $iterations = 100000; // 减少迭代次数 for ($i = 0; $i < $iterations; $i++) { $result += $i * $i; } $time_taken = max(microtime(true) - $start, 0.001); $results['整数运算'] = [ 'score' => intval($iterations / $time_taken), 'time' => round($time_taken * 1000, 2) ]; return $results; } // 3. 组件检测（安全版） function getComponents() { $components = []; // 只检测常用组件 $extensions = [ 'MySQLi' => 'mysqli', 'PDO' => 'pdo', 'PDO MySQL' => 'pdo_mysql', 'Redis' => 'redis', 'GD Library' => 'gd', 'cURL' => 'curl', 'OpenSSL' => 'openssl', 'MBString' => 'mbstring', 'JSON' => 'json', 'XML' => 'xml', 'ZIP' => 'zip', 'OPcache' => 'Zend OPcache' ]; foreach ($extensions as $name => $ext) { $components[$name] = @extension_loaded($ext); } return $components; } // 4. 磁盘信息（安全获取） function getDiskInfo() { $info = []; $path = __DIR__; $total = @disk_total_space($path); $free = @disk_free_space($path); if ($total !== false && $free !== false && $total > 0) { $used = $total - $free; $usage_percent = round(($used / $total) * 100, 1); $info['总容量'] = formatBytes($total); $info['已使用'] = formatBytes($used); $info['可用空间'] = formatBytes($free); $info['使用率'] = $usage_percent; } else { $info['总容量'] = '未知'; $info['已使用'] = '未知'; $info['可用空间'] = '未知'; $info['使用率'] = 0; } return $info; } // 5. PHP配置（安全获取） function getPHPConfig() { $config = []; $settings = [ 'memory_limit', 'max_execution_time', 'post_max_size', 'upload_max_filesize', 'max_file_uploads', 'default_charset', 'display_errors', 'error_reporting' ]; foreach ($settings as $setting) { $value = @ini_get($setting); $config[$setting] = $value !== false ? $value : '未知'; } return $config; } // 6. 安全状态 function getSecurityStatus() { $security = []; $security['错误显示'] = @ini_get('display_errors') ? '开启' : '关闭'; $security['远程文件访问'] = @ini_get('allow_url_fopen') ? '开启' : '关闭'; $security['PHP信息暴露'] = @ini_get('expose_php') ? '开启' : '关闭'; $security['目录限制'] = @ini_get('open_basedir') ? '已设置' : '未设置'; return $security; } // 7. 网络信息 function getNetworkInfo() { $network = []; $network['客户端IP'] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '未知'; $network['请求方法'] = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '未知'; // 简单的响应时间测试 $start = microtime(true); $test = 0; for ($i = 0; $i < 1000; $i++) { $test += $i; } $network['响应时间'] = round((microtime(true) - $start) * 1000, 2) . ' ms'; return $network; } // 辅助函数 function formatBytes($bytes) { if (!$bytes || $bytes <= 0) return '0 B'; $units = ['B', 'KB', 'MB', 'GB', 'TB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1 << (10 * $pow)); return round($bytes, 2) . ' ' . $units[$pow]; } function formatScore($score) { if ($score >= 1000000) { return round($score / 1000000, 1) . 'M'; } elseif ($score >= 1000) { return round($score / 1000, 1) . 'K'; } return $score; } // 获取所有数据 $server_info = getBasicInfo(); $performance = getPerformance(); $components = getComponents(); $disk_info = getDiskInfo(); $php_config = getPHPConfig(); $security = getSecurityStatus(); $network_info = getNetworkInfo(); // 计算健康度 $health_score = 85; // 默认良好 ?> 萝卜-PHP服务器探针 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" /> 萝卜服务器监控探针 开源产品 - 稳定运行 检测时间: 系统健康度 100% <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> 系统状态良好 服务器状态 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" /> 运行环境 性能测试 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /> 整数运算 磁盘使用 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" /> 可用 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" /> 服务器信息 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" /> 性能指标 次/秒 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> 组件支持状态 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /> PHP配置信息 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" /> 安全状态 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" /> 网络信息 萝卜服务器监控探针 © <?php echo date('Y'); ?> - 开源版 稳定运行 | 全面检测 | 安全可靠
今天逛论坛发现有位坛友贴了一段加密代码。问解密难度多大。先看看源代码：<?php echo(gzinflate(base64_decode('[binary]')));这段代码使用的是一种常见的“混淆”方式;我们来分解一下eval(gzinflate(base64_decode('加密代码')));有 base64 编码再用 gzinflate 压缩最后用 eval 执行那如何解密呢？你只需要把 eval 换成 echo 然后浏览器运行跑一下就能还原出原始代码。解密后源代码：/* 加密时间: 2025-10-22 10:47:39 */ $___d41d8='';$___d41d8.=base64_decode('JF9TRVJWRVJb');$___d41d8.=base64_decode('IlJFUVVFU1Rf');$___d41d8.=base64_decode('VVJJIl0=');if(isset($___d41d8) && strpos($_SERVER["REQUEST_URI"], base64_decode('eGRlYnVn')) !== false) exit(base64_decode('5o6l5Y+35qC35LiO77yB')); $___ebd91=base64_decode('cnVua2l0X2Z1bmN0aW9uX3JlZGVmaW5l');if(function_exists($___ebd91)) exit(base64_decode('5o6l5Y+35qC35LiO77yB')); $__funcs=array ( '__full_file__' => '[binary]:4db0b902cec38d5cf0f3d979f5f5c2f0', ); $__salts=array ( '__full_file__' => 'ce390a4e1dfc67b3', ); $__hmacs=array ( '__full_file__' => 'b1415fc56f594e4469d7a47a960e1ba0aa7c900cb8f73bf701449374f827382e', ); $__signatures=array ( '__full_file__' => 'e6e2be52022af457d6476915e7ebb1fa3e493a531b47738b6be2200ebdae84a7ed2605a60b99c6ee70271400c5dd2d4484f80e29e84ad5f87eeddafa0ff19100', ); $__randData=array ( '278edf6f' => 'b665a43e', 'de80dcf3' => 'f3dbc6d2ad84aa', 'bed840e8' => 'NDExMGIxZTQyZTQ4ZWU3ZmM2YjE=', '4a1fe109' => '0c0b92fc8da7a4', '40d454dd' => '5045e44f273606e5', 'fc139939' => '5fe9a6b5', '5b01761d' => 'qs8o2s9sr132172nn5or', 'f373a44b' => 'f0fbca37cdd4', '6f60a960' => 'fe6659baba', 'a8e430f6' => 'n8p98s34q27n5qq14546667r', ); $__keyMap=array ( 0 => 'bed840e8', 1 => '5b01761d', 2 => 'a8e430f6', ); $__segmentCompressMethods=array ( '__full_file__' => 'gzip', ); // 增强的安全密钥处理 $__mk_part1 = strrev(base64_decode($__randData[$__keyMap[0]])); $__mk_part2 = str_rot13($__randData[$__keyMap[1]]); $__mk_part3 = str_rot13($__randData[$__keyMap[2]]); $__masterKey = $__mk_part1 . $__mk_part2 . $__mk_part3; unset($__mk_part1, $__mk_part2, $__mk_part3); $__cache=[]; function f515f13ccdddd7066($n){ global $__funcs, $__salts, $__hmacs, $__signatures, $__masterKey, $__cache, $__segmentCompressMethods; if(isset($__cache[$n])) return; if(!isset($__funcs[$n])) throw new Exception(base64_decode('RnVuY3Rpb24gbm90IGZvdW5kOiB7JG59')); $key = hash_hmac('sha256', $__salts[$n], $__masterKey); list($cb64, $iv) = explode(':', $__funcs[$n]); $cc = base64_decode($cb64, true); if($cc === false) throw new Exception(base64_decode('QmFzZTY057yW56iL5a2X56ym5Liy')); $ivv = hex2bin($iv); if($ivv === false) throw new Exception(base64_decode('SVbil6jmiKvlpKflkI0=')); $dd = openssl_decrypt($cc, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $ivv); if($dd === false) throw new Exception(base64_decode('QUVT57yW56iL5a2X6YOo5pyJ')); $compressMethod = isset($__segmentCompressMethods[$n]) ? $__segmentCompressMethods[$n] : 'gzip'; if($compressMethod === 'zstd' && function_exists('zstd_uncompress')) { $phpCode = zstd_uncompress($dd); } else { $phpCode = gzinflate($dd); } if($phpCode === false) throw new Exception(base64_decode('5byg5bCP5LmL')); if(hash_hmac('sha512', $phpCode, $__masterKey) !== $__signatures[$n]) { throw new Exception(base64_decode('6Z2e57uE6K6+56iL5L2V5LyB5Lia5L2g5rO9')); // 签名校验失败 } if(hash_hmac('sha256', $phpCode, $__masterKey) !== $__hmacs[$n]) exit(base64_decode('Q29kZSBmb3IgeyRufSBoYXMgYmVlbiB0YW1wZXJlZCE=')); eval("?>" . $phpCode); unset($__masterKey, $phpCode); gc_collect_cycles(); static $exec_cache = []; if(!isset($exec_cache[$n])) $exec_cache[$n] = true; $__cache[$n] = true; register_shutdown_function(function() use ($n) { if ($n !== '__full_file__') { exit(base64_decode('5bCP546L5Y2K5YWl77yB')); } }); } f515f13ccdddd7066('__full_file__');
区域选择录制：支持全屏或自定义区域录制实时预览：选择区域后可立即预览参数自定义：可调节FPS、录制时长友好界面：直观的GUI操作界面安装依赖库pip install pillow pyautogui pyinstaller核心源码import tkinter as tk from tkinter import ttk, filedialog, messagebox import pyautogui from PIL import Image, ImageTk, ImageGrab import threading import time import os class GIFRecorderWithRegion: def __init__(self, root): self.root = root self.root.title("GIF录制工具 - 带区域选择") self.root.geometry("500x400") self.recording = False self.selecting_region = False self.frames = [] self.output_path = "favicon.ico" self.region = None # (x1, y1, x2, y2) self.setup_ui() def setup_ui(self): """设置用户界面""" # 输出路径选择 path_frame = ttk.Frame(self.root) path_frame.pack(pady=10, padx=20, fill="x") ttk.Label(path_frame, text="输出路径:").pack(side="left") self.path_var = tk.StringVar(value=self.output_path) ttk.Entry(path_frame, textvariable=self.path_var, width=30).pack(side="left", padx=5) ttk.Button(path_frame, text="浏览", command=self.browse_path).pack(side="left") # 区域选择 region_frame = ttk.LabelFrame(self.root, text="录制区域/////LB5.NET-小野博客//////", padding=10) region_frame.pack(pady=10, padx=20, fill="x") self.region_var = tk.StringVar(value="全屏") ttk.Radiobutton(region_frame, text="全屏", variable=self.region_var, value="全屏", command=self.update_region_display).pack(anchor="w") ttk.Radiobutton(region_frame, text="自定义区域", variable=self.region_var, value="自定义区域", command=self.update_region_display).pack(anchor="w") # 区域坐标显示和选择 coord_frame = ttk.Frame(region_frame) coord_frame.pack(fill="x", pady=5) ttk.Label(coord_frame, text="区域坐标:").pack(side="left") self.coord_var = tk.StringVar(value="未选择") ttk.Label(coord_frame, textvariable=self.coord_var, foreground="green").pack(side="left", padx=5) ttk.Button(coord_frame, text="选择区域", command=self.start_region_selection).pack(side="left", padx=10) # 参数设置 param_frame = ttk.LabelFrame(self.root, text="录制参数", padding=10) param_frame.pack(pady=10, padx=20, fill="x") ttk.Label(param_frame, text="FPS:").grid(row=0, column=0, sticky="w") self.fps_var = tk.StringVar(value="10") ttk.Spinbox(param_frame, from_=1, to=30, textvariable=self.fps_var, width=10).grid(row=0, column=1, padx=5) ttk.Label(param_frame, text="时长(秒):").grid(row=1, column=0, sticky="w", pady=5) self.duration_var = tk.StringVar(value="10") ttk.Spinbox(param_frame, from_=1, to=60, textvariable=self.duration_var, width=10).grid(row=1, column=1, padx=5) # 控制按钮 btn_frame = ttk.Frame(self.root) btn_frame.pack(pady=20) self.record_btn = ttk.Button(btn_frame, text="开始录制", command=self.toggle_recording, width=15) self.record_btn.pack(pady=5) # 状态显示 self.status_var = tk.StringVar(value="准备就绪") status_label = ttk.Label(self.root, textvariable=self.status_var, foreground="blue") status_label.pack(pady=10) # 预览区域 preview_frame = ttk.LabelFrame(self.root, text="区域预览", padding=5) preview_frame.pack(pady=10, padx=20, fill="both", expand=True) self.preview_label = ttk.Label(preview_frame, text="区域预览将显示在这里") self.preview_label.pack(expand=True) def browse_path(self): """选择输出路径""" path = filedialog.asksaveasfilename( defaultextension=".gif", filetypes=[("GIF文件", "*.gif"), ("所有文件", "*.*")] ) if path: self.path_var.set(path) def update_region_display(self): """更新区域显示""" if self.region_var.get() == "全屏": self.coord_var.set("全屏") self.update_preview() elif self.region and self.region_var.get() == "自定义区域": x1, y1, x2, y2 = self.region self.coord_var.set(f"({x1}, {y1}) - ({x2}, {y2})") self.update_preview() def start_region_selection(self): """开始区域选择""" self.status_var.set("请选择录制区域...") self.root.withdraw() # 隐藏主窗口 # 创建区域选择窗口 self.region_selector = RegionSelector(self.root, self.region_selected) def region_selected(self, region): """区域选择回调""" self.region = region self.region_var.set("自定义区域") self.root.deiconify() # 重新显示主窗口 self.update_region_display() self.status_var.set("区域选择完成") def update_preview(self): """更新区域预览""" try: if self.region_var.get() == "全屏": # 全屏预览 - 缩小显示 screenshot = pyautogui.screenshot() preview_size = (300, 200) screenshot.thumbnail(preview_size, Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(screenshot) self.preview_label.config(image=photo) self.preview_label.image = photo elif self.region and self.region_var.get() == "自定义区域": # 自定义区域预览 x1, y1, x2, y2 = self.region region_screenshot = pyautogui.screenshot(region=(x1, y1, x2-x1, y2-y1)) preview_size = (300, 200) region_screenshot.thumbnail(preview_size, Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(region_screenshot) self.preview_label.config(image=photo) self.preview_label.image = photo except Exception as e: self.preview_label.config(text=f"预览更新失败: {str(e)}") def toggle_recording(self): """开始/停止录制""" if not self.recording: self.start_recording() else: self.stop_recording() def start_recording(self): """开始录制""" self.output_path = self.path_var.get() fps = int(self.fps_var.get()) duration = int(self.duration_var.get()) if not self.output_path: messagebox.showerror("错误", "请选择输出路径") return self.recording = True self.frames = [] self.record_btn.config(text="停止录制") self.status_var.set("录制中...") # 在新线程中录制 self.record_thread = threading.Thread( target=self.record_screen, args=(fps, duration) ) self.record_thread.daemon = True self.record_thread.start() def stop_recording(self): """停止录制""" self.recording = False self.record_btn.config(text="开始录制") self.status_var.set("正在保存GIF...") def record_screen(self, fps, duration): """录制屏幕""" start_time = time.time() interval = 1.0 / fps while self.recording and (time.time() - start_time) < duration: frame_start = time.time() try: # 根据选择的区域截屏 if self.region_var.get() == "全屏": screenshot = pyautogui.screenshot() else: if self.region: x1, y1, x2, y2 = self.region screenshot = pyautogui.screenshot(region=(x1, y1, x2-x1, y2-y1)) else: screenshot = pyautogui.screenshot() self.frames.append(screenshot) # 更新状态 elapsed = time.time() - start_time remaining = duration - elapsed self.status_var.set(f"录制中... 剩余时间: {remaining:.1f}秒") except Exception as e: print(f"截图失败: {e}") # 控制帧率 elapsed_frame = time.time() - frame_start sleep_time = interval - elapsed_frame if sleep_time > 0: time.sleep(sleep_time) # 保存GIF if self.frames: self.save_gif() def save_gif(self): """保存GIF文件""" try: if self.frames: self.frames[0].save( self.output_path, save_all=True, append_images=self.frames[1:], duration=1000//int(self.fps_var.get()), loop=0, optimize=True ) self.status_var.set(f"GIF已保存: {self.output_path}") messagebox.showinfo("成功", f"GIF已保存到:\n{self.output_path}\n共{len(self.frames)}帧") else: self.status_var.set("没有录制到帧") messagebox.showwarning("警告", "没有录制到任何帧") except Exception as e: self.status_var.set("保存失败") messagebox.showerror("错误", f"保存失败: {str(e)}") class RegionSelector: """区域选择器""" def __init__(self, parent, callback): self.parent = parent self.callback = callback self.start_x = None self.start_y = None self.current_x = None self.current_y = None self.region = None # 创建全屏透明窗口 self.selector = tk.Toplevel(parent) self.selector.attributes('-fullscreen', True) self.selector.attributes('-alpha', 0.3) self.selector.configure(bg='black') self.selector.attributes('-topmost', True) # 创建画布用于绘制选择框 self.canvas = tk.Canvas(self.selector, highlightthickness=0) self.canvas.pack(fill=tk.BOTH, expand=True) # 绑定事件 self.canvas.bind('<Button-1>', self.on_button_press) self.canvas.bind('<B1-Motion>', self.on_mouse_drag) self.canvas.bind('<ButtonRelease-1>', self.on_button_release) self.selector.bind('<Escape>', self.cancel_selection) self.selector.focus_set() def on_button_press(self, event): """鼠标按下""" self.start_x = event.x self.start_y = event.y self.rect = self.canvas.create_rectangle( self.start_x, self.start_y, self.start_x, self.start_y, outline='red', width=2, fill='blue', stipple='gray50' ) def on_mouse_drag(self, event): """鼠标拖动""" self.current_x = event.x self.current_y = event.y self.canvas.coords(self.rect, self.start_x, self.start_y, self.current_x, self.current_y) def on_button_release(self, event): """鼠标释放""" self.current_x = event.x self.current_y = event.y # 确保坐标正确（左上角到右下角） x1 = min(self.start_x, self.current_x) y1 = min(self.start_y, self.current_y) x2 = max(self.start_x, self.current_x) y2 = max(self.start_y, self.current_y) # 如果区域太小，认为是取消 if abs(x2 - x1) < 10 or abs(y2 - y1) < 10: self.cancel_selection() return self.region = (x1, y1, x2, y2) self.selector.destroy() self.callback(self.region) def cancel_selection(self, event=None): """取消选择""" self.selector.destroy() self.callback(None) class AdvancedGIFRecorder(GIFRecorderWithRegion): def __init__(self, root): super().__init__(root) self.root.title("GIF录制截图") # 添加更多功能按钮 advanced_frame = ttk.Frame(self.root) advanced_frame.pack(pady=5) ttk.Button(advanced_frame, text="测试区域", command=self.test_region).pack(side="left", padx=5) ttk.Button(advanced_frame, text="清除区域", command=self.clear_region).pack(side="left", padx=5) def test_region(self): """测试选择的区域""" if self.region and self.region_var.get() == "自定义区域": try: x1, y1, x2, y2 = self.region # 短暂显示区域边框 test_window = tk.Toplevel(self.root) test_window.attributes('-topmost', True) test_window.geometry(f"{x2-x1}x{y2-y1}+{x1}+{y1}") test_window.overrideredirect(True) test_window.configure(bg='red', highlightthickness=2, highlightbackground='yellow') # 3秒后自动关闭 test_window.after(3000, test_window.destroy) except Exception as e: messagebox.showerror("错误", f"测试失败: {str(e)}") else: messagebox.showinfo("提示", "请先选择自定义区域") def clear_region(self): """清除区域选择""" self.region = None self.region_var.set("全屏") self.update_region_display() self.status_var.set("区域已清除") if __name__ == "__main__": root = tk.Tk() app = AdvancedGIFRecorder(root) root.mainloop()
想一次性把 YYDS 源码网 1-200 页的网盘资源全部扒下来？今天分享 30 行 Python 脚本，无需配置环境，双击即可运行，自动提取标题+真实外链，支持 7 大网盘，完全开源！1. 脚本功能顺序抓取 1-200 页文章列表进入详情页提取「侧边栏按钮 + 正文」网盘链接自动跟随 goto?down=xxx 跳转，拿到真实外链支持蓝奏云 / 百度 / 夸克 / 123 / 阿里 / 迅雷 / 腾讯保存格式：标题 | 网盘链接（txt 可直接复制）2. 运行环境项目版本Python3.7+依赖requests、beautifulsoup4一键安装：pip install requests beautifulsoup4 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 抓取 www.yydsym.com 保存格式：标题 | 网盘链接 输出文件：桌面 title_pan_links.txt """ import os import time import random import requests from bs4 import BeautifulSoup from urllib.parse import urljoin MAX_PAGE = 100 SLEEP = lambda: random.uniform(1.0, 2.0) TIMEOUT = 10 LIST_URL = "https://www.yydsym.com/page/{}" SAVE_FILE = os.path.join(os.path.expanduser("~"), "Desktop", "title_pan_links.txt") HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" } PAN_KEYS = ("lanzou", "lanzn", "pan.baidu", "quark.cn", "123pan.com", "alipan.com", "xunlei.com", "cloud.189.cn", "tc.qq.com") def get_list(page): url = LIST_URL.format(page) resp = requests.get(url, headers=HEADERS, timeout=TIMEOUT) resp.raise_for_status() soup = BeautifulSoup(resp.text, "html.parser") items = [] for art in soup.select('article.post-grid'): a = art.select_one('h2.entry-title a') if not a: continue title = a.get_text(strip=True) link = urljoin(url, a["href"]) items.append((title, link)) return items def extract_pan(html): soup = BeautifulSoup(html, "html.parser") pans = set() for a in soup.select('a[href*="goto?down="]'): short = urljoin("https://www.yydsym.com", a["href"]) try: r = requests.get(short, headers=HEADERS, allow_redirects=False, timeout=10) real = r.headers.get("Location", "") if r.status_code in (301, 302) else short pans.add(real) except Exception: continue for a in soup.find_all("a", href=True): href = a["href"].strip() if any(k in href for k in PAN_KEYS): pans.add(href) return pans def main(): exist = set() if os.path.isfile(SAVE_FILE): with open(SAVE_FILE, "r", encoding="utf-8") as f: for line in f: if "|" in line: exist.add(line.split("|", 1)[1].strip()) print("🔍 开始抓取列表页...") for p in range(1, MAX_PAGE + 1): try: items = get_list(p) print(f" 第 {p:3d} 页 | 本页 {len(items):2d} 篇") except Exception as e: print(f" 第 {p:3d} 页 | 错误：{e}") continue time.sleep(SLEEP()) print(" 扫描详情页...") for title, url in items: try: html = requests.get(url, headers=HEADERS, timeout=TIMEOUT).text pans = extract_pan(html) new = 0 for u in pans: if u in exist: continue new += 1 with open(SAVE_FILE, "a", encoding="utf-8") as f: f.write(f"{title} | {u}\n") exist.add(u) if new: print(f" +{new} 条 | {title[:30]}...") except Exception as e: print(f" 跳过 | {e}") time.sleep(SLEEP()) print(f"\n✅ 全部完成！文件已保存 → {SAVE_FILE}") if __name__ == "__main__": main()
8月08日，cdn.bytedance.com 官方放出消息：字节跳动静态资源公共库(因{cat_highlight color="green" text="黑产原因"}已下线，请不要再使用！临时解决方案一可将 lf3-cdn-tos.bytecdntp.com 等加速域名全部更换为 cdn.bytedance.com 也可正常使用，不知道什么时候失效！临时解决方案二切换至国内其他公共CDN服务 目前，国内仍有多个可靠的公共CDN服务可供选择，例如：BootCDN（由 Bootstrap 中文网支持）：覆盖常见的前端库，如 jQuery、Vue.js、React 等。Staticfile CDN：开源库托管服务，稳定性较高。又拍云和 七牛云：提供广泛的公共库资源与自定义加速服务。用户可通过更新项目中的资源链接，将原字节跳动CDN的URL替换为上述服务的对应链接。例如，原引用： 可替换为： 使用国际公共CDN服务 如果项目用户分布较广，或对加载速度有较高要求，可考虑使用国际公共CDN，例如：Cloudflare CDN：提供广泛的开源库支持，全球节点分布均匀。jsDelivr：免费且高性能，特别适合前端资源的加速。注意：选择国际CDN时需考虑网络延迟及合规性要求，尤其面向国内用户时可能需要备案或结合加速服务使用。
Hi，好久不见，目前正在自驾游！推荐一篇农民工大爷写的文章《我的母亲》
Certd自动化申请SSL证书部署腾讯云-宝塔面板
Certd自动化申请SSL证书并部署到腾讯云教程去年花了30元购买的AlphaSSL泛域名证书还有10天到期，目前最便宜的通配符也在130元左右(舍不得太贵了)，然后去群里请教了别人。推荐我使用Certd昨天在部署网站时尝试了Certd这款自动化SSL证书工具，记录下遇到疑问的操作过程供参考。Certd是一个开源的SSL证书自动化管理工具，能帮你轻松申请和部署免费SSL证书。下面教你如何使用Certd为网站自动申请证书并部署到腾讯云：准备工作准备一台Linux服务器(推荐Ubuntu/CentOS)拥有腾讯云账号并开通云解析DNS服务准备好要申请证书的域名4.宝塔Docker应用商店中找到certd一键安装。Certd自动化申请SSL证书并部署至腾讯云实践安装教程官网都有就不描述了。点击它👉(Certd宝塔安装文档)创建流水线证书部分填写内容截图(这里我选择的是谷歌证书):编辑流水线：七牛云需要提供Access授权(相关截图)又拍云提供注册帐号密码腾讯云是个重点，如果你开通了cdn并且使用了cos对象储存的，注意了只需要选择腾讯云部署到CDN-V2模块。cos桶的ssl证书跟cdn是一起共享的。最后选择部署宝塔面板成功截图(谷歌通配符90天有效期)
插件概述本插件为Typecho博客系统提供了一个强大的AI写作助手功能，通过对接DeepSeek API实现智能文章生成功能。插件在文章编辑页面底部添加AI写作面板，用户只需输入关键词和具体要求，即可快速生成高质量的文章内容。主要功能​AI文章生成​：基于DeepSeek API，根据关键词和要求生成文章内容​模型选择​：支持deepseek-chat（通用写作）和deepseek-coder（技术文章）两种模型​原创性调节​：通过原创性参数（0.1-1.0）控制生成内容的创造性​一键复制​：将生成的内容快速复制到剪贴板​简洁界面​：直观的用户界面，操作简单便捷安装步骤下载插件文件并解压将插件文件夹重命名为 AIDeepSeek上传到Typecho的插件目录 /usr/plugins/登录Typecho后台，进入"控制台" → "插件"找到"AIDeepSeek"插件并启用在插件设置中配置DeepSeek API密钥​ 配置插件​进入插件设置页面填写从DeepSeek官网获取的API密钥选择适合的模型（通用写作或技术文章）设置原创性参数（推荐值0.7）​ 使用AI写作​创建或编辑文章时，滚动到编辑器底部点击"使用AI写作"按钮展开AI面板在"文章主题/关键词"输入框中填写关键词（必填）在"具体要求"文本框中填写具体要求（可选）点击"生成文章内容"按钮获取AI创作使用"复制到剪贴板"按钮将结果插入编辑器注意事项​API密钥​：必须配置有效的DeepSeek API密钥才能使用​关键词要求​：至少需要输入一个关键词才能生成内容​生成时间​：根据内容长度可能需要10-30秒​内容质量​：生成内容的质量取决于关键词和要求的明确程度​原创性参数​：值越高生成内容越有创意，但也可能偏离主题常见问题Q1: 生成失败怎么办？检查API密钥是否正确确保网络连接正常尝试减少关键词数量或简化要求下载地址：{cat_download name="【Github仓库】AI自动生成文章插件" url="https://github.com/qqxt/AIDeepSeek" password=""}{cat_download name="【蓝奏云】AI自动生成文章插件" url="https://saonai.lanzouu.com/iFrpZ2xwamid" password=""}