一、为什么要用Scrapy?
在大数据时代,数据就是企业的核心资产。而获取外部数据最直接的方式之一,就是爬虫。目前主流的爬虫技术有很多:基于C++的Larbin、基于Java的Webmagic和Nutch、基于Golang的Pholcus,以及今天要重点介绍的------基于Python的Scrapy。
那么问题来了:为什么选Scrapy?
简单来说,Scrapy是一个为了爬取网站数据、提取结构性数据而编写的应用框架 ,可以应用在数据挖掘、信息处理或存储历史数据等一系列场景中。它底层基于Twisted异步网络框架,并发能力非常强悍。
举个直观的例子:假设目标网站每页有500个条目,Scrapy可以同时发起20个请求,每个请求耗时1秒,那么每秒就能拿到10000个条目 。如果要将这些数据存储到云端(每个存储操作耗时3秒),就需要同时处理30000个写入请求。传统的多线程方案需要创建30000个线程,系统根本无法承载,但Scrapy基于异步机制,只要硬件够,30000个并发轻松搞定。
这就是Scrapy最核心的竞争力------高吞吐量。
二、Scrapy能做什么?
有了这个利器,你可以做的事情太多了:
- 舆情监测:获取互联网数据,监测舆论动向,评估事态发展
- 热点发现:监测新闻的转发、评论增量趋势,发现潜在热点
- 金融分析:抓取大V调仓记录、上市公司年报,辅助投资决策
- 房产分析:获取交易、价格数据,分析市场走势
关于反爬虫:大部分情况下,反爬虫需求不会影响到网站的正常使用。爬虫可以伪装成正常用户,只是增加了一些代价而已。只要人能正常访问的网页,爬虫在具备同等资源下就一定能抓取。
三、Scrapy架构概览
Scrapy采用组件化设计,各模块间松耦合,便于扩展和定制。
┌─────────────────────────────────────────────────────┐
│ Scrapy Engine │
│ (引擎) │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │Scheduler │ │Downloader│ │ Spiders │ │
│ │ (调度器) │ │ (下载器) │ │ (爬虫) │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ ↑ ↑ ↑ │
│ ┌──────────────────────────────────────────┐ │
│ │ Item Pipeline (项目管道) │ │
│ │ (数据清洗、验证、存储) │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
核心组件说明:
| 组件 | 职责 |
|---|---|
| Engine(引擎) | 控制所有组件间的数据流,触发事件 |
| Scheduler(调度器) | 接收请求并排队,去重,按需返回给引擎 |
| Downloader(下载器) | 下载网页内容,返回响应 |
| Spiders(爬虫) | 用户编写的解析逻辑,提取数据和新的URL |
| Item Pipeline(管道) | 数据处理:清洗、验证、存数据库 |
| Middlewares(中间件) | 处理请求/响应的钩子,可扩展代理、UA等 |
数据流转过程:
- 引擎从Spider获取初始请求
- 引擎将请求交给调度器排队
- 调度器返回下一个请求给引擎
- 引擎通过下载器中间件把请求发给下载器
- 下载器完成下载,返回响应给引擎
- 引擎通过爬虫中间件把响应发给Spider处理
- Spider解析响应,产出Item和新的Request
- 引擎把Item交给Pipeline处理,把新Request交给调度器
- 重复上述过程,直到没有更多请求
四、快速上手
4.1 安装
bash
pip install scrapy
# 或使用国内镜像加速
pip install scrapy -i https://pypi.douban.com/simple
# 验证安装
scrapy version
Windows用户注意:可能需要先安装Microsoft Visual C++ Build Tools来解决依赖包编译问题。
4.2 创建项目
bash
scrapy startproject tutorial
生成的项目结构:
tutorial/
scrapy.cfg # 部署配置文件
tutorial/
__init__.py
items.py # 数据模型定义
pipelines.py # 数据处理管道
settings.py # 项目配置
spiders/ # 爬虫目录
__init__.py
4.3 编写第一个爬虫
在 spiders/quotes_spider.py 中创建爬虫类:
python
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes" # 爬虫唯一标识
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
page = response.url.split("/")[-2]
filename = f'quotes-{page}.html'
with open(filename, 'wb') as f:
f.write(response.body)
self.log(f'Saved file {filename}')
4.4 运行爬虫
bash
# 进入项目根目录
cd tutorial
# 执行爬虫
scrapy crawl quotes
执行后,你会看到生成了 quotes-1.html 和 quotes-2.html 两个文件。
五、数据提取:选择器
Scrapy提供了两种强大的数据提取方式:CSS选择器 和XPath。
5.1 交互式Shell调试
先用Shell测试选择器是否正确:
bash
scrapy shell 'http://quotes.toscrape.com/page/1/'
5.2 CSS选择器示例
python
# 提取标题
response.css('title::text').get() # 'Quotes to Scrape'
# 提取单个元素
response.css('span.text::text').get()
# 提取多个元素
response.css('div.tags a.tag::text').getall()
# 提取属性
response.css('li.next a::attr(href)').get()
注意 :
::text和::attr(name)是Scrapy扩展的伪元素,标准CSS不支持,但爬虫场景非常实用。
5.3 XPath选择器示例
python
response.xpath('//title/text()').get()
response.xpath('//div[@class="quote"]/span/text()').getall()
response.xpath('//a/@href').getall()
XPath功能更强大,可以基于内容进行选择,推荐深入学习。
六、完整示例:爬取名言网站
6.1 定义数据结构(items.py)
python
import scrapy
class QuoteItem(scrapy.Item):
text = scrapy.Field() # 名言内容
author = scrapy.Field() # 作者
tags = scrapy.Field() # 标签列表
6.2 编写爬虫(spiders/quotes_spider.py)
python
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = ['http://quotes.toscrape.com/page/1/']
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
# 翻页处理
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield scrapy.Request(
response.urljoin(next_page),
callback=self.parse
)
6.3 数据存储
方式一:直接导出(最简单)
bash
scrapy crawl quotes -o quotes.json
# 或导出为JSON Lines格式
scrapy crawl quotes -o quotes.jl
注意 :Scrapy默认会追加到已有文件而非覆盖,重复运行会生成损坏的JSON文件。建议使用
.jl格式(每行一个JSON对象)。
方式二:自定义Pipeline(更灵活)
在 pipelines.py 中实现数据清洗和存储:
python
import json
from itemadapter import ItemAdapter
class JsonWriterPipeline:
def open_spider(self, spider):
self.file = open('items.jsonl', 'w')
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(ItemAdapter(item).asdict()) + "\n"
self.file.write(line)
return item
然后在 settings.py 中启用管道:
python
ITEM_PIPELINES = {
'myproject.pipelines.JsonWriterPipeline': 300,
}
数值(300)决定了执行顺序,数字越小越先执行,通常定义在0-1000范围。
七、进阶技巧
7.1 爬虫参数
通过 -a 传递参数给爬虫:
bash
scrapy crawl quotes -a tag=humor
在爬虫中通过 self.tag 获取:
python
def start_requests(self):
url = 'http://quotes.toscrape.com/'
tag = getattr(self, 'tag', None)
if tag:
url = f'{url}tag/{tag}'
yield scrapy.Request(url, self.parse)
7.2 关键配置(settings.py)
python
# 禁用robots.txt(需谨慎)
ROBOTSTXT_OBEY = False
# 请求间隔(防封)
DOWNLOAD_DELAY = 2
# 伪装User-Agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
# 自动限速
AUTOTHROTTLE_ENABLED = True
# 并发控制
CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 8
7.3 遇到动态加载内容怎么办?
对于JavaScript渲染的页面,可以结合 Splash 或 Selenium。以Splash为例:
bash
pip install scrapy-splash
配置中间件使用Splash渲染:
python
SPLASH_URL = 'http://localhost:8050'
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
}
八、总结
Scrapy的核心优势在于:
- 高性能:基于Twisted异步框架,并发能力强
- 模块化:组件松耦合,便于扩展
- 开箱即用:内置选择器、中间件、导出等功能
- 社区活跃:遇到问题容易找到解决方案
记住:只要人能访问的网页,爬虫在同等资源下就一定能抓取。