编程案例:摄影照片展示网站纯前端实现(HTML、CSS、JS)

原文地址:https://itxiaozhang.com/spa-photography-gallery/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 需求分析 「视界捕手」是一个单页面应用(SPA),用于展示摄影照片,主要需求包括: 动态照片展示:用户可浏览高质量摄影作品,页面采用CSS网格布局,支持响应式展示。 无刷新分类筛选:点击分类标签后,页面内容动态更新,无需重新加载。 流畅的交互体验:页面切换采用淡入淡出动画,提升视觉效果。 纯前端实现:利用HTML、CSS和JavaScript完成所有功能,无需后端支持。 核心功能 图片动态加载:通过JavaScript控制图片的渲染,确保页面加载时按需显示。 CSS网格布局:使用Grid/Flexbox技术实现自适应排版,兼容不同设备。 分类筛选:提供分类导航,无刷新切换不同类别的照片。 过渡动画:点击分类时,图片通过淡入淡出效果切换,提升用户体验。 未来规划 图片交互优化:支持点击放大、左右滑动浏览等增强功能。 动态数据管理:使用JSON或本地存储动态加载图片信息。 动画与特效:增加CSS动画,提升视觉体验。 离线访问支持:结合PWA(渐进式网页应用)技术,实现离线浏览。 部分代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>图片展示框架</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: system-ui, sans-serif; min-height: 100vh; background: #f0f4f8; } /* 框架容器 */ .framework-container { max-width: 1200px; margin: 0 auto; padding: 20px; } /* 导航样式 */ .nav-bar { padding: 1rem; background: #1a365d; } .nav-button { padding: 8px 16px; margin: 0 5px; border-radius: 20px; background: rgba(255,255,255,0.1); color: white; border: none; } /* 图片网格布局 */ .gallery-grid { display: grid; gap: 15px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); } .grid-item { aspect-ratio: 4/3; background: #e2e8f0; border-radius: 8px; overflow: hidden; } /* 动效设计 */ .grid-item { transition: transform 0.3s ease; } .grid-item:hover { transform: scale(1.02); } @media (max-width: 768px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } } </style> </head> <body> <nav class="nav-bar"> <div class="framework-container"> <button class="nav-button active">全部</button> <button class="nav-button">分类1</button> <button class="nav-button">分类2</button> <button class="nav-button">分类3</button> </div> </nav> <main class="framework-container"> <div class="gallery-grid"> <div class="grid-item"></div> <div class="grid-item"></div> <div class="grid-item"></div> <div class="grid-item"></div> </div> </main> <script> // 基础交互逻辑框架 const navButtons = document.querySelectorAll('.nav-button'); navButtons.forEach(button => { button.addEventListener('click', () => { navButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); }); }); </script> </body> </html> 视频版本 哔哩哔哩 YouTube

2025年3月30日 · 2 分钟 · IT小章

用HTML、CSS和JavaScript写一个圣诞氛围的网页

