资讯动态

Python音乐数据采集与多通道分发实战:爬取+Excel+MySQL+邮件

发布时间:2026/10/14 1:01:48 来源:尧图企业网站定制
1. 项目概述一次完整的音乐数据采集与多通道分发实践你有没有遇到过这样的场景想批量整理某平台上的歌曲信息——比如歌名、歌手、专辑、发行时间、时长、播放量甚至歌词片段——但手动复制粘贴几十上百首不仅耗时还容易出错更麻烦的是整理完的数据怎么用存Excel里方便查看但没法做复杂筛选放数据库里能查能关联可临时看个汇总又得写SQL要是能自动发到邮箱每天早上喝咖啡时扫一眼最新入库的热门曲目那才叫真正省心。这个标题说的就是一套闭环式音乐数据工程爬取 → 结构化存储 → 多端分发。它不是玩具脚本而是我在给某高校数字人文实验室做音乐传播分析Demo时落地的真实流程全程基于Python生态不依赖任何黑盒工具所有环节都可控、可调、可审计。核心关键词就三个歌曲信息爬取、Excel导出、MySQL写入、邮件自动推送。适合两类人一是刚学完requestsBeautifulSoup/pandas的新手想找个有始有终的练手项目二是需要快速搭建轻量级数据管道的运营或产品同学不需要上大数据平台但要求结果稳、路径清、改起来快。整个流程跑通后你拿到的不是一个“能跑就行”的脚本而是一套可复用的数据流转骨架——换爬豆瓣电影、小红书笔记、招聘网站职位只要把解析逻辑换掉其他模块几乎不用动。2. 整体架构设计与技术选型逻辑2.1 为什么是“爬取ExcelMySQL邮件”四件套先说结论这不是为了堆砌技术名词而是由实际需求倒推出来的最小可行组合。我最初接到的需求很朴素“每周一上午9点把上周新增的TOP100热歌信息发到项目组邮箱同时保证后台能随时按歌手、年份、流派查”。这意味着必须同时满足三个刚性条件人类可读性Excel、机器可查询性MySQL、时效触达性邮件。有人会问为什么不用SQLite轻量啊。实测下来不行——当团队里非技术人员要查“周杰伦2023年发行的慢歌有哪些”让他们装DB Browser、写WHERE语句沟通成本远高于教他们打开Excel筛选。也有人建议直接用CSV替代Excel文件小、生成快。但现实是运营同事常在Excel里加批注、标颜色、做简单图表CSV不支持这些。至于邮件不是为了炫技而是解决“数据存在哪、谁来查、查完干啥”的协作断点。我们试过只存数据库结果两周后没人记得去查也试过只发Excel但版本混乱A发的和B发的字段顺序不一致。四件套的本质是把数据生命周期的四个关键节点——采集、呈现、分析、通知——用最直白的方式锚定下来。2.2 技术栈选择拒绝“最新最潮”只选“最稳最熟”爬虫层requests lxml组合放弃Scrapy。理由很实在Scrapy学习曲线陡配置项多而本项目目标站点结构清晰假设为某公开音乐榜单页单页请求XPath解析足够。lxml比BeautifulSoup快3倍以上解析100首歌的DOM树lxml耗时约0.8秒BS4要2.3秒——别小看这1.5秒当你要每小时跑一次积少成多就是稳定性隐患。数据处理层pandas是唯一选择。它天然解决“同一份数据既要塞Excel又要插MySQL”的格式转换难题。DataFrame就像一个活的数据中转站从爬虫拿到原始字典列表pd.DataFrame()一转立刻能.to_excel()也能.to_sql()。更重要的是它内置空值处理、类型自动推断比如把2023-05-20字符串自动识别为datetime省去大量手工清洗代码。存储层MySQL 8.0明确拒绝SQLite和MongoDB。SQLite虽免部署但并发写入时锁表严重当多个脚本同时尝试写入比如测试时手抖点了两次大概率报database is lockedMongoDB文档灵活但音乐信息是强结构化数据歌名必填、时长必为数字用NoSQL反而要额外写校验逻辑。MySQL的事务支持、索引能力、以及INSERT ... ON DUPLICATE KEY UPDATE语法让“去重插入”一行SQL搞定比在Python里先查再判再插代码量少一半出错率低八成。邮件层smtplibemail标准库不碰任何第三方SDK。很多教程推荐yagmail或sendgrid但前者封装太深出问题时连SMTP错误码都看不到后者要配API Key增加运维负担。原生库虽然写法略啰嗦但每个参数含义清晰比如server.starttls()必须在server.login()之前调试时抓包一看就懂。提示所有技术选型都遵循一个铁律——当两种方案都能实现功能时优先选标准库多、文档全、报错信息直白的那个。因为90%的线上问题不是功能没实现而是异常没捕获、日志不清晰、重启后状态丢失。2.3 架构图数据如何流动整个流程不是线性的“爬完再存再发”而是分阶段流水线[爬虫模块] ↓ 解析为字典列表 [数据清洗模块] ↓ 转为DataFrame补全缺失字段如无发行年份则填未知 [双写模块] ├─→ [Excel写入]按日期生成文件名写入本地./output/目录 └─→ [MySQL写入]开启事务批量INSERT失败则回滚并记录错误行 ↓ 成功后触发 [邮件模块] ↓ 封装Excel附件摘要正文通过SMTP发送关键设计点在于解耦爬虫不关心存哪Excel模块不关心数据库字段名邮件模块只认“本次成功写入X条含新歌Y首”。这样改一个模块不影响其他。比如后期要加微信推送只需新增一个微信模块接入现有“写入完成”事件即可不用动爬虫半行代码。3. 核心细节解析与实操要点3.1 爬虫模块如何稳定获取歌曲信息假设目标是某公开音乐榜单页URL示例https://example-music.com/chart/weekly页面结构为div classsong-item h3 classsong-title晴天/h3 p classartist周杰伦/p span classalbum叶惠美/span time datetime2003-07-312003-07-31/time span classduration4:23/span /div实操步骤与避坑点请求头伪装必须做全不只是User-Agent还要加Accept-Language、Referer、Accept-Encoding。我踩过的坑某站点用navigator.platform检测客户端如果只设UA返回的HTML里关键字段被JS动态填充requests拿不到真实数据。解决方案是加完整headersheaders { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, Connection: keep-alive, Upgrade-Insecure-Requests: 1, }XPath定位要带容错直接写//div[classsong-item]/h3/text()风险极高——万一页面改版class名加了_v2整个脚本就崩。正确写法是用contains()模糊匹配并设置默认值title item.xpath(.//h3[contains(class,title)]/text()) song_title title[0].strip() if title else 未知歌名这样即使h3标签没了也不会抛IndexError而是填入兜底值。反爬应对策略频率控制time.sleep(1)是底线但更优解是用random.uniform(1, 3)模拟人工间隔避免固定节奏被识别。IP轮换本项目不强制但如果目标站有严格限制可在requests.Session()里挂代理池注意仅限合法公开代理如免费HTTP代理列表不涉及任何违规服务。Session复用用session requests.Session()代替反复创建requests.get()复用TCP连接提速30%以上。注意所有爬取行为必须遵守robots.txt协议且仅用于个人学习或非商业分析。对需登录、加密、或明确禁止爬取的站点请停止操作。3.2 数据清洗模块让脏数据变干净爬下来的原始数据常有三大类问题空值、格式乱、类型错。比如“时长”字段可能是4:23、04:23、4分23秒、甚至空字符串“发行时间”可能混着2003-07-31、2003年7月31日、July 31, 2003。pandas的apply()函数是救星但要用对方式。关键清洗逻辑时长标准化统一转为秒数整型便于后续排序、统计平均时长。def parse_duration(x): if not x or pd.isna(x): return 0 # 匹配4:23或04:23 m re.match(r^(\d{1,2}):(\d{2})$, str(x).strip()) if m: return int(m.group(1)) * 60 int(m.group(2)) # 匹配4分23秒 m2 re.match(r^(\d{1,2})分(\d{1,2})秒$, str(x).strip()) if m2: return int(m2.group(1)) * 60 int(m2.group(2)) return 0 # 兜底 df[duration_sec] df[duration].apply(parse_duration)日期归一化强制转为datetime64[ns]类型空值填NaTNot a Time比填字符串未知更利于后续按年份分组。df[release_date] pd.to_datetime(df[release_date], errorscoerce) # errorscoerce 表示解析失败则转为NaT歌手去重与合并一首歌可能有周杰伦 feat. 阿信但分析时想按主艺人归类。用正则提取第一个名字df[main_artist] df[artist].str.extract(r^([^\n]))[0].str.strip()清洗后数据结构示例song_titleartistalbumrelease_dateduration_secmain_artist晴天周杰伦叶惠美2003-07-31263周杰伦夜曲周杰伦 方文山七里香2004-08-03228周杰伦实操心得清洗逻辑一定要写单元测试用pytest写几个assert比如assert parse_duration(3:45) 225。我曾因一个正则少写了^锚点导致123分45秒被误解析为123*6045上线后统计平均时长变成2小时——这种错误只有测试能提前揪出来。3.3 Excel写入模块不只是保存更要“好用”pandas.to_excel()看似简单但细节决定体验文件命名规范用datetime.now().strftime(%Y%m%d_%H%M%S)生成时间戳避免覆盖。但纯时间戳难识别建议加业务前缀fmusic_chart_{now_str}.xlsx。工作表命名不要用默认的Sheet1改为Raw_Data并添加第二张表Summary用openpyxl写入统计摘要如总歌曲数、新歌数、平均时长with pd.ExcelWriter(filename, engineopenpyxl) as writer: df.to_excel(writer, sheet_nameRaw_Data, indexFalse) # 写入摘要 summary_df pd.DataFrame({ 指标: [总歌曲数, 新歌数, 平均时长(秒)], 数值: [len(df), len(df[df[is_new]True]), int(df[duration_sec].mean())] }) summary_df.to_excel(writer, sheet_nameSummary, indexFalse)列宽自适应Excel打开后列太窄看不清用openpyxl调整from openpyxl.styles import Font worksheet writer.sheets[Raw_Data] for column in worksheet.columns: max_length 0 column_letter column[0].column_letter for cell in column: try: if len(str(cell.value)) max_length: max_length len(str(cell.value)) except: pass adjusted_width min(max_length 2, 50) # 最大50字符宽 worksheet.column_dimensions[column_letter].width adjusted_width样式增强给标题行加粗、背景色提升可读性header_font Font(boldTrue, colorFFFFFF) header_fill PatternFill(start_color4472C4, end_color4472C4, fill_typesolid) for cell in worksheet[1]: cell.font header_font cell.fill header_fill注意openpyxl写入比xlsxwriter慢但xlsxwriter不支持读取已有文件。本项目需先写Raw_Data再写Summary必须用openpyxl。3.4 MySQL写入模块安全、高效、可追溯建表语句是根基必须包含主键、索引、约束CREATE TABLE music_songs ( id BIGINT PRIMARY KEY AUTO_INCREMENT, song_title VARCHAR(255) NOT NULL, artist VARCHAR(255) NOT NULL, album VARCHAR(255), release_date DATE, duration_sec INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_title_artist (song_title, artist) -- 防止同歌同歌手重复 );写入核心逻辑批量插入df.to_sql()默认逐行插入100条要100次网络往返。改用methodmulti一条SQL插入多行df.to_sql(music_songs, conengine, if_existsappend, indexFalse, methodmulti, chunksize1000)chunksize1000表示每1000行打包一次实测比默认快5倍。去重更新用ON DUPLICATE KEY UPDATE当uk_title_artist冲突时只更新updated_at不覆盖原始数据# 先确保DataFrame有updated_at列 df[updated_at] pd.Timestamp.now() # 执行INSERT ... ON DUPLICATE KEY UPDATE with engine.connect() as conn: stmt text( INSERT INTO music_songs (song_title, artist, album, release_date, duration_sec, updated_at) VALUES (:song_title, :artist, :album, :release_date, :duration_sec, :updated_at) ON DUPLICATE KEY UPDATE updated_at VALUES(updated_at) ) conn.execute(stmt, df.to_dict(records)) conn.commit()错误隔离单条数据错误如日期格式非法不应导致整批失败。用try...except包裹每条插入记录错误行到日志文件error_rows [] for idx, row in df.iterrows(): try: # 执行单条INSERT except Exception as e: error_rows.append((idx, row.to_dict(), str(e))) if error_rows: with open(./logs/insert_errors.log, a) as f: f.write(f[{datetime.now()}] {len(error_rows)} errors:\n) for err in error_rows: f.write(fRow {err[0]}: {err[1]} - {err[2]}\n)提示MySQL连接必须用连接池SQLAlchemy的create_engine(..., pool_size5, max_overflow10)否则高并发时频繁创建销毁连接CPU飙升。4. 实操过程与核心环节实现4.1 环境准备与依赖安装一步到位命令Linux/macOS# 创建虚拟环境强烈推荐避免包冲突 python3 -m venv music_env source music_env/bin/activate # 安装核心依赖 pip install pandas openpyxl lxml mysql-connector-python python-dotenv # 验证安装 python -c import pandas as pd; print(pd.__version__)Windows用户注意source命令换成music_env\Scripts\activate.bat。若mysql-connector-python编译失败改用pip install PyMySQL纯Python实现稍慢但兼容性更好。环境变量配置.env文件所有敏感配置数据库密码、邮箱密码绝不硬编码用.env文件管理# .env DB_HOSTlocalhost DB_PORT3306 DB_NAMEmusic_db DB_USERadmin DB_PASSWORDyour_secure_password EMAIL_HOSTsmtp.gmail.com EMAIL_PORT587 EMAIL_USERyour_emailgmail.com EMAIL_PASSWORDyour_app_password # 注意Gmail需用App Password非登录密码 RECEIVER_EMAILteamcompany.com加载方式Python中from dotenv import load_dotenv import os load_dotenv() # 自动读取当前目录下的.env文件 db_config { host: os.getenv(DB_HOST), port: int(os.getenv(DB_PORT)), user: os.getenv(DB_USER), password: os.getenv(DB_PASSWORD), database: os.getenv(DB_NAME) }实操心得.env文件必须加入.gitignore我曾因忘记这步把数据库密码提交到GitHub被安全扫描工具告警——幸好是测试库但教训深刻。4.2 完整代码结构与主流程项目目录结构清晰便于维护music_crawler/ ├── main.py # 主入口调度所有模块 ├── crawler/ │ ├── __init__.py │ └── fetcher.py # 爬虫核心逻辑 ├── processor/ │ ├── __init__.py │ └── cleaner.py # 数据清洗逻辑 ├── storage/ │ ├── __init__.py │ ├── excel_writer.py # Excel写入 │ └── mysql_writer.py # MySQL写入 ├── notifier/ │ ├── __init__.py │ └── email_sender.py # 邮件发送 ├── config/ │ ├── __init__.py │ └── database.py # 数据库连接工厂 ├── logs/ # 日志目录 └── output/ # Excel输出目录main.py核心流程带异常兜底def main(): logger.info( 音乐数据采集任务启动 ) try: # 1. 爬取 raw_data fetch_songs() logger.info(f✅ 爬取完成共获取 {len(raw_data)} 首歌曲) # 2. 清洗 df clean_data(raw_data) logger.info(f✅ 清洗完成有效数据 {len(df)} 条) # 3. 存储 excel_path write_to_excel(df) mysql_result write_to_mysql(df) logger.info(f✅ 存储完成Excel已保存至 {excel_path}MySQL写入 {mysql_result[success_count]} 条) # 4. 通知 send_summary_email(excel_path, mysql_result) logger.info(✅ 邮件发送完成) except Exception as e: logger.error(f❌ 任务执行失败{e}, exc_infoTrue) # 发送告警邮件可选 send_alert_email(str(e)) finally: logger.info( 音乐数据采集任务结束 ) if __name__ __main__: main()关键点说明exc_infoTrue让日志包含完整堆栈定位问题快10倍。finally块确保无论成功失败都会记录结束日志方便监控任务是否卡死。所有模块函数都接受logger对象作为参数实现日志统一管理。4.3 MySQL连接与写入实现实录storage/mysql_writer.py核心代码from sqlalchemy import create_engine, text from sqlalchemy.exc import SQLAlchemyError import logging def get_db_engine(): 创建带连接池的MySQL引擎 db_url fmysqlmysqlconnector://{os.getenv(DB_USER)}:{os.getenv(DB_PASSWORD)} \ f{os.getenv(DB_HOST)}:{os.getenv(DB_PORT)}/{os.getenv(DB_NAME)} return create_engine( db_url, pool_size5, max_overflow10, pool_timeout30, pool_recycle3600 ) def write_to_mysql(df): 将DataFrame写入MySQL返回成功/失败统计 engine get_db_engine() success_count 0 error_count 0 error_rows [] # 确保必要列存在 required_cols [song_title, artist] for col in required_cols: if col not in df.columns: df[col] 未知 # 添加时间戳 df[created_at] pd.Timestamp.now() df[updated_at] pd.Timestamp.now() try: with engine.connect() as conn: # 使用事务 trans conn.begin() try: # 批量插入带ON DUPLICATE KEY UPDATE stmt text( INSERT INTO music_songs (song_title, artist, album, release_date, duration_sec, created_at, updated_at) VALUES (:song_title, :artist, :album, :release_date, :duration_sec, :created_at, :updated_at) ON DUPLICATE KEY UPDATE updated_at VALUES(updated_at) ) # 分批执行每100行一批 for i in range(0, len(df), 100): batch df.iloc[i:i100] conn.execute(stmt, batch.to_dict(records)) trans.commit() success_count len(df) logging.info(f✅ MySQL写入成功{success_count} 条) except SQLAlchemyError as e: trans.rollback() error_count len(df) error_rows.append(fMySQL错误{str(e)}) logging.error(f❌ MySQL写入失败{str(e)}) except Exception as e: error_count len(df) error_rows.append(f连接错误{str(e)}) logging.error(f❌ MySQL连接异常{str(e)}) return { success_count: success_count, error_count: error_count, error_rows: error_rows }参数详解pool_recycle3600连接存活1小时后自动回收防止MySQL的wait_timeout断连。batch分批避免单次SQL过长MySQL默认max_allowed_packet4MB100行足够安全。ON DUPLICATE KEY UPDATE利用uk_title_artist唯一索引实现“存在则更新时间戳不存在则插入”的幂等操作。4.4 邮件发送模块附带Excel摘要的自动化报告notifier/email_sender.py实现import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders import os from datetime import datetime def send_summary_email(excel_path, mysql_result): 发送含Excel附件和摘要的邮件 # 1. 构建邮件对象 msg MIMEMultipart() msg[From] os.getenv(EMAIL_USER) msg[To] os.getenv(RECEIVER_EMAIL) msg[Subject] f 音乐数据日报 - {datetime.now().strftime(%Y-%m-%d %H:%M)} # 2. 编写HTML正文 html_body f h2音乐数据采集报告/h2 pstrong执行时间/strong{datetime.now().strftime(%Y-%m-%d %H:%M:%S)}/p pstrong数据来源/strong公开音乐榜单/p hr h3 本次执行摘要/h3 ul li爬取歌曲总数strong{mysql_result[success_count]}/strong/li liMySQL写入成功strong{mysql_result[success_count]}/strong 条/li liExcel文件大小strong{os.path.getsize(excel_path)/1024:.1f} KB/strong/li li文件名strong{os.path.basename(excel_path)}/strong/li /ul p附件为本次采集的完整数据请查收。/p msg.attach(MIMEText(html_body, html)) # 3. 添加Excel附件 with open(excel_path, rb) as f: part MIMEBase(application, vnd.openxmlformats-officedocument.spreadsheetml.sheet) part.set_payload(f.read()) encoders.encode_base64(part) part.add_header( Content-Disposition, fattachment; filename{os.path.basename(excel_path)}, filenameos.path.basename(excel_path) ) msg.attach(part) # 4. 发送邮件 try: server smtplib.SMTP(os.getenv(EMAIL_HOST), int(os.getenv(EMAIL_PORT))) server.starttls() # 启用TLS加密 server.login(os.getenv(EMAIL_USER), os.getenv(EMAIL_PASSWORD)) server.send_message(msg) server.quit() logging.info(✅ 邮件发送成功) except Exception as e: logging.error(f❌ 邮件发送失败{e})关键安全点server.starttls()必须在server.login()之前否则明文传输密码。Gmail用户必须用App Password在Google账户安全设置中生成不能用登录密码。附件名用os.path.basename()防止路径遍历攻击如../etc/passwd.xlsx。5. 常见问题与排查技巧实录5.1 爬虫常见问题速查表问题现象可能原因排查命令/方法解决方案返回空列表XPath取不到数据页面是JS渲染requests拿不到curl -s URL | head -20查看原始HTML改用Selenium仅当必须或找API接口F12 Network找XHRConnectionTimeout目标站屏蔽了你的IPping target.com看是否通加time.sleep(2)或换代理合法公开代理SSLErrorSSL证书验证失败pip install --upgrade certifi在requests.get()中加verifyFalse仅测试环境中文乱码编码未指定response.content[:100]看二进制头response.encoding utf-8或response.apparent_encoding实操心得永远先用curl或浏览器开发者工具看原始响应再写代码。我见过太多人对着BeautifulSoup报错瞎猜其实curl -v URL一眼就能看到HTTP状态码是403还是200。5.2 MySQL写入问题深度排查问题IntegrityError: (1062, Duplicate entry xxx for key uk_title_artist)这是好消息说明唯一索引生效了数据没重复写入。但如果你期望“更新”而非“跳过”检查两点ON DUPLICATE KEY UPDATE语句是否写对漏了VALUES()括号会报错。UNIQUE KEY是否建在正确的字段组合上用SHOW CREATE TABLE music_songs;确认。问题DataError: (1366, Incorrect string value: \\xF0\\x9F\\x8E\\xB5)这是Emoji导致的UTF-8mb4问题。MySQL默认字符集utf8只支持3字节Emoji需4字节。修复步骤修改MySQL配置文件my.cnf[client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci重启MySQL然后执行SQLALTER DATABASE music_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE music_songs CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;问题写入速度慢10秒/100条检查连接池是否生效打印engine.pool.checkedout()应为0空闲。如果持续0说明连接没释放加conn.close()或用with上下文。5.3 邮件发送失败典型场景场景错误日志关键词根本原因解决方案登录失败535 Authentication failedApp Password未开启或输错重新生成App Password确认邮箱地址拼写连接超时timeout防火墙拦截SMTP端口换端口Gmail用587TLS或465SSL附件过大552 5.2.2 Mail message size exceeded邮箱服务商限制Gmail上限25MB压缩Exceldf.to_excel(..., engineopenpyxl, compressionzip)注意生产环境务必加邮件发送成功率监控。简单做法在send_summary_email()末尾加一行logging.info(f 邮件发送完成收件人{os.getenv(RECEIVER_EMAIL)})然后用日志系统如ELK告警“过去1小时无此日志”。5.4 全流程调试技巧分段验证法不要等全部写完再运行按以下顺序逐段验证爬虫段注释掉后续所有代码只留fetch_songs()打印len(raw_data)和raw_data[0]确认能取到数据。清洗段把raw_data写死为[{title:晴天,artist:周杰伦}]运行clean_data()打印df.head()确认DataFrame结构正确。Excel段注释掉MySQL和邮件只运行write_to_excel()手动打开生成的Excel确认格式、列宽、摘要表都OK。MySQL段用SELECT * FROM music_songs LIMIT 5;查数据库确认数据写入且中文正常。邮件段先发一封纯文本测试邮件不带附件确认SMTP通路。每段验证通过再进入下一段效率提升300%。我第一次做时跳过第2步结果清洗后duration_sec全是0查了2小时才发现正则写错了——分段验证早发现早治疗。6. 运维与扩展建议6.1 如何让脚本长期稳定运行定时任务Linux用crontab每天9点执行0 9 * * * cd /path/to/music_crawler /path/to/music_env/bin/python main.py /path/to/logs/cron.log 21进程守护用systemdLinux或launchdmacOS管理崩溃自动重启。日志轮转用Python的RotatingFileHandler日志文件超过10MB自动切分保留30天from logging.handlers import RotatingFileHandler handler RotatingFileHandler(./logs/app.log, maxBytes10*1024*1024, backupCount30)

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