爬虫框架Scrapy从入门到实战
一、Scrapy框架概述
1.1 什么是Scrapy
Scrapy是一个基于Twisted的异步网络爬虫框架,具有以下特性:
-
内置数据提取器(Selector)
-
完善的中间件扩展体系
-
自动的请求调度机制
-
支持多种数据存储方式
1.2 Scrapy vs Requests
特性 | Scrapy | Requests+BS4 |
---|---|---|
并发能力 | 异步高性能 | 同步单线程 |
项目结构 | 标准化目录 | 自由脚本结构 |
扩展性 | 中间件系统 | 需手动实现 |
适用场景 | 大型爬虫项目 | 小规模数据采集 |
二、环境搭建
2.1 安装Scrapy
pip install scrapy
# 验证安装
scrapy version # 应输出版本号如2.11.0
2.2 创建项目
scrapy startproject book_scraper
cd book_scraper
scrapy genspider book_spider books.toscrape.com
生成的项目结构:
book_scraper/ ├── scrapy.cfg └── book_scraper/ ├── __init__.py ├── items.py # 数据容器定义 ├── middlewares.py # 中间件配置 ├── pipelines.py # 数据管道 ├── settings.py # 全局配置 └── spiders/ # 爬虫目录 └── book_spider.py
三、核心组件详解
3.1 Spider类(爬虫核心)
python
复制
import scrapy
class BookSpider(scrapy.Spider):
name = "book_spider" # 爬虫唯一标识
allowed_domains = ["books.toscrape.com"]
start_urls = ["http://books.toscrape.com/"]
def parse(self, response):
# 解析逻辑
passimport scrapy
class BookSpider(scrapy.Spider):
name = "book_spider" # 爬虫唯一标识
allowed_domains = ["books.toscrape.com"]
start_urls = ["http://books.toscrape.com/"]
def parse(self, response):
# 解析逻辑
pass
3.2 Item类(数据结构)
# items.py
import scrapy
class BookItem(scrapy.Item):
title = scrapy.Field()
price = scrapy.Field()
rating = scrapy.Field()
stock = scrapy.Field()
3.3 Pipeline(数据处理管道)
# pipelines.py
class BookPipeline:
def process_item(self, item, spider):
# 数据清洗/存储逻辑
if item['price']:
item['price'] = float(item['price'].replace('£', ''))
return item
3.4 Middleware(中间件系统)
# middlewares.py
class RandomDelayMiddleware:
def process_request(self, request, spider):
# 设置随机请求延迟(0.5-1.5秒)
delay = random.uniform(0.5, 1.5)
request.meta['download_timeout'] = delay
四、完整爬虫实战
4.1 编写爬虫逻辑
# spiders/book_spider.py
class BookSpider(scrapy.Spider):
name = 'book_spider'
def start_requests(self):
urls = [f"http://books.toscrape.com/catalogue/page-{i}.html"
for i in range(1,51)]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
books = response.css('article.product_pod')
for book in books:
item = BookItem()
item['title'] = book.css('h3 a::attr(title)').get()
item['price'] = book.css('p.price_color::text').get()
item['rating'] = book.css('p.star-rating::attr(class)').get().split()[-1]
yield item
# 自动翻页(已由start_requests实现)
4.2 配置settings.py
# 启用Pipeline
ITEM_PIPELINES = {
'book_scraper.pipelines.BookPipeline': 300,
}
# 遵守robots协议
ROBOTSTXT_OBEY = True
# 并发控制
CONCURRENT_REQUESTS = 16
DOWNLOAD_DELAY = 0.5
4.3 运行爬虫
scrapy crawl book_spider -o books.csv
# 支持导出格式:json, csv, xml等
五、高级功能扩展
5.1 动态网页处理(Selenium中间件)
# middlewares.py
from selenium import webdriver
class SeleniumMiddleware:
def process_request(self, request, spider):
if request.meta.get('selenium'):
driver = webdriver.Chrome()
driver.get(request.url)
html = driver.page_source
driver.quit()
return HtmlResponse(url=request.url, body=html, encoding='utf-8')
5.2 分布式爬虫(Redis集成)
# settings.py
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
REDIS_URL = 'redis://localhost:6379'
5.3 自动限速算法
# settings.py
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 5.0
AUTOTHROTTLE_MAX_DELAY = 60.0
六、调试技巧
6.1 Scrapy Shell
scrapy shell "http://books.toscrape.com"
>>> response.css('title::text').get()
'All products | Books to Scrape - Sandbox'
6.2 日志分级
scrapy crawl book_spider -L INFO # DEBUG/INFO/WARNING/ERROR
6.3 中间件断点调试
# 在middleware中插入调试代码
import pdb; pdb.set_trace()
七、最佳实践
-
遵守robots.txt:设置
ROBOTSTXT_OBEY = True
-
设置User-Agent:使用随机User-Agent中间件
-
异常处理:覆盖
spider_error
回调 -
数据去重:使用
scrapy-deltafetch
扩展 -
监控报警:集成ScrapydWeb可视化面板
八、常见问题解答
Q1:如何处理登录验证?
# 在Spider中实现登录
def start_requests(self):
return [scrapy.FormRequest(
'https://example.com/login',
formdata={'user': 'admin', 'pass': 'secret'},
callback=self.after_login
)]
Q2:如何避免被封IP?
-
使用代理中间件
-
设置合理的
DOWNLOAD_DELAY
-
使用
scrapy-rotating-proxies
扩展
Q3:如何抓取JavaScript渲染的页面?
-
使用Selenium中间件
-
集成Splash服务
-
使用
scrapy-playwright
本文技术要点:
-
Scrapy框架的组件化设计
-
可扩展的中间件系统
-
生产级爬虫的最佳实践
-
分布式爬虫架构基础
-
常见反爬应对策略
下一步学习建议:
-
研究Scrapy源码理解运行机制
-
学习Scrapy-Redis实现分布式
-
实践反反爬技术(验证码识别、指纹伪装)