原文地址:https://itxiaozhang.com/create-christmas-themed-webpage-with-html-css-javascript/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 介绍 使用HTML、CSS和JavaScript创建一个充满节日气氛的圣诞节网页,包含雪花飘落、圣诞树、蜡烛闪烁等动画效果。通过这些效果,网页能更好地营造出圣诞节的氛围,并增加互动性。 使用 将下面的源码复制到记事本中。 将文件保存为 .html 格式(例如 christmas_scene.html)。 双击保存的文件,即可在浏览器中打开并查看动感的圣诞节网页。 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 <!-- ================================ 作者:IT小章 网站:itxiaozhang.com 时间:2024年12月23日 Copyright © 2024 IT小章 ================================ --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Christmas Scene with Candles</title> <style> body, html { margin: 0; padding: 0; height: 100%; overflow: hidden; } .scene { background-color: black; width: 100vw; height: 100vh; overflow: hidden; position: relative; } #snowCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; filter: blur(1px); } .mainTree { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: min(60vw, 60vh); /* 增大圣诞树尺寸 */ color: #00ff00; z-index: 3; transition: all 0.3s ease; cursor: pointer; animation: sway 4s ease-in-out infinite; } .mainTree:hover { transform: translate(-50%, -50%) scale(1.05); } @keyframes sway { 0%, 100% { transform: translate(-50%, -50%) rotate(-1deg); } 50% { transform: translate(-50%, -50%) rotate(1deg); } } .candle { position: absolute; font-size: min(3vw, 3vh); color: rgba(255, 255, 224, 0.7); text-shadow: 0 0 5px rgba(255, 255, 224, 0.7); animation: flicker 2s ease-in-out infinite alternate; z-index: 2; cursor: pointer; transition: all 0.3s ease; } .candle:hover { transform: scale(1.2); color: rgba(255, 255, 224, 1); text-shadow: 0 0 10px rgba(255, 255, 224, 1); } @keyframes flicker { 0% { opacity: 0.5; transform: scale(0.95); } 50% { opacity: 1; transform: scale(1.05); } 100% { opacity: 0.7; transform: scale(1); } } @keyframes sparkle { 0%, 100% { opacity: 0; } 50% { opacity: 1; } } .sparkle { position: absolute; width: 5px; height: 5px; background-color: white; border-radius: 50%; animation: sparkle 0.8s linear infinite; } </style> </head> <body> <div class="scene"> <canvas id="snowCanvas"></canvas> <div class="mainTree">🎄</div> </div> <script> const scene = document.querySelector('.scene'); const canvas = document.getElementById('snowCanvas'); const ctx = canvas.getContext('2d'); function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); class Snowflake { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.size = Math.random() * 5 + 2; this.speed = Math.random() * 1.5 + 0.5; this.opacity = Math.random() * 0.5 + 0.3; this.wind = Math.random() * 0.5 - 0.25; } update() { this.y += this.speed; this.x += this.wind; if (this.y > canvas.height) { this.y = 0; this.x = Math.random() * canvas.width; } if (this.x > canvas.width) this.x = 0; if (this.x < 0) this.x = canvas.width; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`; ctx.fill(); } } const snowflakes = Array.from({ length: 150 }, () => new Snowflake()); function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); snowflakes.forEach(snowflake => { snowflake.update(); snowflake.draw(); }); requestAnimationFrame(animate); } animate(); function createCandles() { const numCandles = 15; const minDistance = 100; const candles = []; for (let i = 0; i < numCandles; i++) { let x, y, overlapping; do { x = Math.random() * (window.innerWidth - 100) + 50; y = Math.random() * (window.innerHeight - 100) + 50; overlapping = candles.some(candle => Math.hypot(candle.x - x, candle.y - y) < minDistance ); } while (overlapping); const candle = document.createElement('div'); candle.className = 'candle'; candle.textContent = '🕯️'; candle.style.left = `${x}px`; candle.style.top = `${y}px`; const scale = Math.random() * 0.3 + 0.3; const rotation = Math.random() * 20 - 10; candle.style.transform = `scale(${scale}) rotate(${rotation}deg)`; scene.appendChild(candle); candles.push({ x, y }); } } function handleClick(event) { const rect = event.target.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; for (let i = 0; i < 10; i++) { const sparkle = document.createElement('div'); sparkle.className = 'sparkle'; sparkle.style.left = `${x + Math.random() * 40 - 20}px`; sparkle.style.top = `${y + Math.random() * 40 - 20}px`; sparkle.style.width = `${Math.random() * 3 + 2}px`; sparkle.style.height = sparkle.style.width; event.target.appendChild(sparkle); setTimeout(() => sparkle.remove(), 800); } } function adjustLayout() { const mainTree = document.querySelector('.mainTree'); const candles = document.querySelectorAll('.candle'); mainTree.style.fontSize = `min(60vw, 60vh)`; // 增大圣诞树尺寸 candles.forEach(candle => { candle.style.fontSize = `min(3vw, 3vh)`; const x = Math.random() * (window.innerWidth - 100) + 50; const y = Math.random() * (window.innerHeight - 100) + 50; candle.style.left = `${x}px`; candle.style.top = `${y}px`; }); } createCandles(); adjustLayout(); document.querySelector('.mainTree').addEventListener('click', handleClick); document.querySelectorAll('.candle').forEach(candle => { candle.addEventListener('click', handleClick); }); window.addEventListener('resize', adjustLayout); </script> </body> </html> 视频版本 哔哩哔哩 YouTube

2024年12月23日 · 5 分钟 · IT小章

Python爬虫实战: 58同城房产信息自动化采集

