Python站点定时爬虫脚本开发与腾讯云CVM crontab定时执行完全指南
前言:为什么需要把爬虫搬到云上
在数据驱动的时代,网络爬虫已成为获取公开信息的重要工具。然而,在本地电脑上运行爬虫存在诸多痛点:电脑关机或待机后任务中断、本地网络IP易被限制、无法保证7×24小时持续采集、手动启动脚本效率低下。将爬虫部署到云端服务器,配合定时任务自动执行,是解决这些问题的标准方案。
腾讯云CVM(Cloud Virtual Machine)云服务器提供了稳定可靠的计算资源,结合Linux系统原生的crontab定时任务工具,可以轻松实现爬虫脚本的自动化、周期性执行。本文将从零开始,带领读者完成从爬虫脚本开发到CVM部署、crontab定时配置的完整流程,并提供一套可直接复用代码与配置模板。
需要先登录腾讯云控制台,点击:腾讯云控制台,还没有账号,点击:注册后再关联,已有账号点击:登录后再关联
第一部分:Python站点爬虫脚本开发
1.1 爬虫脚本的核心构成
一个生产级的定时爬虫脚本,至少应包含以下五个核心模块:HTTP请求模块、数据解析模块、异常处理模块、数据持久化模块、执行入口与日志模块。缺少任何一个环节,都可能导致爬虫在无人值守时崩溃或数据丢失。
1.2 依赖库选型与安装
本文选用Python生态中最成熟、最稳定的爬虫技术栈:
- requests:最流行的HTTP客户端库,用于发送网络请求、获取网页内容
- BeautifulSoup4:HTML/XML解析库,用于从网页中提取结构化数据
- lxml:高性能HTML解析器,作为BeautifulSoup的解析引擎可大幅提升解析效率
- pymysql:Python连接MySQL数据库的驱动库,用于将采集数据存入数据库
- logging:Python标准日志库,用于记录脚本运行状态与错误信息
在本地开发环境中,通过以下命令安装所有依赖:
pip install requests beautifulsoup4 lxml pymysql为方便后续在CVM上批量安装,建议使用pip freeze > requirements.txt命令将当前环境的全部依赖导出到requirements.txt文件中,部署时只需执行pip install -r requirements.txt即可一键完成所有依赖安装。
1.3 编写爬虫脚本:完整示例
以下是一个完整的站点定时爬虫脚本示例,模拟采集某技术社区的最新文章标题与链接,并将数据存入MySQL数据库。脚本涵盖了请求发送、异常捕获、数据解析、数据库写入和日志记录等全部核心环节。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
站点定时爬虫脚本 - 每日采集技术社区最新文章
适用于腾讯云CVM + crontab 定时执行
"""
import requests
from bs4 import BeautifulSoup
import pymysql
import logging
import time
import json
from datetime import datetime
from typing import List, Dict, Optional
# ========== 日志配置 ==========
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/var/log/spider.log', encoding='utf-8'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# ========== 数据库配置 ==========
DB_CONFIG = {
'host': 'localhost',
'user': 'spider_user',
'password': 'your_password',
'database': 'spider_db',
'charset': 'utf8mb4'
}
# ========== 爬虫配置 ==========
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
TIMEOUT = 30
MAX_RETRIES = 3
def get_db_connection():
"""获取数据库连接"""
try:
conn = pymysql.connect(**DB_CONFIG)
return conn
except pymysql.Error as e:
logger.error(f'数据库连接失败: {e}')
raise
def init_database():
"""初始化数据库表结构"""
conn = get_db_connection()
cursor = conn.cursor()
create_table_sql = '''
CREATE TABLE IF NOT EXISTS articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(500) NOT NULL COMMENT '文章标题',
url VARCHAR(1000) NOT NULL COMMENT '文章链接',
summary TEXT COMMENT '文章摘要',
publish_time DATETIME COMMENT '发布时间',
crawl_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '采集时间',
UNIQUE KEY uk_url (url(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='采集文章表'
'''
cursor.execute(create_table_sql)
conn.commit()
cursor.close()
conn.close()
logger.info('数据库表初始化完成')
def fetch_page(url: str) -> Optional[str]:
"""
发送HTTP请求获取网页内容,带重试机制
"""
for attempt in range(MAX_RETRIES):
try:
logger.info(f'正在请求: {url} (尝试 {attempt + 1}/{MAX_RETRIES})')
response = requests.get(
url,
headers=HEADERS,
timeout=TIMEOUT
)
response.raise_for_status() # 检查HTTP状态码
response.encoding = response.apparent_encoding
logger.info(f'请求成功,状态码: {response.status_code}')
return response.text
except requests.exceptions.Timeout:
logger.warning(f'请求超时 (尝试 {attempt + 1}/{MAX_RETRIES})')
time.sleep(2 ** attempt) # 指数退避
except requests.exceptions.ConnectionError:
logger.warning(f'连接错误 (尝试 {attempt + 1}/{MAX_RETRIES})')
time.sleep(2 ** attempt)
except requests.exceptions.HTTPError as e:
logger.error(f'HTTP错误: {e}')
break
except Exception as e:
logger.error(f'未知错误: {e}')
break
logger.error(f'请求失败: {url}')
return None
def parse_articles(html: str) -> List[Dict[str, str]]:
"""
解析HTML提取文章列表
"""
soup = BeautifulSoup(html, 'lxml')
articles = []
# 假设目标站点的文章列表结构为: .article-list > .item
items = soup.select('.article-list .item')
if not items:
logger.warning('未找到文章列表元素,请检查选择器')
# 可在此处添加备选选择器
return articles
for item in items:
try:
title_elem = item.select_one('.title a')
if not title_elem:
continue
title = title_elem.get_text(strip=True)
url = title_elem.get('href')
if url and not url.startswith('http'):
url = 'https://example.com' + url # 补全完整URL
# 提取摘要(如果有)
summary_elem = item.select_one('.summary')
summary = summary_elem.get_text(strip=True) if summary_elem else ''
articles.append({
'title': title,
'url': url,
'summary': summary
})
except Exception as e:
logger.error(f'解析单个文章失败: {e}')
continue
logger.info(f'成功解析 {len(articles)} 篇文章')
return articles
def save_articles(articles: List[Dict[str, str]]) -> int:
"""
将文章数据批量存入数据库(去重)
"""
if not articles:
return 0
conn = get_db_connection()
cursor = conn.cursor()
insert_sql = '''
INSERT IGNORE INTO articles (title, url, summary, crawl_time)
VALUES (%s, %s, %s, NOW())
'''
data = [(a['title'], a['url'], a['summary']) for a in articles]
try:
cursor.executemany(insert_sql, data)
conn.commit()
inserted = cursor.rowcount
logger.info(f'成功插入 {inserted} 条新记录(跳过 {len(articles) - inserted} 条重复)')
return inserted
except pymysql.Error as e:
logger.error(f'数据库写入失败: {e}')
conn.rollback()
return 0
finally:
cursor.close()
conn.close()
def run_spider():
"""
爬虫主执行函数
"""
start_time = time.time()
logger.info('=' * 50)
logger.info(f'爬虫开始执行 - {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
try:
# 1. 初始化数据库
init_database()
# 2. 抓取目标页面
target_url = 'https://example.com/articles' # 替换为实际目标URL
html = fetch_page(target_url)
if not html:
logger.error('获取页面内容失败,爬虫终止')
return
# 3. 解析数据
articles = parse_articles(html)
if not articles:
logger.warning('未解析到任何文章数据')
return
# 4. 存储数据
saved = save_articles(articles)
elapsed = time.time() - start_time
logger.info(f'爬虫执行完成 - 耗时: {elapsed:.2f}秒, 新增: {saved}条')
except Exception as e:
logger.error(f'爬虫执行过程中发生未捕获异常: {e}', exc_info=True)
finally:
logger.info('=' * 50)
if __name__ == '__main__':
run_spider()1.4 脚本关键设计解析
异常处理机制:爬虫在无人值守的服务器上运行,任何未捕获的异常都会导致任务中断。上述脚本在fetch_page函数中对Timeout、ConnectionError、HTTPError等常见异常进行了分类捕获,并实现了最多3次重试与指数退避策略。同时在run_spider主函数最外层添加了try-except兜底,确保任何意外错误都能被记录而不会导致整个任务崩溃。
数据去重设计:在数据库建表时对url字段设置了唯一索引(UNIQUE KEY),并使用INSERT IGNORE语句写入数据。这种设计可以自动跳过已存在的记录,避免重复采集造成的数据冗余。
日志记录:脚本同时将日志输出到控制台和文件(/var/log/spider.log),既方便在crontab中重定向查看,也保留了持久化的运行记录用于事后排查。
第二部分:腾讯云CVM云服务器环境准备
2.1 CVM实例选购建议
在腾讯云控制台购买CVM实例时,需根据爬虫的实际负载做合理选型:
- 地域选择:建议选择距离目标网站服务器较近的地域,或选择中国大陆境内的地域以保证网络稳定性。若目标网站为境外站点,可考虑选用腾讯云的海外节点。
- 操作系统:推荐Ubuntu 22.04 LTS或CentOS 7.9。本文以Ubuntu 22.04 LTS为例进行演示。
- 实例规格:对于日均采集量在万级以下的轻量爬虫,2核4GB内存的配置已绰绰有余。若涉及大规模并发采集或使用Selenium等重型工具,建议升级至4核8GB或更高配置。
- 公网带宽:建议选择≥3Mbps的带宽,确保请求响应速度。若采集数据量较大,可考虑按流量计费模式。
- 安全组配置:至少开放22端口(SSH)用于远程管理。如果爬虫需要对外提供API服务,还需开放对应端口。
2.2 连接CVM与系统初始化
购买实例后,通过SSH登录服务器:
ssh -i 你的密钥.pem ubuntu@服务器公网IP登录后首先更新系统包并安装基础工具:
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-pip python3-venv git vim验证Python环境:
python3 --version
pip3 --version2.3 Python虚拟环境配置
强烈建议为爬虫项目创建独立的Python虚拟环境,以隔离不同项目的依赖,避免版本冲突。操作步骤如下:
# 创建项目目录
mkdir -p ~/spider_project
cd ~/spider_project
# 创建虚拟环境
python3 -m venv venv
# 激活虚拟环境
source venv/bin/activate
# 确认环境已激活(命令行前缀会显示(venv))
which python3虚拟环境激活后,所有pip install安装的包都会被隔离在~/spider_project/venv目录下,不会影响系统全局的Python环境。
2.4 上传爬虫项目文件
将本地开发完成的爬虫脚本和requirements.txt文件上传到CVM。推荐使用scp命令:
# 在本地电脑执行
scp -i 你的密钥.pem spider.py requirements.txt ubuntu@服务器IP:~/spider_project/也可以在CVM上直接使用git clone从代码仓库拉取项目:
git clone https://github.com/yourname/spider_project.git文件上传后,在CVM上激活虚拟环境并安装依赖:
cd ~/spider_project
source venv/bin/activate
pip install -r requirements.txt至此,爬虫项目已成功部署到CVM,接下来可以通过命令行手动测试脚本是否能正常运行:
python3 spider.py确认脚本执行无误后,即可进入定时任务配置环节。
第三部分:crontab定时任务配置
3.1 crontab基础概念
crontab是Unix/Linux系统中用于设置周期性执行任务的工具。每个用户都有自己的crontab配置文件,通过crontab -e命令可以编辑当前用户的定时任务列表。cron守护进程会在后台持续运行,按照配置的时间表达式自动执行指定的命令或脚本。
腾讯云CVM基于标准的Linux内核,完全支持crontab的所有功能。此外,腾讯云还提供了云监控(Cloud Monitor)服务,可以监控CVM上定时任务的执行情况并提供告警功能。
3.2 crontab时间表达式详解
crontab时间表达式由5个字段组成,分别表示分钟、小时、日、月、星期:
┌─────────── 分钟 (0 - 59)
│ ┌───────── 小时 (0 - 23)
│ │ ┌─────── 日 (1 - 31)
│ │ │ ┌───── 月 (1 - 12)
│ │ │ │ ┌─── 星期 (0 - 7, 0和7都表示周日)
│ │ │ │ │
* * * * * 要执行的命令常用的时间表达式示例:
0 1 * * *— 每天凌晨1点执行*/30 * * * *— 每30分钟执行一次0 8,20 * * *— 每天上午8点和晚上8点各执行一次0 9 * * 1— 每周一上午9点执行0 0 1 * *— 每月1日凌晨0点执行
特别提醒:“每5分钟执行一次”的正确写法是*/5 * * * *,但要注意如果脚本执行时间超过5分钟,会出现任务重叠、并发冲突的问题。建议将执行间隔设置为脚本实际运行时间的2倍以上。
3.3 配置crontab定时任务
执行以下命令编辑当前用户的crontab文件:
crontab -e如果是第一次执行,系统会提示选择编辑器,推荐选择nano或vim。在文件末尾添加以下内容:
# 每天凌晨2点执行爬虫脚本
0 2 * * * cd /home/ubuntu/spider_project && /home/ubuntu/spider_project/venv/bin/python3 spider.py >> /var/log/spider_cron.log 2>&1
# 每6小时执行一次(可选)
0 */6 * * * cd /home/ubuntu/spider_project && /home/ubuntu/spider_project/venv/bin/python3 spider.py >> /var/log/spider_cron.log 2>&1配置中的关键点解析:
- 使用绝对路径:cron执行时的环境变量与终端不同,必须使用命令和文件的绝对路径。例如
/home/ubuntu/spider_project/venv/bin/python3而非python3。 - 切换工作目录:使用
cd命令切换到项目目录,确保脚本中的相对路径(如./config.json)能正确解析。 - 日志重定向:
>> /var/log/spider_cron.log 2>&1将标准输出和标准错误都追加写入日志文件。
保存并退出编辑器后,cron任务会自动生效。可以通过以下命令查看当前用户的所有定时任务:
crontab -l3.4 使用shell脚本封装(推荐做法)
当爬虫项目较为复杂(涉及多个Python文件、配置文件、数据目录等)时,建议编写一个shell脚本来封装启动逻辑,然后在crontab中直接调用该shell脚本。这种方式更易于维护和调试。
创建启动脚本~/spider_project/run_spider.sh:
#!/bin/bash
# 爬虫启动脚本
# 设置工作目录
cd /home/ubuntu/spider_project || exit 1
# 激活虚拟环境并执行爬虫
source /home/ubuntu/spider_project/venv/bin/activate
python3 spider.py
# 记录执行状态
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 爬虫执行完成,退出码: $?" >> /var/log/spider_cron.log给脚本添加可执行权限:
chmod +x ~/spider_project/run_spider.sh然后在crontab中配置:
0 2 * * * /home/ubuntu/spider_project/run_spider.sh3.5 crontab日志管理与排障
定时任务配置完成后,需要持续监控其运行状态。以下是常用的排查手段:
查看cron系统日志:
sudo grep CRON /var/log/syslog | tail -20查看爬虫自定义日志:
tail -f /var/log/spider.log
tail -f /var/log/spider_cron.log常见问题与解决方案:
- 脚本未执行:检查cron服务是否运行
sudo service cron status,确认时间表达式语法正确。 - 脚本执行但无输出:cron环境与终端环境不同,环境变量(如PATH)可能不完整。解决方法是在crontab中显式设置PATH,或在shell脚本中
source ~/.bashrc加载环境变量。 - 权限被拒绝:确保脚本和日志文件有正确的读写权限,
chmod +x赋予执行权限。 - 任务重叠执行:如果脚本执行时间过长,下一次任务启动时上一次还未结束,会导致资源竞争。可使用
flock命令实现任务互斥:0 2 * * * flock -n /tmp/spider.lock -c /path/to/run_spider.sh
第四部分:进阶优化与运维建议
4.1 爬虫合规性提醒
网络爬虫的合法合规使用是每个开发者必须重视的问题。在部署定时爬虫前,请务必注意以下几点:
- 仅采集公开可访问的数据,不爬取需要登录或涉及用户隐私的内容
- 遵守目标网站的
robots.txt协议 - 控制合理的请求频率,避免对目标服务器造成过大压力
- 不得将采集数据用于商业牟利或侵犯他人权益的用途
4.2 应对反爬策略
实际生产环境中,很多站点会部署反爬机制。以下是一些常见的应对策略:
- 更换User-Agent:定期轮换User-Agent,模拟不同浏览器访问
- 使用代理IP:通过代理池轮换IP地址,避免单一IP高频请求被封禁
- 设置随机延迟:在请求之间加入随机间隔(如1-5秒),模拟人类浏览行为
- 处理验证码:对于简单的验证码,可使用OCR技术识别;复杂的验证码建议人工介入或更换采集策略
4.3 监控与告警配置
生产环境的定时爬虫必须配备监控告警机制,确保在任务失败时能及时感知。腾讯云CVM可结合以下方式实现监控:
- 云监控告警:在腾讯云控制台配置CPU、内存、磁盘等资源指标的告警策略
- 日志关键字监控:使用
grep配合cron定时检查日志中是否包含"ERROR"或"失败"等关键词,发现问题后通过邮件或短信通知 - 数据量监控:每日采集完成后,检查数据库新增记录数是否在合理范围内,异常偏低可能意味着采集失败
4.4 使用systemd替代crontab(可选)
对于需要更精细控制(如依赖关系、重启策略、开机自启)的定时任务,可以考虑使用systemd timer替代crontab。systemd timer提供了更丰富的功能,如任务依赖、错过执行后的补偿执行等。但crontab凭借其简洁性和普及度,仍是大多数爬虫定时任务的首选方案。
总结
本文完整地介绍了从Python爬虫脚本开发到腾讯云CVM部署、crontab定时执行的整套流程。核心要点可概括为:
- 爬虫脚本需包含完善的异常处理、日志记录和数据去重机制,确保无人值守时的稳定性
- CVM选购时根据爬虫负载合理配置规格,使用虚拟环境隔离项目依赖
- crontab配置时必须使用绝对路径、切换工作目录、重定向日志,这三项是避免"cron不执行"的黄金法则
- 生产环境需配合日志监控和告警机制,确保任务异常时能快速发现和恢复
通过以上方法,读者可以快速搭建一套稳定、可靠的云上定时爬虫系统,让数据采集真正实现7×24小时自动化运行。
常见问题问答
问1:crontab配置后脚本没有执行,如何排查?
答:首先检查cron服务是否运行(sudo service cron status);其次查看cron系统日志(sudo grep CRON /var/log/syslog)确认任务是否被调度;最后检查脚本中的路径是否全部使用绝对路径,以及脚本是否有可执行权限。
问2:爬虫脚本在终端手动运行正常,但crontab执行时失败,为什么?
答:这是crontab最常见的坑。cron执行时的环境变量(如PATH、PYTHONPATH)与终端不同。解决方法是在crontab中显式设置PATH,或在shell脚本中source ~/.bashrc加载环境变量,同时所有路径必须使用绝对路径。
问3:如何让crontab任务的输出写入日志文件?
答:在crontab命令末尾添加重定向:>> /var/log/spider.log 2>&1,其中>>表示追加写入,2>&1表示将标准错误也重定向到标准输出。建议同时在Python脚本内部使用logging模块写入独立的日志文件,实现双重保障。
问4:爬虫被目标网站封IP了怎么办?
答:可采取以下措施:降低请求频率、轮换User-Agent、使用代理IP池、设置随机请求间隔。如果封禁严重,可考虑使用腾讯云提供的弹性IP或NAT网关更换出口IP。
问5:定时爬虫的数据如何备份?
答:数据备份可分为数据库备份和文件备份两个层面。数据库可使用mysqldump配合crontab定时导出;文件数据可通过rsync同步到腾讯云COS对象存储,或使用CVM的自动快照功能进行磁盘级备份。
问6:爬虫执行时间过长,与下一次cron任务重叠了怎么办?
答:使用flock命令实现任务互斥锁,确保同一时间只有一个脚本实例在运行。示例:0 2 * * * flock -n /tmp/spider.lock -c /path/to/run_spider.sh,其中-n参数表示如果锁已被占用则直接退出而不等待。




