Scrapy 是什么?为什么你需要学习它
Scrapy 是 Python 生态中最成熟、最强大的网页抓取框架之一。它像一个自动化程度极高的图书管理员,可以自动浏览网页、提取数据,并将这些数据整理成结构化格式。对于想要掌握数据采集技术的开发者来说,Python Scrapy 库是入门和进阶的必备工具。相比原生 requests + BeautifulSoup 组合,Scrapy 提供了完整的异步处理能力、内置的去重机制、自动的请求调度等特性,能将抓取效率提升 3-5 倍。本文将通过完整案例,带你从零开始掌握这个高效爬虫工具。
安装与基础配置
安装 Python Scrapy 库
在终端输入以下命令即可安装:
pip install scrapy
⚠️ 注意:Windows 用户建议使用 pip install -U Twisted[windows] 预处理,避免安装失败
创建项目结构
Scrapy 项目包含多个核心组件,通过如下命令创建项目:
scrapy startproject douban_movies
cd douban_movies
这会生成包含以下文件的项目框架:
douban_movies/
├── douban_movies/
│ ├── __init__.py
│ ├── items.py # 数据模型定义
│ ├── middlewares.py # 中间件配置
│ ├── pipelines.py # 数据处理流程
│ └── settings.py # 全局配置
└── scrapy.cfg # 项目配置
基本使用流程
创建第一个 Spider
在 spiders 目录新建 movie_spider.py:
import scrapy
class DoubanMovieSpider(scrapy.Spider):
name = "douban_movies" # Spider 名称
start_urls = ['https://movie.douban.com/top250'] # 起始URL
def parse(self, response):
"""解析豆瓣电影列表页面的核心方法"""
for movie in response.css('div.item'):
yield {
'title': movie.css('span.title::text').get(), # 提取电影标题
'rating': movie.css('span.rating_num::text').get(), # 提取评分
'year': movie.css('div.bd p::text')[1].re(r'(\d{4})'), # 提取年份
}
next_page = response.css('span.next a::attr(href)').get() # 获取下一页链接
if next_page:
yield response.follow(next_page, self.parse) # 递归抓取
✅ 关键点解析:
parse方法中的 yield 语句会生成字典格式的数据,response.follow能自动拼接相对路径
运行爬虫并导出数据
使用以下命令启动爬虫并保存为 JSON 文件:
scrapy crawl douban_movies -o movies.json
这将执行:
- 发起初始请求
- 解析 CSS 选择器定位的元素
- 递归抓取所有分页
- 将结果保存到 JSON 文件
数据解析进阶技巧
使用选择器定位元素
Scrapy 提供两种主要的选择器:
movies = response.css('div.item')
director = movies[0].xpath('.//div/p/text()').getall()[1]
| 选择器类型 | 语法示例 | 匹配结果 |
|---|---|---|
| CSS | .class #id | 类选择器、ID选择器 |
| XPath | //div[@class='x'] | 路径选择器、属性筛选 |
构建结构化数据模型
在 items.py 中定义数据结构:
import scrapy
class DoubanMoviesItem(scrapy.Item):
title = scrapy.Field() # 电影标题
rating = scrapy.Field() # 评分信息
year = scrapy.Field() # 上映年份
intro = scrapy.Field() # 简介内容
修改 Spider 文件使其使用定义的 Item:
from douban_movies.items import DoubanMoviesItem
class DoubanMovieSpider(scrapy.Spider):
# ...其他配置保持不变...
def parse(self, response):
item = DoubanMoviesItem()
item['title'] = response.css('span.title::text').get()
return item
高级功能实践
自定义请求头与中间件
修改 settings.py 配置请求头:
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
DOWNLOADER_MIDDLEWARES = {
'douban_movies.middlewares.DoubanMoviesSpiderMiddleware': 543, # 启用中间件
}
编写中间件处理请求头:
from scrapy import signals
class DoubanMoviesSpiderMiddleware:
def process_request(self, request, spider):
"""在请求前添加自定义Header"""
request.headers['Accept-Language'] = 'en-US,en;q=0.9,zh-TW;q=0.8,zh;q=0.7'
request.headers['Referer'] = 'https://movie.douban.com/'
数据持久化方案
Scrapy 支持多种数据导出格式:
scrapy crawl douban_movies -o movies.csv # 导出CSV
scrapy crawl douban_movies -o movies.xml # 导出XML
scrapy crawl douban_movies -o movies.json # 导出JSON
进阶存储到数据库的实现方式:
import sqlite3
class DoubanMoviesPipeline:
def open_spider(self, spider):
"""爬虫启动时创建数据库连接"""
self.conn = sqlite3.connect('movies.db')
self.cur = self.conn.cursor()
self.cur.execute('''
CREATE TABLE IF NOT EXISTS movies
(title TEXT, rating TEXT, year TEXT, intro TEXT)
''')
def close_spider(self, spider):
"""爬虫结束时关闭连接"""
self.conn.commit()
self.conn.close()
def process_item(self, item, spider):
"""将数据写入数据库"""
self.cur.execute('''
INSERT INTO movies (title, rating, year, intro)
VALUES (?, ?, ?, ?)
''', (
item['title'][0].strip(), # 去除标题首尾空格
item['rating'][0], # 评分数据
item['year'][0], # 年份数据
item['intro'][0] # 简介内容
))
return item
常见问题解决方案
处理动态加载内容
对于需要 JavaScript 渲染的页面,可以使用 Splash 中间件:
from scrapy_splash import SplashRequest
class MySpider(scrapy.Spider):
def start_requests(self):
yield SplashRequest(
url='https://example.com/ajax',
callback=self.parse,
args={'wait': 2}, # 等待2秒确保JS执行完成
endpoint='render.html'
)
识别与处理验证码
虽然 Scrapy 本身无法处理验证码,但可以配合第三方服务:
import requests
def handle_captcha(image_url):
"""将验证码发送到识别服务"""
response = requests.post(
'https://api.captcha-service.com/recognize',
files={'image': requests.get(image_url).content}
)
return response.json()['text']
实战案例:豆瓣电影Top250爬虫
完整代码示例
以下是一个完整的豆瓣电影爬取示例:
import scrapy
from douban_movies.items import DoubanMoviesItem
class DoubanTop250Spider(scrapy.Spider):
name = "douban_top250"
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
"""主解析方法"""
for movie in response.css('div.item'):
item = DoubanMoviesItem()
item['title'] = movie.css('span.title::text').getall()
item['rating'] = movie.css('span.rating_num::text').get()
item['year'] = movie.css('div.bd p::text')[1].re(r'(\d{4})')
item['intro'] = movie.css('p.quote span::text').get()
yield item
next_page = response.css('span.next a::attr(href)').get()
if next_page:
yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse)
数据处理流程
在 pipelines.py 添加数据清洗逻辑:
class DoubanMoviesPipeline:
def process_item(self, item, spider):
# 清洗标题数据
item['title'] = [t.strip() for t in item['title']]
# 处理中文/英文标题
if len(item['title']) > 1:
item['title'] = f"{item['title'][0]} / {item['title'][1]}"
else:
item['title'] = item['title'][0]
# 简介为空时设置默认值
if not item['intro']:
item['intro'] = '暂无简介'
return item
结语
通过本文的讲解,你应该已经掌握了 Python Scrapy 库的核心使用方法。从基础安装到实战案例,我们逐步构建了一个完整的电影数据爬虫系统。记住 Scrapy 的设计哲学:高内聚低耦合,每个组件都专注于特定功能。当你需要处理更复杂的场景时,可以:
- 使用
scrapy-splash处理 JS 渲染 - 配合
scrapy-redis实现分布式爬虫 - 利用
Item Loaders简化数据处理
建议通过 scrapy shell <url> 工具进行实时调试,这能显著提升开发效率。现在就动手实践本文的代码示例,在实际项目中体验 Python Scrapy 库的魅力吧!