原文地址:https://itxiaozhang.com/python-web-crawler-58city-real-estate-data-collection/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 1. 简介 这是一个基于Python开发的58同城房产信息采集系统,主要用于自动采集商铺、写字楼、厂房和生意转让等房产信息。系统提供图形界面操作,支持多城市数据采集。 2. 主要功能 多城市房产信息采集 多线程并发处理 自动代理IP切换 数据实时入库 自动去重过滤 定时采集更新 3. 技术特点 多线程采集 使用Python threading实现并发采集,提高效率。 代理IP池 自动维护代理IP池,避免被反爬: 1 2 3 4 5 6 def roxies_ip(url): ip = requests.get(url).text proxies_ip_lists = [] for i in ip: proxies_ip_lists.append({'https': "//" + i}) return proxies_ip_lists[0] 数据存储 采用MySQL存储数据,支持实时入库和查重。 4. 使用方法 配置数据库信息 设置代理IP接口 选择目标城市和类型 设置采集间隔 启动采集任务 相关源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 """ ================================ 作者:IT小章 网站:itxiaozhang.com 时间:2024年12月01日 Copyright © 2024 IT小章 ================================ """ import threading import tkinter as tk from tkinter import ttk, messagebox import requests import time import json from bs4 import BeautifulSoup import logging from datetime import datetime import random import re from queue import Queue # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) class Config: """配置管理类""" # 示例城市配置 CITIES = { "北京": {"北京": "bj|1"}, "上海": {"上海": "sh|2"}, "广州": {"广州": "gz|3"}, "深圳": {"深圳": "sz|4"} } # 房产类型配置 HOUSE_TYPES = { "商铺": "/shangpucz/0/", "写字楼": "/zhaozu/0/", "厂房": "/changfang/0/", "生意转让": "/shengyizr/0/" } @staticmethod def load_config(file_path="config.json"): """加载配置文件""" try: with open(file_path, 'r', encoding='utf-8') as f: return json.load(f) except FileNotFoundError: logging.warning(f"配置文件 {file_path} 不存在,使用默认配置") return {} class ProxyPool: """代理IP池管理类""" def __init__(self): self.proxies = Queue() self.lock = threading.Lock() def add_proxy(self, proxy): """添加代理""" self.proxies.put(proxy) def get_proxy(self): """获取代理""" try: return self.proxies.get() except: return None def remove_proxy(self, proxy): """移除失效代理""" with self.lock: if proxy in self.proxies.queue: self.proxies.queue.remove(proxy) class DataStorage: """数据存储类""" def __init__(self): self.data_queue = Queue() def save(self, data): """保存数据 实际使用时请实现具体的存储逻辑 """ logging.info(f"保存数据: {data}") self.data_queue.put(data) class HouseCrawler: """房产信息爬虫类""" def __init__(self): self.proxy_pool = ProxyPool() self.storage = DataStorage() self.headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" } def get_page(self, url, retry_times=3): """获取页面内容""" for _ in range(retry_times): try: proxy = self.proxy_pool.get_proxy() response = requests.get(url, headers=self.headers, proxies=proxy, timeout=10) response.encoding = 'utf-8' if response.status_code == 200: return response.text except Exception as e: logging.error(f"获取页面失败: {e}") if proxy: self.proxy_pool.remove_proxy(proxy) return None def parse_list_page(self, html): """解析列表页""" if not html: return [] try: soup = BeautifulSoup(html, 'lxml') items = soup.select('ul .item') results = [] for item in items: try: title = item.select_one('.title').text.strip() link = item.select_one('.link')['href'] results.append({ 'title': title, 'link': link }) except Exception as e: logging.error(f"解析列表项失败: {e}") return results except Exception as e: logging.error(f"解析列表页失败: {e}") return [] def parse_detail_page(self, html): """解析详情页""" if not html: return None try: # 示例解析逻辑 data = { 'title': '', 'price': '', 'area': '', 'location': '', 'contact': '', 'description': '' } soup = BeautifulSoup(html, 'lxml') # 实际使用时需要根据具体网页结构实现解析逻辑 return data except Exception as e: logging.error(f"解析详情页失败: {e}") return None class GUI: """图形界面类""" def __init__(self): self.root = tk.Tk() self.root.title("58同城房产信息采集系统 (学习版)") self.root.geometry('500x400') self.crawler = HouseCrawler() self.setup_gui() def setup_gui(self): """设置GUI界面""" # 城市选择 tk.Label(self.root, text="选择城市:").grid(row=0, column=0, padx=5, pady=5) self.city_var = tk.StringVar() self.city_combo = ttk.Combobox(self.root, textvariable=self.city_var, state="readonly") self.city_combo['values'] = list(Config.CITIES.keys()) self.city_combo.current(0) self.city_combo.grid(row=0, column=1, padx=5, pady=5) # 房产类型选择 tk.Label(self.root, text="房产类型:").grid(row=1, column=0, padx=5, pady=5) self.type_var = tk.StringVar() self.type_combo = ttk.Combobox(self.root, textvariable=self.type_var, state="readonly") self.type_combo['values'] = list(Config.HOUSE_TYPES.keys()) self.type_combo.current(0) self.type_combo.grid(row=1, column=1, padx=5, pady=5) # 页数设置 tk.Label(self.root, text="采集页数:").grid(row=2, column=0, padx=5, pady=5) self.pages_var = tk.StringVar(value="1") self.pages_entry = tk.Entry(self.root, textvariable=self.pages_var) self.pages_entry.grid(row=2, column=1, padx=5, pady=5) # 间隔时间设置 tk.Label(self.root, text="间隔时间(秒):").grid(row=3, column=0, padx=5, pady=5) self.interval_var = tk.StringVar(value="5") self.interval_entry = tk.Entry(self.root, textvariable=self.interval_var) self.interval_entry.grid(row=3, column=1, padx=5, pady=5) # 状态显示 self.status_text = tk.Text(self.root, height=10, width=50) self.status_text.grid(row=4, column=0, columnspan=2, padx=5, pady=5) # 控制按钮 self.start_button = tk.Button(self.root, text="开始采集", command=self.start_crawl) self.start_button.grid(row=5, column=0, padx=5, pady=5) self.stop_button = tk.Button(self.root, text="停止采集", command=self.stop_crawl) self.stop_button.grid(row=5, column=1, padx=5, pady=5) # 采集状态 self.is_running = False def log_message(self, message): """显示日志信息""" self.status_text.insert(tk.END, f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}\n") self.status_text.see(tk.END) def start_crawl(self): """开始采集""" if self.is_running: messagebox.showwarning("警告", "采集任务正在进行中") return try: pages = int(self.pages_var.get()) interval = int(self.interval_var.get()) if pages < 1 or interval < 1: raise ValueError except ValueError: messagebox.showerror("错误", "请输入有效的页数和间隔时间") return self.is_running = True threading.Thread(target=self.crawl_task, args=(pages, interval)).start() self.log_message("开始采集任务...") def stop_crawl(self): """停止采集""" self.is_running = False self.log_message("正在停止采集任务...") def crawl_task(self, pages, interval): """采集任务""" city = self.city_var.get() house_type = self.type_var.get() try: for page in range(1, pages + 1): if not self.is_running: break self.log_message(f"正在采集第 {page} 页...") url = self.generate_url(city, house_type, page) # 获取列表页 html = self.crawler.get_page(url) if not html: self.log_message(f"获取第 {page} 页失败,跳过...") continue # 解析列表页 items = self.crawler.parse_list_page(html) self.log_message(f"第 {page} 页发现 {len(items)} 条房源信息") # 处理每个房源 for item in items: if not self.is_running: break # 获取详情页 detail_html = self.crawler.get_page(item['link']) if detail_html: detail_data = self.crawler.parse_detail_page(detail_html) if detail_data: self.crawler.storage.save(detail_data) self.log_message(f"成功采集: {item['title']}") # 间隔等待 time.sleep(interval) except Exception as e: self.log_message(f"采集过程出错: {str(e)}") finally: self.is_running = False self.log_message("采集任务已完成") def generate_url(self, city, house_type, page): """生成目标URL""" city_code = Config.CITIES[city][city].split('|')[0] type_path = Config.HOUSE_TYPES[house_type] return f"https://{city_code}.58.com{type_path}pn{page}/" def run(self): """运行GUI""" self.root.mainloop() def main(): """主程序入口""" try: # 启动GUI界面 app = GUI() app.run() except Exception as e: logging.error(f"程序运行出错: {str(e)}") messagebox.showerror("错误", f"程序运行出错: {str(e)}") if __name__ == "__main__": """ 使用说明: 1. 安装依赖包: pip install requests beautifulsoup4 lxml 2. 运行程序: python crawler.py 3. 使用步骤: - 选择目标城市 - 选择房产类型 - 设置采集页数 - 设置采集间隔时间 - 点击"开始采集" 4. 注意事项: - 采集间隔建议设置在5秒以上 - 采集页数建议从小到大测试 - 如遇到错误,请查看日志信息 - 使用代理IP可以提高采集成功率 5. 数据存储: - 默认将数据打印到日志 - 可以修改DataStorage类实现其他存储方式 6. 代理设置: - 默认使用直连方式 - 可以修改ProxyPool类实现代理池功能 """ main()

