Python爬虫框架Scrapy实战:从入门到分布式抓取
1. Scrapy框架概述与核心价值Scrapy是一个基于Python的高性能网络爬虫框架专为大规模数据抓取而设计。不同于简单的requestsBeautifulSoup组合Scrapy提供了完整的爬虫开发生命周期管理包括请求调度、数据提取、持久化存储等模块化组件。我在实际项目中多次使用Scrapy处理千万级数据抓取任务其异步处理机制能轻松实现单机日抓取百万页面。框架的核心优势在于其Batteries included设计理念内置CSS选择器和XPath解析器自动的请求去重与优先级队列完善的中间件扩展机制支持分布式爬取和增量抓取丰富的导出格式JSON/CSV/XML等注意Scrapy最新稳定版2.11要求Python 3.8环境与部分旧版扩展插件可能存在兼容性问题。2. 环境配置与项目初始化2.1 基础环境准备推荐使用conda创建独立Python环境避免包冲突conda create -n scrapy_env python3.10 conda activate scrapy_env pip install scrapy验证安装scrapy version # 应输出类似Scrapy 2.11.02.2 创建第一个爬虫项目执行项目生成命令scrapy startproject lagou_jobs cd lagou_jobs scrapy genspider lagou www.lagou.com生成的项目结构解析lagou_jobs/ ├── scrapy.cfg # 部署配置文件 └── lagou_jobs/ # 项目主目录 ├── __init__.py ├── items.py # 数据模型定义 ├── middlewares.py # 中间件配置 ├── pipelines.py # 数据处理管道 ├── settings.py # 爬虫配置 └── spiders/ # 爬虫代码目录 ├── __init__.py └── lagou.py # 生成的爬虫模板3. 核心组件深度解析3.1 数据模型定义items.py以拉勾网职位数据为例import scrapy class LagouJobItem(scrapy.Item): position scrapy.Field() # 职位名称 salary scrapy.Field() # 薪资范围 company scrapy.Field() # 公司名称 experience scrapy.Field() # 经验要求 education scrapy.Field() # 学历要求 skills scrapy.Field() # 技能标签 pub_date scrapy.Field() # 发布日期 job_url scrapy.Field() # 职位链接经验提示字段命名建议采用全小写下划线风格与后续数据库存储字段保持一致。3.2 爬虫逻辑实现spiders/lagou.pyimport scrapy from lagou_jobs.items import LagouJobItem class LagouSpider(scrapy.Spider): name lagou allowed_domains [www.lagou.com] start_urls [https://www.lagou.com/zhaopin/Python/] custom_settings { DOWNLOAD_DELAY: 2, # 爬取间隔(秒) CONCURRENT_REQUESTS: 8 # 并发请求数 } def parse(self, response): jobs response.css(.item__10RTO) for job in jobs: item LagouJobItem() item[position] job.css(h3::text).get().strip() item[salary] job.css(.money__3Lkgq::text).get() yield item # 翻页处理 next_page response.css(.next::attr(href)).get() if next_page: yield response.follow(next_page, self.parse)3.3 数据处理管道pipelines.py典型的数据清洗与存储实现import pymongo from itemadapter import ItemAdapter class LagouJobsPipeline: def __init__(self, mongo_uri, mongo_db): self.mongo_uri mongo_uri self.mongo_db mongo_db classmethod def from_crawler(cls, crawler): return cls( mongo_uricrawler.settings.get(MONGO_URI), mongo_dbcrawler.settings.get(MONGO_DATABASE) ) def open_spider(self, spider): self.client pymongo.MongoClient(self.mongo_uri) self.db self.client[self.mongo_db] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): # 薪资范围标准化处理 if salary in item: item[salary] self._normalize_salary(item[salary]) self.db[jobs].insert_one(ItemAdapter(item).asdict()) return item def _normalize_salary(self, salary_str): # 实现薪资字符串的标准化逻辑 pass4. 高级配置与优化技巧4.1 反爬策略应对方案在settings.py中配置DOWNLOADER_MIDDLEWARES { scrapy.downloadermiddlewares.useragent.UserAgentMiddleware: None, scrapy_user_agents.middlewares.RandomUserAgentMiddleware: 400, scrapy.downloadermiddlewares.retry.RetryMiddleware: 500, } RETRY_TIMES 3 RETRY_HTTP_CODES [500, 502, 503, 504, 522, 524, 408, 429] DOWNLOAD_DELAY 3 AUTOTHROTTLE_ENABLED True4.2 分布式爬虫实现使用scrapy-redis组件安装依赖pip install scrapy-redis修改爬虫继承类from scrapy_redis.spiders import RedisSpider class LagouRedisSpider(RedisSpider): name lagou_redis redis_key lagou:start_urls配置Redis连接SCHEDULER scrapy_redis.scheduler.Scheduler DUPEFILTER_CLASS scrapy_redis.dupefilter.RFPDupeFilter REDIS_URL redis://:passwordlocalhost:6379/05. 数据分析实战案例5.1 数据清洗与转换使用Pandas进行数据分析import pandas as pd from pymongo import MongoClient client MongoClient(mongodb://localhost:27017/) db client[lagou] df pd.DataFrame(list(db.jobs.find())) # 薪资分析 df[min_salary] df[salary].str.extract(r(\d)k-).astype(float) df[max_salary] df[salary].str.extract(r-(\d)k).astype(float) df[avg_salary] (df[min_salary] df[max_salary]) / 2 # 热门技能统计 skills_count pd.Series( sum([s.split(,) for s in df[skills].dropna()], []) ).value_counts()5.2 可视化展示使用Matplotlib生成图表import matplotlib.pyplot as plt plt.figure(figsize(12,6)) skills_count[:10].plot(kindbarh) plt.title(Top 10 Required Skills for Python Jobs) plt.xlabel(Count) plt.tight_layout() plt.savefig(skills_distribution.png)6. 常见问题排查手册问题现象可能原因解决方案返回403错误被目标网站封禁1. 增加DOWNLOAD_DELAY2. 使用代理中间件3. 更换User-Agent数据提取为空页面结构变更1. 更新CSS/XPath选择器2. 检查JavaScript渲染需求MongoDB连接失败认证配置错误1. 检查MONGO_URI格式2. 确认服务已启动内存占用过高未及时清理缓存1. 调整CONCURRENT_REQUESTS2. 启用AUTOTHROTTLE实战经验遇到页面解析问题时建议先用scrapy shell命令交互式调试scrapy shell https://www.lagou.com/zhaopin/Python/ # 然后在交互环境中测试选择器 response.css(h3::text).get()