您的位置:首页 >Python爬虫实战:Scrapy框架项目详解
发布于2025-07-14 阅读(0)
扫一扫,手机访问
Scrapy框架实现网络爬虫的核心步骤如下:1. 安装Scrapy并创建项目,使用scrapy startproject命令搭建项目结构;2. 在spiders目录编写爬虫类,定义请求发起、响应解析及数据提取逻辑;3. 通过items.py定义结构化数据模型;4. 在pipelines.py中构建数据处理流程,包括清洗、验证和存储;5. 配置settings.py参数优化爬取效率,如设置USER_AGENT、DOWNLOAD_DELAY、CONCURRENT_REQUESTS等;6. 运行爬虫命令scrapy crawl执行抓取任务。各组件协同工作:引擎控制数据流,调度器管理请求队列,下载器执行网络请求,蜘蛛解析数据,管道处理持久化,中间件拦截请求/响应,从而实现高效、可扩展的数据抓取流程。

Python实现网络爬虫,尤其是面对规模化、结构化的数据抓取需求时,Scrapy框架无疑是一个非常强大的选择。它不仅仅是一个库,更像是一个完整的爬虫开发骨架,帮你把复杂的网络请求、数据解析、存储以及并发控制等一系列流程都安排得明明白白。用它来抓取数据,你会发现效率和可维护性都有了质的飞跃。

要用Scrapy实现网络爬虫,我们通常会从创建一个Scrapy项目开始。这就像是搭好了房子的框架,然后我们再往里面填充具体的房间(蜘蛛)、家具(数据模型)和水电管道(数据处理流程)。
首先,你得确保安装了Scrapy:pip install scrapy。

接着,创建一个新的Scrapy项目。比如,我们要爬取一个图书网站:
scrapy startproject book_crawler
这个命令会生成一个项目目录结构,里面包含了spiders、items、pipelines、settings等关键文件。