2024年12月1日 · 6 分钟 · IT小章

Python自动化工具:HMDB代谢物信息一键批量获取

原文地址:https://itxiaozhang.com/python-hmdb-metabolites-batch-extractor/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 简介 这是一个用于从HMDB(人类代谢组数据库)批量提取代谢物信息的小工具。它可以帮助研究人员快速获取多个代谢物的详细信息,节省手动查询的时间。 功能特点 支持正负离子模式(Positive/Negative) 批量处理多个质量数 自动提取代谢物的关键信息 结果保存为Excel可直接打开的CSV文件 支持断点续传,出错自动重试 使用方法 准备输入文件: positive.txt:存放正离子模式的质量数 negative.txt:存放负离子模式的质量数 每行一个质量数 运行程序: 双击运行程序 等待程序自动处理 完成后按回车键退出 查看结果: 程序会生成代谢物数据.csv文件 使用Excel打开即可查看所有提取的信息 输出结果包含 HMDB ID 离子模式 描述信息 分类信息(超类/类/子类) 来源信息 组织位置 相关数据库ID(KEGG/ChEBI/METLIN) 结构图链接 可增加其他字段 注意事项 请确保输入的质量数格式正确 程序运行需要网络连接 处理时间取决于数据量大小 部分代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 """ ================================ 作者:IT小章 网站:itxiaozhang.com 时间:2024年11月27日 Copyright © 2024 IT小章 ================================ """ import requests import re import csv import time import logging import os from concurrent.futures import ThreadPoolExecutor, as_completed from tqdm import tqdm from lxml import html # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("代谢物提取.log", encoding='utf-8'), logging.StreamHandler() ] ) class MetaboliteExtractor: """代谢物数据提取器""" def __init__(self): self.base_url = "https://hmdb.ca" self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } def process_files(self): """处理输入文件""" try: logging.info("程序开始运行...") results = [] # 处理positive模式 if os.path.exists('positive.txt'): logging.info("处理positive.txt...") results.extend(self._process_file('positive.txt', 'positive')) # 处理negative模式 if os.path.exists('negative.txt'): logging.info("处理negative.txt...") results.extend(self._process_file('negative.txt', 'negative')) if results: self._save_results(results) logging.info(f"处理完成,共获取 {len(results)} 条结果") else: logging.error("未找到任何结果") except Exception as e: logging.error(f"处理过程出错: {str(e)}") finally: logging.info("程序运行结束") print("\n" + "="*50) input("按回车键退出程序...") def _process_file(self, filename, mode): """处理单个文件(具体实现已隐藏)""" try: with open(filename, 'r', encoding='utf-8') as f: data = f.read().strip() if not data: logging.warning(f"{filename} 为空") return [] logging.info(f"正在处理 {filename}") return self._extract_data(data, mode) except FileNotFoundError: logging.error(f"未找到文件: {filename}") return [] def _extract_data(self, data, mode): """提取数据(具体实现已隐藏)""" # 核心实现已隐藏 pass def _save_results(self, results): """保存结果到CSV""" try: filename = '代谢物数据.csv' with open(filename, 'w', newline='', encoding='utf-8-sig') as f: if results: writer = csv.DictWriter(f, fieldnames=results[0].keys()) writer.writeheader() writer.writerows(results) logging.info(f"数据已保存到 {filename}") except Exception as e: logging.error(f"保存数据失败: {str(e)}") def main(): extractor = MetaboliteExtractor() extractor.process_files() if __name__ == "__main__": main() 视频版本 哔哩哔哩 YouTube

