欢迎光临
我们一直在努力

Scrapy 爬虫框架从入门到实战——高效采集不再是难题

前面用 requests + BeautifulSoup 写了那么多爬虫,已经能解决大部分需求了。但如果遇到需要大规模采集、数据量上万页的场景,就该上 Scrapy 了。

Scrapy 是 Python 最强大的爬虫框架,内置了多线程调度、请求去重、数据管道、自动限速等功能,让你写更少的代码,爬更多的数据。

一、安装与创建项目

pip install scrapy

# 创建爬虫项目
scrapy startproject myspider

# 目录结构
myspider/
├── scrapy.cfg
└── myspider/
├── __init__.py
├── items.py # 定义数据字段
├── middlewares.py # 中间件(代理、UA等)
├── pipelines.py # 数据管道(存数据库/文件)
├── settings.py # 配置文件
└── spiders/ # 爬虫代码目录
└── __init__.py

# 创建爬虫
cd myspider
scrapy genspider quotes quotes.toscrape.com

二、核心组件速览

URLs → 调度器(Scheduler) → 下载器(Downloader) → 爬虫(Spider) → 管道(Pipeline)
↑ ↓
去重过滤器(DupeFilter) 中间件(Middleware)

各组件作用:

组件职责
Spider 解析响应、提取数据、生成新请求
Scheduler 管理待爬取的请求队列
Downloader 下载网页内容
Pipeline 清洗、验证、存储数据
Middleware 处理请求/响应的钩子(UA切换、代理等)

三、实战:爬取名言网站

以 quotes.toscrape.com 为例,爬取名言、作者和标签。

1. 定义数据模型

# items.py
import scrapy

class QuoteItem(scrapy.Item):
text = scrapy.Field() # 名言内容
author = scrapy.Field() # 作者
tags = scrapy.Field() # 标签列表
author_url = scrapy.Field() # 作者详情页URL

2. 编写爬虫

# spiders/quotes.py
import scrapy
from myspider.items import QuoteItem

class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["https://quotes.toscrape.com"]

def parse(self, response):
"""解析列表页"""
# 提取当前页所有名言
for quote in response.css("div.quote"):
item = QuoteItem()
item["text"] = quote.css("span.text::text").get()
item["author"] = quote.css("small.author::text").get()
item["tags"] = quote.css("div.tags a.tag::text").getall()
# 提取作者详情页URL
item["author_url"] = quote.css("a::attr(href)").get()

yield item

# 翻页:提取下一页链接并继续爬取
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)

3. 数据存储——Pipeline

# pipelines.py
import json
from itemadapter import ItemAdapter

class JsonPipeline:
"""保存为JSON文件"""
def open_spider(self, spider):
self.file = open("quotes.json", "w", encoding="utf-8")
self.file.write("[\\n")
self.first = True

def close_spider(self, spider):
self.file.write("\\n]")
self.file.close()

def process_item(self, item, spider):
line = json.dumps(dict(item), ensure_ascii=False)
if not self.first:
self.file.write(",\\n")
self.first = False
self.file.write(" " + line)
return item

class MongoPipeline:
"""保存到MongoDB"""
def open_spider(self, spider):
self.client = pymongo.MongoClient("localhost", 27017)
self.db = self.client["quotes_db"]

def close_spider(self, spider):
self.client.close()

def process_item(self, item, spider):
self.db["quotes"].insert_one(dict(item))
return item

4. 启用 Pipeline

# settings.py
ITEM_PIPELINES = {
"myspider.pipelines.JsonPipeline": 300, # 数字越小优先级越高
# "myspider.pipelines.MongoPipeline": 400,
}

5. 运行

scrapy crawl quotes -o quotes.json # 内置方式,最简单
# 或者
scrapy crawl quotes # 走自定义Pipeline

-o quotes.json 是 Scrapy 内置的导出方式,一行命令搞定。

四、中间件——随机User-Agent

# middlewares.py
import random

class RandomUserAgentMiddleware:
"""随机切换 User-Agent"""
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0",
]

def process_request(self, request, spider):
request.headers["User-Agent"] = random.choice(self.user_agents)
return None # 返回 None 继续处理

# settings.py 中启用
DOWNLOADER_MIDDLEWARES = {
"myspider.middlewares.RandomUserAgentMiddleware": 543,
}

五、爬虫配置优化

# settings.py —— 常用配置项

# 遵守 robots.txt(开发时建议设为 False)
ROBOTSTXT_OBEY = False

# 下载延迟(秒),防止被封
DOWNLOAD_DELAY = 1.5 # 每两个请求间隔 1.5 秒

# 并发请求数
CONCURRENT_REQUESTS = 16 # 总并发
CONCURRENT_REQUESTS_PER_DOMAIN = 8 # 每个域名并发

# 请求超时
DOWNLOAD_TIMEOUT = 15

# 是否启用cookies
COOKIES_ENABLED = False

# 自动限流扩展(智能调节爬取速度)
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 5.0
AUTOTHROTTLE_MAX_DELAY = 60.0

# 重试配置
RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 403, 408]

# 日志级别
LOG_LEVEL = "INFO"

六、实战:爬取翻页详情页

很多网站是列表页 → 详情页的结构,两步完成:

import scrapy
from myspider.items import BookItem

class BooksSpider(scrapy.Spider):
name = "books"
start_urls = ["https://books.toscrape.com/"]

def parse(self, response):
"""第一步:提取列表页中的书籍链接"""
for book in response.css("article.product_pod"):
detail_url = book.css("h3 a::attr(href)").get()
yield response.follow(detail_url, callback=self.parse_book)

# 翻页
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)

def parse_book(self, response):
"""第二步:解析详情页"""
item = BookItem()
item["title"] = response.css("h1::text").get()
item["price"] = response.css("p.price_color::text").get()
item["description"] = response.css(
"#product_description ~ p::text"
).get()
# 提取分类
breadcrumb = response.css("ul.breadcrumb li a::text").getall()
item["category"] = breadcrumb[2] if len(breadcrumb) > 2 else ""
yield item

七、如何断点续爬

Scrapy 支持暂停和恢复:

# 启动时生成状态文件
scrapy crawl quotes -s JOBDIR=quotes_state

# 再次运行会自动恢复(Ctrl+C 中断后)
scrapy crawl quotes -s JOBDIR=quotes_state

适合大规模采集怕中断的场景。

八、Scrapy vs requests 怎么选?

场景推荐方案原因
爬几十页数据 requests + BS4 简单灵活,没必要上框架
爬几百页数据 Scrapy 多线程自动调度,速度快
爬上万页数据 Scrapy + 分布式 Scrapy-Redis 实现分布式
需要登录/复杂交互 Scrapy + Selenium 配合中间件处理动态内容

建议路线: requests 入门 → Scrapy 进阶 → Scrapy-Redis 分布式

总结

Scrapy 真正强的地方在于:你只管写解析逻辑,调度、并发、重试、限速、去重这些脏活累活框架全包了。对做数据采集来说,Scrapy 是值得投入时间学习的利器。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

赞(0)
未经允许不得转载:171主机测评 » Scrapy 爬虫框架从入门到实战——高效采集不再是难题
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址