前言
Scrapy 是一个基于 python 开发的爬虫框架,用于抓取 web 站点并从页面中提取结构化的数据,提供很多爬虫组件、基类,可扩展性强,通过其可以将爬虫中重复冗杂的逻辑步骤抽离出来,将通用步骤做成组件,至规则化通用爬虫,后续只需要添加或更改特定的逻辑,将代码编写简洁化,也提升了后期的可维护性。
Scrapy 文档:https://doc.scrapy.org/
Scrapy 框架的架构组成
Scrapy 框架的架构主要由以下五部分组成:
- 引擎(Engine):整个架构的核心,处理整个系统的数据流和事件,负责数据的流转和逻辑的处理,类似于框架的中央处理器控制整个流程
- 调度器(Scheduler):接收 Engine 发送的 Request 并将其加入队列中,同时也可以将 Request 发回给 Engine 供 Downloader 执行,负责维护 Request 的调度逻辑,通过它来决定下一个抓取的网址是什么,同时去除重复的网址
- 下载器(Downloader):用于高速地下载网络上的资源,Engine 向 Downloader 发送 Request,其得到 Response 再发送给 Engine 处理,建立在高效的异步模型 twisted 上
- 爬虫(Spider):定义了站点的爬取逻辑和页面的解析规则,负责响应并生成 Item 和新的 Request 并发送给 Engine 进行处理,可从网页中提取所需的信息
- 项目管道(Item Pipeline):负责处理 Spider 从页面中抽取的 Item,进行数据清洗、验证和存储等工作
整体架构如下图所示:
- 下载器中间件(Downloader Middlewares):位于 Engine 与 Downloader 之间的 Hook 框架,负责实现 Engine 与 Downloader 之间的请求和响应的处理过程
- 爬虫中间件(Spider Middlewares):位于 Spider 与 Downloader 之间的 Hook 框架,负责实现 Spider 与 Downloader 之间的 Item、请求和响应的处理过程
- Item:保存爬取到的数据的容器
Hook:https://blog.csdn.net/Yy_Rose/article/details/124216720
Scrapy 框架的数据流过程
Scrapy 框架中,由 Engine 负责了整个数据流的分配和处理,数据流主要包括 Item、Request、Response,如上图所示,基本数据流如下:
- 启动爬虫时,Engine 根据要爬取的目标站点找到处理该站点的 Spider,Spider 会生成最初需要爬取的页面对应的一个或多个 Request,然后发送给 Engine
- Engine 从 Spider 中获取这些 Request,然后把它们交给 Scheduler 等待被调度
- Engine 向 Scheduler 索取下一个要处理的 Request,这时候 Scheduler 根据其调度逻辑选择合适的 Request 发送给 Engine
- Engine 将 Scheduler 发送的 Request 转发给 Downloader 进行下载执行,将 Request 发送给 Downloader 的过程会经由许多定义好的 Dwonloader Middlewares 处理
- Downloader 将 Request 发送给目标服务器,得到对应的 Response,然后将其返回给 Engine,将 Response 返回给 Engine 的过程同样会经由许多定义好的 Downloader Middlewares 处理
- Engine 从 Downloader 处接收到的 Response 里包含了爬取的目标站点的内容,Engine 会将此 Response 发送给对应的 Spider 进行处理,将 Response 发送给 Spider 的过程中会经由定义好的 Spider Middlewares 处理
- Spider 处理 Response,解析 Response 的内容,这时候 Spider 会产生一个或多个爬取结果 Item 或者后续要爬取的目标页面对应的一个或多个 Request,然后再将这些 Item 或 Request 发送给 Engine 进行处理,将 Item 或 Request 发送给 Engine 的过程经由定义好的 Spider Middlewares 处理
- Engine 将 Spider 发回的一个或多个 Item 转发给定义好的 Item Pipelines 进行数据处理或存储的一系列操作,将 Spider 发回的一个或多个 Request 转发给 Scheduler 等待下一次被调度
重复 2 至 8 过程,直至 Scheduler 中没有更多的 Request,这时候 Engine 会关闭 Spider,整个爬取过程结束。
以上内容来自:https://cuiqingcai.com/17777.html,丛书
Scrapy 爬取 NBA 中文网球员季后赛数据
创建项目(scrapy startproject 项目名称):
scrapy startproject nbainfo
切换到项目文件夹,创建 spider(scrapy genspider spider名称 网站域名):
scrapy genspider nba china.nba.cn
scrapy 框架创建完成:
nbainfo # 项目文件夹
spiders # spider文件夹
__init__.py
nba.py # spider 名称,网站链接的配置,抓取逻辑,解析逻辑的实现
__init__.py
items.py # 定义爬取的数据结构,创建 Item
middlewares.py # 定义爬取时的中间件,设置请求头,设置代理,设置 cookie,处理重定向等
pipelines.py # 定义数据管道,存储数据等
settings.py # 配置文件
scrapy.cfg # Scrapy 部署时的配置文件
球员数据为 Ajax 动态加载:
items.py → 创建 Item
from scrapy import Item, Field
class NbainfoItem(Item):
rank = Field()
player = Field()
team = Field()
games = Field()
starter = Field()
average_score = Field()
average_backboard = Field()
average_assists = Field()
playing_time = Field()
efficiency = Field()
two_rates = Field()
three_rates = Field()
penalty_shot_rates = Field()
attack = Field()
defense = Field()
average_snatch = Field()
average_capping = Field()
error = Field()
break_the_rules = Field()
nba.py → 解析 Response
from scrapy import Spider, Request
from nbainfo.items import NbainfoItem
class NbaSpider(Spider):
name = 'nba'
allowed_domains = ['china.nba.cn']
start_urls = 'https://china.nba.cn'
def start_requests(self):
url = f'{self.start_urls}/statistics/'
yield Request(url, callback=self.parse_detail)
def parse_detail(self, response):
infos = response.xpath('//div[@class="nba-stat-table__overflow"]//tr')
for info in infos:
item = NbainfoItem()
item['rank'] = info.xpath('//td/text()')
item['player'] = info.xpath('//td[@class="left player"]//span[2]/text()')
item['team'] = info.xpath(
'//a[@class="anchor-unstyled ng-binding ng-isolate-scope"]/text()')
item['games'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][1]/text()')
item['starter'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][2]/text()')
item['average_score'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][3]/text()')
item['average_backboard'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][4]/text()')
item['average_assists'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][5]/text()')
item['playing_time'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][6]/text()')
item['efficiency'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][7]/text()')
item['two_rates'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][8]/text()')
item['three_rates'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][9]/text()')
item['penalty_shot_rates'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][10]/text()')
item['attack'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][11]/text()')
item['defense'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][12]/text()')
item['average_snatch'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][13]/text()')
item['average_capping'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][14]/text()')
item['error'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][15]/text()')
item['break_the_rules'] = info.xpath(
'//td[@class="nobr center bold ng-binding"][16]/text()')
yield item
Scrapyd
Scrapyd 提供一些列 HTTP 接口帮助部署、启动、停止和删除爬虫程序,可以对 Scrapy 爬虫任务进行有效便捷的管理。
安装
pip3 install scrapyd
以下结果即为启动成功:
由上图可知,Scrapyd 启动在 6800 端口,网址链接为 http://127.0.0.1:6800/,访问服务器的该端口,就能看到 Web UI 页面,以下即为成功:
Scrapyd 的相关接口方法可参考官方文档:https://python-scrapyd-api.readthedocs.io/en/latest/
总结
以上是对 scrapy 框架的学习总结,以及项目实践,代码实现部分未完待续......
参考资料:
https://blog.csdn.net/ck784101777/article/details/104468780