2024年11月27日 · 2 分钟 · IT小章

用python写一个网页源码下载器

原文地址:https://itxiaozhang.com/python-webpage-source-downloader/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 介绍 这个Python脚本能帮助您轻松保存完整网页,包括图片、样式和脚本。 主要功能 下载指定网页的HTML内容 获取并内嵌所有资源(图片、CSS、JavaScript等) 将完整网页保存为单个HTML文件 支持同时处理多个网页 源代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 # 作者:IT小章 # 时间:2024年10月16日 # 网站:itxiaozhang.com import os import cloudscraper from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse import base64 from concurrent.futures import ThreadPoolExecutor, as_completed def download_resource(url, base_url, scraper): try: response = scraper.get(url, timeout=10) if response.status_code == 200: content_type = response.headers.get('content-type', '').split(';')[0] return f"data:{content_type};base64,{base64.b64encode(response.content).decode('utf-8')}" except: pass return urljoin(base_url, url) def save_webpage(url, output_file): # 创建一个cloudscraper实例 scraper = cloudscraper.create_scraper(browser='chrome') # 获取网页内容 response = scraper.get(url) soup = BeautifulSoup(response.text, 'html.parser') # 处理外部资源 for tag in soup.find_all(['img', 'script', 'link']): if tag.name == 'img' and tag.has_attr('src'): tag['src'] = download_resource(tag['src'], url, scraper) elif tag.name == 'script' and tag.has_attr('src'): tag['src'] = download_resource(tag['src'], url, scraper) elif tag.name == 'link' and tag.has_attr('href'): tag['href'] = download_resource(tag['href'], url, scraper) # 处理内联样式中的URL for tag in soup.find_all(style=True): style = tag['style'] urls = [u.strip() for u in style.split('url(') if ')' in u] for u in urls: old_url = u.split(')')[0].strip("'").strip('"') new_url = download_resource(old_url, url, scraper) style = style.replace(f"url({old_url})", f"url({new_url})") tag['style'] = style # 保存处理后的HTML with open(output_file, 'w', encoding='utf-8') as f: f.write(str(soup)) print(f"网页已保存至 {output_file}") def save_multiple_webpages(urls_and_outputs): with ThreadPoolExecutor(max_workers=5) as executor: future_to_url = {executor.submit(save_webpage, url, output): url for url, output in urls_and_outputs} for future in as_completed(future_to_url): url = future_to_url[future] try: future.result() except Exception as exc: print(f'{url} 生成过程中产生了一个异常: {exc}') urls_and_outputs = [ ("https://lasempresas.com.mx/", "saved_webpage_1.html"), ("https://indialei.in/", "saved_webpage_2.html"), ("https://www.zaubacorp.com/", "saved_webpage_3.html"), ] save_multiple_webpages(urls_and_outputs)

2024年10月17日 · 2 分钟 · IT小章

使用 Python 从 HMDB 网站提取数据

