🔥本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~持续更新中!!

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [📚 上期回顾](#📚 上期回顾)
- [🎯 本篇目标](#🎯 本篇目标)
- [💡 上线前的准备清单](#💡 上线前的准备清单)
- [🛠️ 模块 1:配置管理](#🛠️ 模块 1:配置管理)
- [🛠️ 模块 2:日志管理](#🛠️ 模块 2:日志管理)
- [🛠️ 模块 3:定时任务](#🛠️ 模块 3:定时任务)
-
- [方案 1:系统 Crontab(推荐 Linux)](#方案 1:系统 Crontab(推荐 Linux))
- [方案 2:APScheduler(推荐 Windows 或需要复杂调度)](#方案 2:APScheduler(推荐 Windows 或需要复杂调度))
- [🛠️ 模块 4:异常告警](#🛠️ 模块 4:异常告警)
- [🛠️ 模块 5:主程序改造](#🛠️ 模块 5:主程序改造)
- [📝 运维文档模板](#📝 运维文档模板)
- [📝 小结](#📝 小结)
- [🎓 专栏总结](#🎓 专栏总结)
- [🌟 文末](#🌟 文末)
-
- [📌 专栏持续更新中|建议收藏 + 订阅](#📌 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 👉 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅/关注专栏《Python爬虫实战》
订阅后更新会优先推送,按目录学习更高效~
📚 上期回顾
恭喜你完成了信息聚合站项目《项目 2:信息聚合站 Demo(列表+详情+增量+质量报告)》!现在你有了一个功能完整的爬虫系统------能采集、能去重、能生成质量报告。但它还只是一个"玩具",离真正的"生产级"还差最后一步。
你有没有想过这些问题:
- 怎么让爬虫每天自动运行,不用手动敲命令?
- 日志文件越来越大,怎么自动清理?
- 采集失败了,怎么第一时间知道?
今天,我们就来搞定这最后一公里------让爬虫真正上线运行!
🎯 本篇目标
看完这篇,你能做到:
- 配置定时任务(crontab/理**(日志轮转、分级记录)
- 异常监控告警(邮件/企业微信/钉钉)
- 编写运维文档(让别人也能接手)
验收标准:爬虫能每天凌晨 2 点自动运行,失败时发送告警通知。
💡 上线前的准备清单
检查清单
json
✅ 代码能稳定运行(至少手动跑 3 次不出错)
✅ 数据库路径是绝对路径(不要用相对路径)
✅ 敏感信息已配置化(API Key、数据库密码等)
✅ 依赖已冻结(requirements.txt)
✅ README 写清楚了(别人能看懂怎么用)
目录结构规范
json
my_spider/
├── config/
│ ├── config.yaml # 配置文件
│ └── logging.yaml # 日志配置
├── src/
│ ├── crawler.py # 爬虫主逻辑
│ ├── db_manager.py # 数据库管理
│ └── utils.py # 工具函数
├── logs/ # 日志目录
├── data/ # 数据目录
│ └── aggregator.db
├── requirements.txt # 依赖清单
├── main.py # 入口文件
└── README.md # 使用文档
🛠️ 模块 1:配置管理
配置文件设计
yaml
# config/config.yaml
app:
name: "信息聚合爬虫"
version: "1.0.0"
crawler:
source_name: "示例新闻网"
max_pages: 10
timeout: 15
retry: 3
delay: 2 # 请求间隔(秒)
database:
path: "/home/user/my_spider/data/aggregator.db"
logging:
level: "INFO"
log_dir: "/home/user/my_spider/logs"
max_bytes: 10485760 # 10MB
backup_count: 5
alert:
enabled: true
type: "email" # email/wechat/dingtalk
email:
smtp_server: "smtp.qq.com"
smtp_port: 587
sender: "your_email@qq.com"
password: "your_password"
receivers: ["admin@example.com"]
threshold:
fail_rate: 0.3 # 失败率超过 30% 告警
配置加载器
python
# src/config_loader.py
import yaml
from pathlib import Path
class Config:
"""配置管理器"""
def __init__(self, config_file="config/config.yaml"):
self.config_file = Path(config_file)
self.config = self._load_config()
def _load_config(self):
"""加载配置文件"""
if not self.config_file.exists():
raise FileNotFoundError(f"配置文件不存在:{self.config_file}")
with open(self.config_file, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
def get(self, key_path, default=None):
"""
获取配置项(支持多级路径)
示例:
config.get('crawler.timeout') # 获取 crawler.timeout
"""
keys = key_path.split('.')
value = self.config
for key in keys:
if isinstance(value, dict):
value = value.get(key)
else:
return default
if value is None:
return default
return value
# 全局配置实例
config = Config()
🛠️ 模块 2:日志管理
日志配置
python
# src/logger.py
import logging
import logging.handlers
from pathlib import Path
from datetime import datetime
def setup_logger(name='spider', log_dir='logs', level='INFO'):
"""
配置日志系统
特性:
- 按日期自动创建日志文件
- 日志轮转(超过大小自动切分)
- 同时输出到控制台和文件
"""
# 创建日志目录
log_dir = Path(log_dir)
log_dir.mkdir(exist_ok=True)
# 创建 logger
logger = logging.getLogger(name)
logger.setLevel(getattr(logging, level.upper()))
# 清除已有的 handlers(避免重复)
logger.handlers.clear()
# 日志格式
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 1. 控制台输出
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 2. 文件输出(按日期)
today = datetime.now().strftime('%Y%m%d')
log_file = log_dir / f'spider_{today}.log'
file_handler = logging.handlers.RotatingFileHandler(
log_file,
maxBytes=10*1024*1024, # 10MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# 3. 错误日志单独记录
error_file = log_dir / f'error_{today}.log'
error_handler = logging.handlers.RotatingFileHandler(
error_file,
maxBytes=10*1024*1024,
backupCount=5,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)
logger.addHandler(error_handler)
return logger
# 创建全局 logger
logger = setup_logger()
日志使用示例
python
from src.logger import logger
# 不同级别的日志
logger.debug("调试信息:变量值为 xxx")
logger.info("✅ 采集成功:获得 100 条数据")
logger.warning("⚠️ 部分字段缺失:author 字段为空")
logger.error("❌ 采集失败:网络超时")
# 记录异常堆栈
try:
# 可能出错的代码
pass
except Exception as e:
logger.exception(f"未处理的异常:{e}")
🛠️ 模块 3:定时任务
方案 1:系统 Crontab(推荐 Linux)
步骤 1:编写启动脚本
bash
#!/bin/bash
# run_spider.sh
# 设置环境变量
export PATH=/usr/local/bin:/usr/bin:/bin
export LANG=zh_CN.UTF-8
# 进入项目目录
cd /home/user/my_spider
# 激活虚拟环境
source venv/bin/activate
# 运行爬虫
python main.py >> logs/cron_$(date +\%Y\%m\%d).log 2>&1
# 记录运行时间
echo "爬虫运行完成:$(date)" >> logs/cron.log
步骤 2:添加执行权限
bash
chmod +x run_spider.sh
步骤 3:配置 crontab
json
# 编辑定时任务
crontab -e
# 添加以下内容(每天凌晨 2 点运行)
0 2 * * * /home/user/my_spider/run_spider.sh
crontab 时间语法:
json
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 日期 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 星期 (0 - 7,0 和 7 都是周日)
# │ │ │ │ │
# * * * * * 命令
# 示例
0 2 * * * # 每天凌晨 2 点
0 */6 * * * # 每 6 小时
30 9 * * 1 # 每周一上午 9:30
0 0 1 * * # 每月 1 号零点
方案 2:APScheduler(推荐 Windows 或需要复杂调度)
python
# scheduler.py
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from datetime import datetime
from src.logger import logger
from main import run_spider
def scheduled_job():
"""定时任务入口"""
logger.info("="*50)
logger.info(f"⏰ 定时任务开始:{datetime.now()}")
logger.info("="*50)
try:
run_spider()
logger.info("✅ 定时任务完成")
except Exception as e:
logger.exception(f"❌ 定时任务失败:{e}")
def main():
scheduler = BlockingScheduler()
# 添加定时任务
scheduler.add_job(
scheduled_job,
trigger=CronTrigger(hour=2, minute=0), # 每天凌晨 2 点
id='daily_crawl',
name='每日采集',
replace_existing=True
)
logger.info("📅 调度器已启动")
logger.info("下次运行时间:" + str(scheduler.get_jobs()[0].next_run_time))
try:
scheduler.start()
except (KeyboardInterrupt, SystemExit):
logger.info("调度器已停止")
if __name__ == '__main__':
main()
运行方式:
bash
# 后台运行
nohup python scheduler.py > logs/scheduler.log 2>&1 &
# 或者用 systemd(更专业)
🛠️ 模块 4:异常告警
邮件告警
python
# src/alerter.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from src.config_loader import config
from src.logger import logger
class EmailAlerter:
"""邮件告警器"""
def __init__(self):
self.smtp_server = config.get('alert.email.smtp_server')
self.smtp_port = config.get('alert.email.smtp_port')
self.sender = config.get('alert.email.sender')
self.password = config.get('alert.email.password')
self.receivers = config.get('alert.email.receivers')
def send_alert(self, subject, content):
"""
发送告警邮件
Args:
subject: 邮件主题
content: 邮件内容(支持 HTML)
"""
if not config.get('alert.enabled'):
logger.info("告警已禁用,跳过发送")
return
try:
# 创建邮件
msg = MIMEMultipart()
msg['From'] = self.sender
msg['To'] = ','.join(self.receivers)
msg['Subject'] = subject
# 添加正文
msg.attach(MIMEText(content, 'html', 'utf-8'))
# 发送
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
server.starttls()
server.login(self.sender, self.password)
server.send_message(msg)
logger.info(f"📧 告警邮件已发送:{subject}")
except Exception as e:
logger.error(f"邮件发送失败:{e}")
def send_crawl_report(self, stats):
"""发送采集报告"""
total = stats.get('total', 0)
success = stats.get('success', 0)
failed = stats.get('failed', 0)
fail_rate = failed / total if total > 0 else 0
# 判断是否需要告警
threshold = config.get('alert.threshold.fail_rate', 0.3)
if fail_rate > threshold:
subject = f"⚠️ 爬虫失败率告警:{fail_rate*100:.1f}%"
status_emoji = "🚨"
else:
subject = f"✅ 爬虫运行报告:成功率 {(1-fail_rate)*100:.1f}%"
status_emoji = "✅"
# 构造 HTML 内容
content = f"""
<html>
<body>
<h2>{status_emoji} 爬虫运行报告</h2>
<table border="1" cellpadding="10">
<tr><td>总计</td><td>{total}</td></tr>
<tr><td>成功</td><td style="color: green">{success}</td></tr>
<tr><td>失败</td><td style="color: red">{failed}</td></tr>
<tr><td>失败率</td><td>{fail_rate*100:.2f}%</td></tr>
</table>
<p>时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
{'<p style="color: red">⚠️ 失败率超过阈值,请检查日志!</p>' if fail_rate > threshold else ''}
</body>
</html>
"""
self.send_alert(subject, content)
# 全局告警器
alerter = EmailAlerter()
企业微信告警(可选)
python
import requests
class WeChatAlerter:
"""企业微信告警器"""
def __init__(self, webhook_url):
self.webhook_url = webhook_url
def send_alert(self, content):
"""发送文本消息"""
data = {
"msgtype": "text",
"text": {
"content": content
}
}
try:
resp = requests.post(self.webhook_url, json=data, timeout=5)
if resp.json().get('errcode') == 0:
logger.info("📱 企业微信告警已发送")
else:
logger.error(f"企业微信发送失败:{resp.text}")
except Exception as e:
logger.error(f"企业微信发送异常:{e}")
# 使用
# wechat = WeChatAlerter('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx')
# wechat.send_alert('⚠️ 爬虫采集失败率达到 50%,请检查!')
🛠️ 模块 5:主程序改造
python
# main.py(增强版)
from src.crawler import AggregatorSpider
from src.config_loader import config
from src.logger import logger
from src.alerter import alerter
from datetime import datetime
import traceback
def run_spider():
"""主运行逻辑"""
start_time = datetime.now()
logger.info("="*60)
logger.info(f"🚀 爬虫启动:{start_time.strftime('%Y-%m-%d %H:%M:%S')}")
logger.info("="*60)
stats = {
'total': 0,
'success': 0,
'failed': 0,
'error': None
}
try:
# 读取配置
source_name = config.get('crawler.source_name')
max_pages = config.get('crawler.max_pages', 10)
# 创建爬虫实例
spider = AggregatorSpider(source_name)
# 运行采集
spider.run_full_crawl(
list_url_template='https://news.example.com/list?page={page}',
max_pages=max_pages
)
# 获取统计信息
stats = spider.db.get_stats()
stats['total'] = sum(stats.values())
# 打印统计
logger.info("\n" + "="*60)
logger.info("📊 采集统计")
logger.info("="*60)
logger.info(f"总计:{stats['total']}")
logger.info(f"成功:{stats.get('SUCCESS', 0)}")
logger.info(f"失败:{stats.get('FAILED', 0)}")
except Exception as e:
logger.exception(f"❌ 爬虫运行异常:{e}")
stats['error'] = traceback.format_exc()
finally:
# 计算耗时
end_time = datetime.now()
duration = (end_time - start_time).total_seconds()
logger.info("="*60)
logger.info(f"⏱️ 运行耗时:{duration:.2f} 秒")
logger.info("="*60)
# 发送告警(如果配置了)
try:
formatted_stats = {
'total': stats['total'],
'success': stats.get('SUCCESS', 0),
'failed': stats.get('FAILED', 0)
}
alerter.send_crawl_report(formatted_stats)
except Exception as e:
logger.error(f"告警发送失败:{e}")
if __name__ == '__main__':
run_spider()
📝 运维文档模板
markdown
# 信息聚合爬虫 - 运维手册
## 1. 部署说明
### 环境要求
- Python 3.8+
- 磁盘空间 > 10GB
- 内存 > 2GB
### 安装步骤
```json
# 1. 克隆代码
git clone https://github.com/yourname/my_spider.git
cd my_spider
# 2. 创建虚拟环境
python -m venv venv
source venv/bin/activate
# 3. 安装依赖
pip install -r requirements.txt
# 4. 配置文件
cp config/config.example.yaml config/config.yaml
# 编辑 config.yaml,填写数据库路径、告警配置等
# 5. 初始化数据库
python -c "from src.db_schema import init_database; init_database()"
# 6. 测试运行
python main.py
## 2. 定时任务配置
# 编辑 crontab
crontab -e
# 添加定时任务(每天凌晨 2 点)
0 2 * * * /home/user/my_spider/run_spider.sh
## 3. 日志查看
# 查看今日日志
tail -f logs/spider_$(date +\%Y\%m\%d).log
# 查看错误日志
tail -f logs/error_$(date +\%Y\%m\%d).log
# 搜索关键词
grep "失败" logs/spider_*.log
## 4. 常见问题
### 问题 1:数据库锁定
**现象**:`database is locked`
**原因**:多个进程同时写入
**解决**:检查是否有重复的定时任务在运行
### 问题 2:告警邮件发不出去
**检查清单**:
* SMTP 服务器地址是否正确
* 端口是否开放(587/465)
* 密码是否是"授权码"(QQ 邮箱需要)
* 防火墙是否拦截
## 5. 监控指标
每日检查:
* 采集成功率 > 90%
* 日志无 ERROR 级别错误
* 数据库增长正常
📝 小结
今天我们完成了爬虫的上线部署全流程:
- 配置管理(YAML 配置文件)
- 日志系统(分级记录、自动轮转)
- 定时任务(Crontab/APScheduler)
- 异常告警(邮件/企业微信)
- 运维文档(让别人也能维护)
至此,你的爬虫已经从"玩具"升级为"生产级系统"------能自动运行、能监控告警、能稳定服务!
🎓 专栏总结
恭喜你完成全部 28 篇的学习!我们一起走过了:
第 0-1 章 :环境搭建、网页基础
第 2-3 章 :Requests 采集、解析清洗
第 4-5 章 :数据入库、增量去重、断点续爬
第 6-7 章 :Playwright 动态页面、API 逆向
第 8 章:完整项目、上线部署
现在你已经掌握了 爬虫工程化的完整链路 ,可以独立完成从需求分析到上线运维的全流程。稳定性第一(容错、重试、去重)
✅ 合规合法 (robots.txt、频率控制)
✅ 质量优先 (数据清洗、质量检查)
✅ 可维护性(日志、文档、监控)
加油!你已经是一个合格的爬虫工程师了!往后,我就直接上硬通货了,带大家做各种爬虫项目!
🌟 文末
好啦~以上就是本期 《Python爬虫实战》的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
📌 专栏持续更新中|建议收藏 + 订阅
专栏 👉 《Python爬虫实战》,我会按照"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一篇都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】写成专栏实战?
评论区留言告诉我你的需求,我会优先安排更新 ✅
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。