爬虫--爬虫镜像化--docker部署scrapy 做爬虫开发的朋友一定都遇到过这样的场景在本地调试得好好的爬虫部署到服务器上就各种报错——Python版本不对、依赖库缺失、系统环境差异……一套环境配下来半天就没了。Docker的出现正好解决了这个问题。它把爬虫代码和运行环境一起打包成一个“镜像”实现一次构建到处运行。换机器部署只需要一条docker run命令不用再被环境配置折磨。本文将以抓取电影网站以豆瓣电影TOP250为例的Scrapy项目为基础带你从零开始把爬虫容器化并分享实践中踩过的坑和解决方案。前置准备开始之前确保你已经具备一个能正常运行的Scrapy爬虫项目机器上已安装Docker参考Docker官方文档准备好requirements.txt依赖清单实战示例豆瓣电影TOP250爬虫我们先用Scrapy创建一个完整的豆瓣电影爬虫作为示例项目。1. 创建Scrapy项目scrapy startproject movie_spidercdmovie_spider scrapy genspider douban movie.douban.com2. 编写爬虫代码编辑movie_spider/spiders/douban.pyimportscrapyfromscrapyimportRequestfrommovie_spider.itemsimportMovieItemclassDoubanSpider(scrapy.Spider):namedoubanallowed_domains[movie.douban.com]start_urls[https://movie.douban.com/top250]defparse(self,response):# 提取每一部电影的信息moviesresponse.css(div.item)formovieinmovies:itemMovieItem()item[rank]movie.css(em::text).get()item[title]movie.css(span.title::text).get()item[rating]movie.css(span.rating_num::text).get()item[quote]movie.css(span.inq::text).get()yielditem# 翻页继续爬取下一页next_pageresponse.css(span.next a::attr(href)).get()ifnext_page:yieldRequest(response.urljoin(next_page),callbackself.parse)3. 定义Item结构编辑movie_spider/items.pyimportscrapyclassMovieItem(scrapy.Item):rankscrapy.Field()titlescrapy.Field()ratingscrapy.Field()quotescrapy.Field()4. 配置设置可选编辑movie_spider/settings.py启用一些常用配置# 遵守robots协议豆瓣允许爬虫但建议设置延迟以示礼貌ROBOTSTXT_OBEYTrueDOWNLOAD_DELAY1.5# 设置User-AgentUSER_AGENTMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36# 日志级别LOG_LEVELINFO# 输出格式数据保存为JSON文件FEEDS{output/movies.json:{format:json,encoding:utf8,indent:2,},}5. 准备依赖文件创建requirements.txtscrapy2.11.0第一步编写DockerfileDockerfile是构建镜像的“说明书”。以下是针对本电影爬虫项目优化的Dockerfile# 使用轻量级Python镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 先复制依赖文件利用Docker层缓存提高构建效率 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制项目代码 COPY . . # 创建输出目录用于保存爬取结果 RUN mkdir -p /app/output # 容器启动时执行的命令爬取豆瓣TOP250 CMD [scrapy, crawl, douban, -o, output/movies.json]关键点说明电影爬虫不需要复杂的系统依赖不需要编译lxml所以直接用slim基础镜像即可依赖安装放在代码复制之前这样代码改动后重新构建时可以复用缓存层速度更快在容器内预创建输出目录便于挂载数据卷第二步构建镜像在项目根目录Dockerfile所在目录执行# 查看项目结构确认ls-la# 应该看到Dockerfile requirements.txt movie_spider/# 构建镜像dockerbuild-tdouban-spider:v1.-t给镜像打上标签方便管理。构建完成后用docker images可以查看生成的镜像。$ docker images | grep douban douban-spider v1 abc123def456 2 minutes ago 189MB第三步运行容器基础运行方式dockerrun--rmdouban-spider:v1--rm表示容器运行结束后自动删除避免残留无用容器。数据持久化保存爬取结果容器内产生的数据会随容器删除而丢失。使用数据卷Volume可以把结果保存到宿主机dockerrun--rm-v$(pwd)/output:/app/output douban-spider:v1这样爬虫保存到/app/output/movies.json的数据会出现在当前目录的output文件夹中。覆盖默认命令抓取其他内容如果需要覆盖默认命令例如只抓取评分最高的10部电影dockerrun--rmdouban-spider:v1 scrapy crawl douban-atop10-ooutput/top10.json需要在爬虫中增加对应的参数支持扩展def__init__(self,topNone,*args,**kwargs):super(DoubanSpider,self).__init__(*args,**kwargs)self.topint(top)iftopelseNone进阶配置1. 环境变量动态配置爬虫通过环境变量传递配置如数据源、开关等dockerrun--rm\-eOUTPUT_FORMATcsv\-eENABLE_PROXYtrue\-v$(pwd)/output:/app/output\douban-spider:v1在settings.py中读取环境变量importos# 动态配置输出格式FEEDS{foutput/movies.{os.getenv(OUTPUT_FORMAT,json)}:{format:os.getenv(OUTPUT_FORMAT,json),encoding:utf8,indent:2,},}2. 代理配置如果目标网站有反爬限制可以在Docker运行时挂载代理dockerrun--rm\-eHTTP_PROXYhttp://proxy.example.com:8080\-eHTTPS_PROXYhttp://proxy.example.com:8080\douban-spider:v13. 定时执行结合crontab或docker run配合调度工具如Apache Airflow实现定时抓取# 每天凌晨2点执行一次02* * *dockerrun--rm-v/data/output:/app/output douban-spider:v1实战踩坑记录坑1Scrapy的Robots.txt被屏蔽豆瓣等网站可能对爬虫做限制。解决方法方案A在settings.py中设置ROBOTSTXT_OBEY False方案B自定义User-Agent模拟真实浏览器见前文配置坑2中文乱码问题JSON输出中文显示为\uXXXX编码。解决方法在settings.py中设置FEED_EXPORT_ENCODING utf-8在Item导出时指定ensure_asciiFalse对于自定义导出器坑3容器内时区问题容器默认UTC时区爬取时间戳可能不正确。解决方案# 在Dockerfile中设置时区 RUN apt-get update apt-get install -y tzdata \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone或在运行时挂载宿主机时区dockerrun--rm-v/etc/localtime:/etc/localtime:ro douban-spider:v1坑4镜像体积过大保持镜像轻量化的策略使用python:*-slim或python:*-alpine基础镜像利用Docker层缓存依赖先复制清理apt缓存apt-get clean rm -rf /var/lib/apt/lists/*使用多阶段构建如需要编译的库多容器协作Scrapy Redis MongoDB真实生产环境往往需要多个服务配合。以分布式电影爬虫为例Redis做URL去重队列MongoDB存储电影数据。使用docker-compose一键启动整套环境version:3.8services:# Redis - URL队列和去重redis:image:redis:7-alpineports:-6379:6379volumes:-redis_data:/datarestart:unless-stopped# MongoDB - 存储电影数据mongodb:image:mongo:7environment:MONGO_INITDB_ROOT_USERNAME:adminMONGO_INITDB_ROOT_PASSWORD:secretMONGO_INITDB_DATABASE:moviesports:-27017:27017volumes:-mongo_data:/data/dbrestart:unless-stopped# Scrapy爬虫 - 支持多实例spider:build:.depends_on:-redis-mongodbenvironment:REDIS_HOST:redisREDIS_PORT:6379MONGO_HOST:mongodbMONGO_USER:adminMONGO_PASSWORD:secretvolumes:-./output:/app/output# 启动多个爬虫实例实现并发抓取# deploy:# replicas: 3volumes:redis_data:mongo_data:启动整个集群# 启动所有服务docker-composeup-d# 查看容器状态docker-composeps# 启动3个爬虫实例并发抓取docker-composeup-d--scalespider3# 查看爬虫日志docker-composelogs-fspider这样Redis做URL队列去重MongoDB存储电影详情多个爬虫容器共享任务一套分布式爬虫系统就搭建起来了。完整项目结构最终的项目文件结构movie_spider/ ├── Dockerfile ├── requirements.txt ├── docker-compose.yml ├── output/ # 运行后生成 │ └── movies.json └── movie_spider/ ├── __init__.py ├── items.py ├── middlewares.py ├── pipelines.py ├── settings.py └── spiders/ ├── __init__.py └── douban.py总结把Scrapy爬虫Docker化核心价值在于价值说明环境一致性本地跑通线上必然跑通告别“环境问题”快速部署新机器只需要Docker一条命令启动资源隔离不同爬虫项目互不干扰弹性伸缩配合docker-compose或K8s轻松横向扩展从单容器到分布式集群Docker让爬虫项目的部署和运维变得前所未有的简单。