原文地址:https://itxiaozhang.com/python-hmdb-data-extraction/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 本文配合视频食用效果最佳,视频教程在文章末尾。 这个程序是做什么的? 这个程序可以从人类代谢组数据库(HMDB)网站https://hmdb.ca/上获取代谢物的信息。你只需要提供一些HMDB ID,程序就会自动去网站上查找并下载相关数据。 主要功能 批量处理:可以一次处理很多HMDB ID。 数据检查:会检查你输入的ID是否正确,有没有重复的。 信息收集:从网站上获取代谢物的各种信息,比如它的分类、在人体内的分布等。 自动重试:如果因为网络问题失败了,会自动再试几次。 进度显示:会告诉你现在处理到哪里了,完成了多少。 定期保存:每处理一些数据就会保存一次,防止意外丢失。 程序特点 容易使用:设计得比较简单,普通人也能操作。 速度快:可以同时处理多个请求,所以比较快。 不容易出错:有很多防错设计,运行起来比较稳定。 中文界面:所有提示都是中文的,看起来更容易懂。 源代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 """ ================================ 作者:IT小章 网站:itxiaozhang.com 时间:2024年10月11日 Copyright © 2024 IT小章 ================================ """ import csv import time import requests from concurrent.futures import ThreadPoolExecutor, as_completed from lxml import html import os from collections import Counter import sys HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } NEW_COLUMNS = [ "exists in all living species, ranging from bacteria to humans", "only found in individuals that have used or taken this drug", "not a naturally occurring metabolite and is only found in those individuals exposed to this compound or its derivatives" ] def check_ids(ids): invalid_ids = [] id_counts = Counter(ids) duplicates = {id: count for id, count in id_counts.items() if count > 1} for index, id in enumerate(ids, 1): if not id.startswith("HMDB"): invalid_ids.append((index, id)) return invalid_ids, duplicates def get_metabolite_data(hmdb_id): # 省略 pass def get_metabolite_data_with_retry(hmdb_id, max_retries=3): for attempt in range(max_retries): try: return get_metabolite_data(hmdb_id) except Exception: if attempt == max_retries - 1: print(f"[{hmdb_id}] 所有重试尝试均失败。") time.sleep(2 ** attempt) return None def write_to_csv(results, filename, mode='a'): fieldnames = ['HMDB ID'] + NEW_COLUMNS + [ 'Super Class', 'Class', 'Sub Class', 'Disposition_source(Endogenous)', 'Endogenous(plant or animal or more)', 'Biological Properties_Biospecimen Locations', 'Biological Properties_Tissue Locations', 'KEGG Compound ID', 'ChEBI ID', 'METLIN ID' ] file_exists = os.path.isfile(filename) with open(filename, mode, newline='', encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) if not file_exists or mode == 'w': writer.writeheader() writer.writerows(results) def process_ids(hmdb_ids, max_workers=5): # 省略 pass def main(): try: if not os.path.exists('id.txt'): print("错误:找不到 id.txt 文件。") return with open('id.txt', 'r') as f: hmdb_ids = f.read().splitlines() invalid_ids, duplicates = check_ids(hmdb_ids) valid_ids = [id for id in hmdb_ids if id.startswith("HMDB")] unique_ids = list(dict.fromkeys(valid_ids)) write_to_csv([], '代谢物数据_最终.csv', mode='w') success_count, failure_count = process_ids(unique_ids) print(f"数据提取完成。总计: {len(unique_ids)}, 成功: {success_count}, 失败: {failure_count}") except Exception as e: print(f"发生错误: {e}") if __name__ == "__main__": main() 视频教程 哔哩哔哩 YouTube

2024年10月11日 · 2 分钟 · IT小章

利用JavaScript书签提取构造URL下载PDF文件

原文地址:https://itxiaozhang.com/create-bookmark-url-download-pdf/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 1. 写脚本 1 2 3 4 5 6 7 javascript:(function() { const id = new URLSearchParams(window.location.search).get('id'); if (id) { const pdfUrl = `https://example.com/path/to/pdf/${id}.pdf`; window.open(pdfUrl, '_blank'); } })(); 2. 创建书签 打开书签管理器: Chrome: 按 Ctrl + Shift + B 或通过菜单进入“书签” -> “书签管理器”。 Firefox: 按 Ctrl + Shift + B 或通过菜单进入“书签” -> “管理所有书签”。 Edge: 按 Ctrl + Shift + B 或通过菜单进入“收藏夹” -> “管理收藏夹”。 添加新书签: 名称: “自动下载PDF” URL: 粘贴上述JavaScript代码。 保存书签。 3. 使用书签 访问包含ID参数的页面(例如 https://example.com/page?id=123)。 点击书签“自动下载PDF”,新窗口将打开构造的PDF文件链接。 说明 提取ID:使用 URLSearchParams 从当前页面的URL中提取ID参数。 构造URL:根据提取的ID生成PDF文件的下载链接。 打开PDF:使用 window.open() 在新窗口中打开构造的PDF文件链接。