spiders目录:这是你编写爬虫逻辑的地方。每个爬虫(Spider)都是一个Python类,定义了如何发起请求、如何解析响应、以及如何从页面中提取数据。items.py:定义了你想要从网页中抓取的数据结构。它有点像数据库中的表结构,帮你规范化数据。pipelines.py:定义了数据处理管道。当数据(Item)从爬虫中被提取出来后,会依次经过这里定义的处理函数,比如清洗数据、验证数据、存储到数据库或文件。settings.py:项目的全局配置,比如请求延迟、并发数、用户代理、以及开启哪些中间件和管道等。middlewares.py:定义了下载器中间件和蜘蛛中间件。它们可以拦截请求和响应,做一些预处理或后处理,比如设置代理、处理Cookie、或者修改请求头。一个简单的爬虫(book_crawler/spiders/books.py)可能长这样:
import scrapy
from book_crawler.items import BookItem # 假设你定义了BookItem
class BooksSpider(scrapy.Spider):
name = 'books' # 爬虫的唯一标识符
allowed_domains = ['books.toscrape.com'] # 限制爬取范围,防止爬到不相干的网站
start_urls = ['http://books.toscrape.com/'] # 爬虫的起始URL
def parse(self, response):
# 这里的response对象包含了网页的内容,我们可以用CSS选择器或XPath来提取数据
# 比如,找到所有图书的链接
for book_link in response.css('h3 a::attr(href)').getall():
yield response.follow(book_link, callback=self.parse_book_detail)
# 尝试找到下一页的链接并继续爬取
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
def parse_book_detail(self, response):
# 解析图书详情页的数据
item = BookItem()
item['title'] = response.css('h1::text').get()
item['price'] = response.css('.price_color::text').get()
item['stock'] = response.css('.instock.availability::text').re_first(r'\((\d+) available\)')
# 更多字段...
yield item # 将提取到的数据交给Item Pipeline处理在book_crawler/items.py中定义BookItem:
import scrapy
class BookItem(scrapy.Item):
title = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
# 可以根据需要添加更多字段,比如描述、作者、封面图片URL等运行这个爬虫:
scrapy crawl books
Scrapy就会按照你定义的逻辑开始抓取数据,并将提取到的BookItem实例传递给管道。
理解Scrapy的内部机制,就像是了解一个大型工厂里各个部门的职责和协作方式。Scrapy的核心设计理念是异步和事件驱动,这让它在处理大量网络请求时表现得游刃有余。它主要由几个核心组件构成,它们各司其职,又紧密配合:
Scrapy引擎(Engine):这是整个框架的“大脑”,负责控制所有组件之间的数据流和事件触发。它就像一个总调度员,告诉调度器什么时候该给下载器发请求,下载器拿到响应后又该交给哪个蜘蛛处理,以及蜘蛛解析出数据后又该送往哪里。它协调着整个爬取过程的生命周期。
调度器(Scheduler):一个请求队列和请求去重器。它接收来自引擎的请求,并把它们排队等待下载。同时,它会确保同一个URL不会被重复下载(除非你明确配置)。你可以把它想象成一个快递分拣中心,管理着所有待发送的包裹(请求)。
下载器(Downloader):负责执行网络请求,获取网页内容。它接收调度器发来的请求,然后真正地去互联网上“下载”页面数据,并将原始的HTTP响应返回给引擎。这是爬虫与外部世界直接交互的唯一端口。
蜘蛛(Spiders):我们编写爬虫逻辑的地方。蜘蛛接收引擎传来的响应,从中解析出需要的数据(Items)以及新的待抓取URL(Requests)。它们是爬虫的“眼睛”和“手”,负责识别和提取信息。
项目管道(Item Pipelines):数据处理和持久化的环节。当蜘蛛解析出数据(Item)后,这些Item会被送入管道。管道可以对数据进行清洗、验证、去重,最终将其存储到数据库、文件或其他存储介质中。比如,你可以有一个管道负责把价格字符串转换成浮点数,另一个管道负责把数据存入MySQL。
下载器中间件(Downloader Middlewares):位于引擎和下载器之间的一层。它们可以拦截和处理所有通过下载器的请求和响应。这使得我们可以在请求发送前修改请求(如添加User-Agent、代理),或者在响应到达蜘蛛前处理响应(如解压、处理Cookie)。这是实现反爬策略和扩展下载功能的重要工具。
蜘蛛中间件(Spider Middlewares):位于引擎和蜘蛛之间。它们可以处理蜘蛛的输入(响应)和输出(Items和Requests)。例如,你可以在这里处理蜘蛛抛出的异常,或者对蜘蛛产生的Item和Request进行过滤。
这些组件就像齿轮一样,环环相扣。引擎是驱动力,它从调度器获取请求,交给下载器执行;下载器拿到响应后,再传回给引擎,引擎又交给对应的蜘蛛处理;蜘蛛解析出数据和新的请求,数据送往管道,请求再回到调度器,如此循环,直至所有任务完成。这种模块化的设计,让Scrapy的扩展性和灵活性都非常出色。
在Scrapy中,Item和Pipeline是数据流处理的两个核心概念,它们共同确保了从网页中提取的数据能够被有效地结构化、处理和存储。
Item的定义与使用:
Item是Scrapy中用来定义你希望抓取的数据结构的一种方式。它本质上是一个类似于字典的容器,但比普通的字典更强大,因为它允许你预先定义好你期望的字段(Field),这有助于保持数据的一致性和可读性。
定义Item非常直观,你只需要在项目的items.py文件中创建一个继承自scrapy.Item的类,并定义你的字段,每个字段都是一个scrapy.Field()实例。
# book_crawler/items.py
import scrapy
class BookItem(scrapy.Item):
# 定义图书的各个属性
title = scrapy.Field() # 书名
price = scrapy.Field() # 价格
stock = scrapy.Field() # 库存
author = scrapy.Field() # 作者
category = scrapy.Field() # 分类
description = scrapy.Field() # 描述
image_urls = scrapy.Field() # 封面图片URL列表
images = scrapy.Field() # 图片下载后的本地路径(通常由ImagesPipeline处理)在蜘蛛(Spider)中,当你从网页中解析出数据时,你会创建一个Item实例,并将提取到的数据赋值给它的字段,然后通过yield语句将这个Item传递出去:
# book_crawler/spiders/books.py (部分代码)
from book_crawler.items import BookItem
class BooksSpider(scrapy.Spider):
# ... (省略其他代码)
def parse_book_detail(self, response):
item = BookItem()
item['title'] = response.css('h1::text').get()
item['price'] = response.css('.price_color::text').get().replace('£', '') # 简单清洗
item['stock'] = response.css('.instock.availability::text').re_first(r'\((\d+) available\)')
# 假设作者在某个特定的p标签里
item['author'] = response.xpath('//table[@class="table table-striped"]/tr[2]/td/a/text()').get()
# 假设描述在某个p标签里
item['description'] = response.xpath('//div[@id="product_description"]/following-sibling::p/text()').get()
item['image_urls'] = [response.urljoin(response.css('#product_gallery img::attr(src)').get())] # 确保是绝对URL
yield item # 将这个结构化的数据发送到Item PipelinePipeline的定义与使用:
Item Pipeline是一系列按照特定顺序执行的处理组件。当一个Item被蜘蛛yield出来后,它会依次经过所有激活的Pipeline。每个Pipeline都可以对Item进行处理,比如:
FilesPipeline和ImagesPipeline,可以自动下载Item中指定的图片或文件。定义Pipeline,你需要在pipelines.py中创建一个类,并实现process_item方法:
# book_crawler/pipelines.py
import json
import pymysql # 假设你要存入MySQL
class BookCrawlerPipeline:
def process_item(self, item, spider):
# 简单的清洗,比如把价格字符串转为浮点数
if item.get('price'):
try:
item['price'] = float(item['price'])
except ValueError:
item['price'] = None # 或者记录错误
# 可以在这里做其他处理,比如验证字段是否存在
if not item.get('title'):
spider.logger.warning(f"Item missing title: {item}")
raise DropItem("Missing title") # 丢弃没有标题的Item
return item # 必须返回item,否则它不会传递给下一个Pipeline或被丢弃
class JsonWriterPipeline:
def open_spider(self, spider):
# 爬虫启动时打开文件
self.file = open('books.jsonl', 'a', encoding='utf-8')
def close_spider(self, spider):
# 爬虫关闭时关闭文件
self.file.close()
def process_item(self, item, spider):
# 将Item转换为JSON字符串并写入文件
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item
class MySQLPipeline:
def __init__(self):
self.conn = pymysql.connect(host='localhost', user='root', password='your_password', db='book_db', charset='utf8mb4')
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
sql = """
INSERT INTO books (title, price, stock, author, description, category)
VALUES (%s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
price=VALUES(price), stock=VALUES(stock), description=VALUES(description)
"""
try:
self.cursor.execute(sql, (
item.get('title'),
item.get('price'),
item.get('stock'),
item.get('author'),
item.get('description'),
item.get('category')
))
self.conn.commit()
except pymysql.Error as e:
self.conn.rollback()
spider.logger.error(f"Error inserting item into DB: {e} - {item.get('title')}")
return item
def close_spider(self, spider):
self.cursor.close()
self.conn.close()最后,你需要在settings.py中启用并配置你的Pipeline。ITEM_PIPELINES是一个字典,键是Pipeline类的路径,值是它们的优先级(0-1000,数字越小优先级越高,越早执行):
# book_crawler/settings.py
ITEM_PIPELINES = {
'book_crawler.pipelines.BookCrawlerPipeline': 300, # 先进行通用清洗
'book_crawler.pipelines.JsonWriterPipeline': 800, # 再写入JSON文件
'book_crawler.pipelines.MySQLPipeline': 900, # 最后存入数据库
}通过这种方式,你可以将数据提取和数据处理的逻辑清晰地分离,使得代码更加模块化和易于维护。
Scrapy的settings.py文件是整个项目的配置中心,它提供了大量参数来控制爬虫的行为,从最基本的请求头到复杂的并发策略。合理配置这些参数,不仅能让你的爬虫更“文明”,也能显著提升爬取效率。
这里列举一些常用且对爬取效率影响较大的参数:
USER_AGENT:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'ROBOTSTXT_OBEY:
robots.txt协议。True。遵守协议是爬虫的基本道德,也能避免不必要的法律风险和服务器压力。如果设置为False,可能会导致你的IP被封禁或被网站管理员投诉。ROBOTSTXT_OBEY = TrueDOWNLOAD_DELAY:
DOWNLOAD_DELAY = 1.5CONCURRENT_REQUESTS:
CONCURRENT_REQUESTS = 32CONCURRENT_REQUESTS_PER_DOMAIN:
CONCURRENT_REQUESTS更精细的控制。即使你的总并发很高,你也可以限制对单个网站的并发,避免对某个特定网站造成过大负担。通常设置为CONCURRENT_REQUESTS的较小值,或者根据目标网站的特点设置。CONCURRENT_REQUESTS_PER_DOMAIN = 8CONCURRENT_REQUESTS_PER_IP:
CONCURRENT_REQUESTS_PER_IP = 0 (0表示禁用此限制,通常与DOWNLOAD_DELAY和CONCURRENT_REQUESTS_PER_DOMAIN配合使用)AUTOTHROTTLE_ENABLED:
DOWNLOAD_DELAY,以尽可能快地爬取,同时又不会给服务器带来过大压力。它会尝试在不被网站封禁的前提下,最大化爬取效率。AUTOTHROTTLE_ENABLED = TrueAUTOTHROTTLE_START_DELAY:初始延迟(秒),默认为5。AUTOTHROTTLE_MAX_DELAY:最大延迟(秒),默认为60。AUTOTHROTTLE_TARGET_CONCURRENCY:期望的并发数。Scrapy会尝试调整延迟,使并发请求数接近这个目标。HTTPCACHE_ENABLED:
HTTPCACHE_ENABLED = True (调试时)DEPTH_LIMIT:
DEPTH_LIMIT = 5 (只爬取5层深度)RETRY_ENABLED 和 RETRY_TIMES:
RETRY_ENABLED = True, RETRY_TIMES = 3优化Scrapy爬取效率,本质上是在“速度”与“礼貌”之间找到一个平衡点。你需要根据目标网站的规模、服务器性能、以及你的数据需求来反复测试和调整这些
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9