2024年8月11日 · 1 分钟 · IT小章

gwang.top:一键官网查询

原文地址:https://itxiaozhang.com/one-click-official-site-query/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 本文配合视频食用效果最佳,视频教程在文章末尾。 简介 gwang.top 是一个小章做的在线查询工具,可以快速找到各种官网,例如软件、学校、组织等等。 使用方法 访问网站:打开浏览器,地址栏输入gwang.top。 输入关键词:在搜索框中输入软件名称、机构名或公司名。 获取结果:点击查询按钮,gwang.top将展示目标官方网站的链接。 直接访问:点击链接,直达官方网站。 视频教程 哔哩哔哩 西瓜视频

2024年7月17日 · 1 分钟 · IT小章

文件批量重命名

原文地址:https://itxiaozhang.com/batch-file-renaming/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 本文配合视频食用效果最佳,视频教程在文章末尾。 客户需求 目前有很多文件,文件名的格式类似于"附件n,其他内容",其中n是一个数字。我希望将所有以"附件n"开头的文件名中的数字n增加m,文件名中的其他部分应保持不变。假设m=8,举例如下: 原来的名称 修改后的名称 附件1-其他内容.txt 附件9-其他内容.txt 附件2-其他内容2.pdf 附件10-其他内容2.pdf 附件11-其他名称.txt 附件19-其他名称.txt 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import os import re import logging from pathlib import Path import argparse def setup_logging(log_file): """ 设置日志记录的配置,同时输出到文件和控制台。 """ logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(log_file), logging.StreamHandler() ] ) def rename_files(directory, number_to_add): """ 重命名指定目录及其子目录中的文件。 """ pattern = re.compile(r'^附件(\d+)(.*)$') # 匹配以“附件数字”开头的文件名 total_renamed_count = 0 total_error_count = 0 for root, _, files in os.walk(directory): renamed_count = 0 error_count = 0 for filename in files: match = pattern.match(filename) if match: try: number = int(match.group(1)) # 提取文件名中的数字部分 rest = match.group(2) # 提取文件名中的其余部分 new_number = number + number_to_add new_filename = f'附件{new_number}{rest}' # 构造新的文件名 old_file = Path(root) / filename new_file = Path(root) / new_filename old_file.rename(new_file) # 重命名文件 renamed_count += 1 total_renamed_count += 1 except Exception as e: logging.error(f'Error renaming file {filename}: {e}') error_count += 1 total_error_count += 1 logging.info(f'在目录 "{root}" 中修改了 {renamed_count} 个文件。') logging.info(f'Renaming complete. {total_renamed_count} files renamed, {total_error_count} errors occurred.') def main(): """ 主程序入口,解析命令行参数并调用重命名函数。 """ parser = argparse.ArgumentParser(description='重命名目录及其子目录中的文件。') parser.add_argument('--dir', type=str, default=os.getcwd(), help='要处理的目录 (默认: 当前目录)') parser.add_argument('--log', type=str, default='rename_log.txt', help='日志文件名 (默认: rename_log.txt)') args = parser.parse_args() setup_logging(args.log) try: number_to_add = 8 # 用户输入的增量m,这里设置为固定值8 except ValueError: print("请输入一个有效的整数。") return rename_files(args.dir, number_to_add) input("操作完成。按 Enter 键退出。") if __name__ == '__main__': main() 目录结构 1 2 3 4 5 | rename_files.py.py \---文件夹1 附件11,其他名称.txt 附件149,其他名称.pdf 附件149,其他名称1.txt

2024年7月3日 · 2 分钟 · IT小章

Python小游戏开发:贪吃蛇的实现

原文地址:https://itxiaozhang.com/python-mini-game-development-snake-implementation/ 如果您需要远程电脑维修或者编程开发,请加我微信咨询。 介绍 这是一个经典的贪吃蛇游戏,通过 Pygame 库实现。玩家控制蛇在屏幕上移动,吃掉食物以增长蛇的长度,同时避免撞到墙壁或蛇身。游戏包含主菜单、得分显示和重玩功能。 玩法 启动游戏:运行 snake_game.py 文件,打开游戏界面。 主菜单: 使用上下方向键选择菜单项。 按回车键确认选择。 菜单选项包括开始游戏、继续游戏、暂停游戏和结束游戏。 控制蛇: 使用方向键(上下左右)控制蛇的移动方向。 吃掉红色方块(食物)以增加长度和得分。 游戏重启: 当蛇撞到墙壁或自身时,游戏结束。 按空格键重新开始游戏。 游戏目标 尽可能吃掉更多食物,获得更高的得分。 避免撞到墙壁或自身,使蛇尽量长时间存活。 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 import pygame import random WIDTH = 600 HEIGHT = 600 class Snake: def __init__(self, screen): self.screen = screen self.body = [] self.fx = pygame.K_RIGHT self.init_body() def init_body(self, length=5): left, top = (100, 100) for i in range(length): if self.body: left, top = self.body[0].left, self.body[0].top node = pygame.Rect(left + 20, top, 20, 20) else: node = pygame.Rect(left, top, 20, 20) self.body.insert(0, node) def draw_snake(self): for n in self.body: pygame.draw.rect(self.screen, (62, 122, 178), n, 0) def add_node(self): if self.body: left, top = self.body[0].left, self.body[0].top if self.fx == pygame.K_RIGHT: left += 20 elif self.fx == pygame.K_LEFT: left -= 20 elif self.fx == pygame.K_UP: top -= 20 else: top += 20 node = pygame.Rect(left, top, 20, 20) self.body.insert(0, node) def del_node(self): self.body.pop() def move(self): self.del_node() self.add_node() def change(self, fx): LR = [pygame.K_LEFT, pygame.K_RIGHT] UD = [pygame.K_UP, pygame.K_DOWN] if fx in LR + UD: if fx in LR and self.fx in LR: return if fx in UD and self.fx in UD: return self.fx = fx def is_dead(self): if self.body[0].left not in range(WIDTH): return True if self.body[0].top not in range(HEIGHT): return True if self.body[0] in self.body[1:]: return True class Food: def __init__(self): self.node = pygame.Rect(60, 80, 20, 20) self.flag = False def set(self): all_x_point = range(20, WIDTH - 20, 20) all_y_point = range(20, HEIGHT - 20, 20) left = random.choice(all_x_point) top = random.choice(all_y_point) self.node = pygame.Rect(left, top, 20, 20) self.flag = False def reset(self): self.flag = True def show_text(screen, pos, text, color, font_size=20): cur_font = pygame.font.SysFont('SimHei', font_size) text_fmt = cur_font.render(text, 1, color) screen.blit(text_fmt, pos) def show_score(screen, score, high_score): score_text = f'得分: {score} 最高分: {high_score}' show_text(screen, (WIDTH - 280, 30), score_text, (0, 0, 0)) def main_menu(screen): menu_font = pygame.font.SysFont('SimHei', 40) menu_options = ['开始游戏', '继续游戏', '暂停游戏', '结束游戏'] selected_option = 0 while True: screen.fill((255, 255, 255)) # 绘制标题 title_text = menu_font.render('贪吃蛇', True, (0, 0, 0)) title_rect = title_text.get_rect(center=(WIDTH // 2, 100)) screen.blit(title_text, title_rect) # 绘制菜单选项 for i, option in enumerate(menu_options): if i == selected_option: color = (0, 255, 0) # 选中的菜单项为绿色 else: color = (0, 0, 0) # 其他菜单项为黑色 option_text = menu_font.render(option, True, color) option_rect = option_text.get_rect(center=(WIDTH // 2, 200 + i * 60)) screen.blit(option_text, option_rect) # 处理用户输入 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() return elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: selected_option = (selected_option - 1) % len(menu_options) elif event.key == pygame.K_DOWN: selected_option = (selected_option + 1) % len(menu_options) elif event.key == pygame.K_RETURN: return selected_option pygame.display.flip() def main(): pygame.init() screen = pygame.display.set_mode([WIDTH, HEIGHT]) pygame.display.set_caption("贪吃蛇") # 显示主菜单界面 selected_option = main_menu(screen) # 根据选择的菜单项执行相应的操作 if selected_option == 0: # 开始游戏 score = 0 high_score = 0 sk = Snake(screen) fd = Food() dead = False # 读取历史最高分 try: with open('high_score.txt', 'r') as file: high_score = int(file.read()) except FileNotFoundError: pass clock = pygame.time.Clock() while True: for e in pygame.event.get(): if e.type == pygame.QUIT: pygame.quit() return if e.type == pygame.KEYDOWN: if not dead: sk.change(e.key) if e.key == pygame.K_SPACE: score = 0 sk = Snake(screen) fd = Food() dead = False screen.fill((255, 255, 255)) sk.draw_snake() if not dead: sk.move() if sk.is_dead(): show_text(screen, (140, 160), '我一定会回来的', (202, 92, 85), 30) show_text(screen, (180, 320), '按 空格 重新开始', (116, 181, 103), 20) dead = True # 更新最高分 if score > high_score: high_score = score with open('high_score.txt', 'w') as file: file.write(str(high_score)) if fd.flag: fd.set() pygame.draw.rect(screen, (255, 0, 0), fd.node, 0) if sk.body[0].colliderect(fd.node): score += 1 sk.add_node() fd.reset() show_score(screen, score, high_score) pygame.display.update() clock.tick(10) elif selected_option == 1: # 继续游戏 # 继续之前的游戏 pass elif selected_option == 2: # 暂停游戏 # 实现暂停游戏的功能 pass elif selected_option == 3: # 结束游戏 pygame.quit() return if __name__ == '__main__': main()

2024年3月27日 · 4 分钟 · IT小章