一、简介
1 Scrapy的定义
Scrapy 是一个用 Python 编写的开源网络爬虫框架。
2 Scrapy的特点
- 基于
Twisted
异步网络库,实现高效地异步操作。 - 支持自动抓取数据,自动处理数据。
- 支持分布式爬取和多线程、多进程的高效爬取。
- 可以自定义条件进行
URL
过滤。 - 支持动态网站的数据爬取。
3 Scrapy的优点
- 组件化设计,方便维护和扩展。
- 可以通过插件的形式和其他库结合使用。
- 自带命令行工具,方便调试和操作。
- 支持多种数据存储方式。
- 代码简洁,易于上手。
二、Scrapy的模块结构与工作流程
1 Scrapy的模块结构
- Scrapy Engine(引擎):整个框架的核心,负责调度和控制整个爬虫流程。
- Scheduler(调度器):调度器,负责安排爬取任务的先后顺序。
- Downloader(下载器):下载器,负责下载网页内容和其他资源文件。
- Spiders(爬虫):爬虫,负责页面解析和数据项的抽取。
- Item Pipeline(数据管道):数据管道,负责数据的处理和存储。
- Middlewares(中间件):中间件,负责整个爬虫流程中的各种处理逻辑。
2 Scrapy的工作流程
- 爬虫从
start_urls
开始爬取,并将URL
添加到调度器中。
```python
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['http://example.com']
def parse(self, response):
# 解析响应
pass
2. **调度器从等待队列中取出一个爬取请求,并把它传递给下载器。**
```python
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['http://example.com']
def parse(self, response):
url = 'http://example.com/page1' # 爬取的网址
yield scrapy.Request(url, callback=self.parse_page1) # 构造请求,添加至队列中
def parse_page1(self, response):
# 解析响应
pass
下载器下载请求中的网页,并将响应交给爬虫。
class MySpider(scrapy.Spider): name = 'example' start_urls = ['http://example.com'] def parse(self, response): url = 'http://example.com/page1' # 爬取的网址 yield scrapy.Request(url, callback=self.parse_page1) # 构造请求,添加至队列中 def parse_page1(self, response): html = response.body # 获取响应的 HTML 代码 pass
爬虫解析响应内容,筛选数据并生成 Item 对象。
class MySpider(scrapy.Spider): name = 'example' start_urls = ['http://example.com'] def parse(self, response): url = 'http://example.com/page1' # 爬取的网址 yield scrapy.Request(url, callback=self.parse_page1) # 构造请求,添加至队列中 def parse_page1(self, response): item = { } item['title'] = response.css("title::text").get() # 从响应数据中获取文章标题 yield item # 返回项目
Item Pipeline 处理 Item 对象,将数据存储到指定的地方。
```python
class MyPipeline(object):def process_item(self, item, spider):
# 处理 item return item
settings.py
ITEM_PIPELINES = {'myproject.pipelines.MyPipeline': 300}
6. **爬虫结束后,Scrapy Engine 关闭。**
## 3 Scrapy的数据流
Scrapy 的数据流可以用以下 4 个步骤来描述:
1. **开始爬取页面**
```python
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['http://example.com']
def parse(self, response):
# 开始爬取
pass
页面下载
class MySpider(scrapy.Spider): name = 'example' start_urls = ['http://example.com'] def parse(self, response): url = 'http://example.com/page1' # 爬取的网址 yield scrapy.Request(url, callback=self.parse_page1) # 构造请求,添加至队列中
页面解析和数据抽取
class MySpider(scrapy.Spider): name = 'example' start_urls = ['http://example.com'] def parse(self, response): item = { } item['title'] = response.css("title::text").get() # 从响应数据中获取文章标题 yield item # 返回项目
数据保存
class MyPipeline(object): def process_item(self, item, spider): # 处理 item with open('data.json', 'w') as f: json.dump(item, f) return item
通过以上的介绍和示例对 Scrapy 已经有了一个基本的了解,如果想要更深入的了解和使用可以参考 Scrapy 的官方文档或者尝试写一些简单的 Scrapy 程序,感受一下其中的乐趣。
三、Scrapy的主要组件
1 Scrapy Engine
Scrapy Engine是整个框架的核心,它负责控制和调度整个爬虫流程,包括处理整个流程中的各种事件和数据,以及在最终完成爬虫工作时通知其他所有组件。
Scrapy Engine也是其他所有组件的接口,因此其他组件必须与Scrapy Engine交互,以便在整个爬虫流程中发挥其功能。
class Engine(object):
def __int__(self):
self.spider = None
self.scheduler = None
self.downloader = None
self.pipeline = None
def start(self):
# 启动整个流程,包括 Scheduler、Downloader、Spider 和 Pipeline 组件
pass
2 Scheduler组件
Scheduler组件是用于管理Scrapy Engine和其他所有组件之间的调度流程的主要组件。它的工作是将爬取请求安排好按顺序逐个执行,通过按照设定的规则为每个爬取请求选择最优的下载器,并且确保经过多次爬取请求的重试和处理后,将结果存储到Item Pipeline中。
class Scheduler(object):
def __init__(self):
self.queue = []
def push(self, request):
# 将 request 加入队列等待下载器的下载
pass
def pop(self):
# 从队列中获取下一个要下载的 request,并返回
pass
def next_request(self):
# 得到下一个要下载的 request 并调用 Downloader 组件
pass
3 Downloader组件
Downloader组件是用于处理爬取请求的主要组件。它从Web服务器上下载HTML、XML等页面和图片、视频等资源文件,并将下载好的资源交给Spider组件解析得到数据结果集。
Downloader组件通过异步I/O通道进行访问互联网获取相应网页,并区分不同的请求,处理cookie、User-Agent和代理IP,还能在不同的线程和进程中处理请求,提高爬取和处理数据的效率。
class Downloader(object):
def __init__(self):
self.session = requests.Session()
def fetch(self, request):
# 根据 request 获取数据,并返回 Response 对象
pass
def process_request(self, request):
# 处理 Request 对象
pass
4 Spider组件
Spider组件是用于处理爬取请求以及解析和抽取数据的主要组件。它接收Downloader组件下载好的资源和响应,并解析出所需的数据,将其封装到Item对象中,然后再传递给Item Pipeline进行处理。
一个Spider组件属于某个项目,并且设计Spider组件时需要模拟这个项目的数据清晰结构和相应爬取规则。其工作方式通常是,首先通过scrapy.Spider类的方法解析给定的起始url,然后跟进响应的url递归请求直到完成爬取任务。在这个过程中,还可以使用各种的解析器和正则表达式,以获得需要的数据。
class Spider(object):
def process_response(self, request, response):
# 处理 Response 对象
pass
5 Item Pipeline组件
Item Pipeline组件是Scrapy中一个非常重要的组件,它是数据处理管道的核心,负责处理和持久化Item对象中的数据。Item Pipeline组件可以为数据处理提供方便的机制,并且可以允许多个数据流通过管道,并进行高级数据处理。
Item Pipeline组件的处理方式可以分为三个步骤:接收完整的Item对象,进行数据处理和管理以及发送处理后的Item对象。
class Pipeline(object):
def process_item(self, item):
# 处理 Item 对象
pass
四、Scrapy的数据流
Scrapy的数据流包括Request/Response Objects,Item Objects,Spiders和Item Pipeline,具体的数据流程如下:
1 Request/Response Objects
爬虫代码中用户定义Request对象,Scrapy引擎将Request发送到Scheduler组件。Scheduler在一定时间内安排请求的执行和重试,然后将最终结果返回给Scrapy Engine。在Scrapy Engine中,Response对象包含着Scrapy从Web服务器上获得的所有HTML、XML或JSON代码等响应内容。当Scrapy Engine获得这些响应并分配给正确的Spider处理时,Response对象就成为Request对象的响应,再由Spider解析响应对象并抽取出所需数据,最终将数据保存到Item对象中。
2 Item Objects
在Scrapy Engine中,Item对象是处理数据的容器。Item Pipeline将Item对象中的数据处理和保存下来,如XML或JSON格式的数据存储到数据库中,或将处理后得到的数据渲染到模板上,生成可以阅读或打印的本地文件。Scrapy中支持多个Item Pipeline,它们通过一系列的操作和管道将数据从Spider组件传递到其他数据处理组件中。
class Item(object):
def __init__(self):
self.data = {
}
def __setitem__(self, key, value):
# 设置数据项
self.data[key] = value
def __getitem__(self, key):
# 获取数据项
return self.data[key]
3 Spiders
Spiders就是写爬取规则的地方,它包含了处理爬取请求和处理响应的所有必要逻辑代码。Spiders生成至少一个Item对象,该对象封装数据并且发送到Item Pipeline中。以Python代码为例,Spiders使用XPath或BeautifulSoup等HTML解析器将Response对象分析为字典或列表的形式,并从中抽取所需数据。
class Spider(object):
def parse(self, response):
# 解析 Response 对象获取数据并返回 Item 对象
pass
def start_requests(self):
# 发起 HTTP 请求或将请求加入 Scheduler 中等待处理?
pass
4 Item Pipeline
Item Pipeline负责处理和管理从Spider组件中创建的Item对象,以及进行数据持久化、数据格式转换和数据清洗等操作。数据处理管道将Item对象传递到另一个处理器,最终将处理后的数据存储在数据库等其他某种存储设备中。Item Pipeline的有效性在于可以提供一种机制,即使从多个并行流中处理数据,也可以按照你的预期顺序操作,而不会出现任何错误的顺序或数据问题。
class Pipeline(object):
def process_item(self, item):
# 处理 Item 对象
pass
五、Scrapy的常用命令
1 创建Scrapy项目
要创建一个新的Scrapy项目可以在命令行中使用 "scrapy startproject" 命令。如下所示,对于一个名为 "myproject" 的项目,我们可以按照以下方式创建它:
$ scrapy startproject myproject
2 创建Spider
要创建一个新的Spider可以使用命令 "scrapy genspider"。如下所示,为了创建一个名为 "mySpider" 的Spider并以 "mydomain.com" 为起始地址,可以按照以下方式创建它:
$ scrapy genspider mySpider mydomain.com
3 运行Spider
要运行Spider可以在项目主目录下使用 "scrapy crawl" 命令。如下所示,对于名为 "mySpider" 的Spider,我们可以按照以下方式运行它:
$ scrapy crawl mySpider
4 查看Spider数据
Scrapy提供了多种方便的方式来查看Spider的数据,包括输出到命令行、保存到文件、存储到数据库等。在Spider类中,可以使用 "logging"类将数据输出到命令行或日志文件。
import logging
class MySpider(scrapy.Spider):
name = 'myspider'
start_urls = ['http://www.example.com']
def parse(self, response):
# 解析响应并得到数据
item = {
'title': response.css('title::text').get()}
# 将数据输出到命令行和日志文件
self.logger.info(item)
logging.debug(item)
六、Scrapy常见问题解决方法
1 Scrapy运行慢
Scrapy的运行速度是由众多因素决定的,但可能会与以下因素有关:
- 网络连接速度慢或Web服务器响应速度慢。
- Spider在解析和处理页面数据时,采用了复杂的算法,导致爬取过程缓慢。
- 爬虫程序在运行时占用大量的系统资源,如、内存或处理器带宽等。
为了尽可能地优化Scrapy运行速度,可以采取以下措施:
- 优化网络连接速度:使用更快的Internet连接或使用Web服务器的CDN等加速技术。
- 优化Spider,避免在处理数据时采用过于复杂的算法。尽可能选择更快和更轻量级的算法。
- 调整Scrapy的配置,以便更充分优化资源使用,这包括适当地配置线程、进程和内存限制等。
2 Scrapy在多线程上的表现
Scrapy是一种基于协程的异步框架,因此它对于多线程的支持不同于其他的爬虫框架。Scrapy通过异步执行HTTP请求来提高爬取性能,并通过并发递归实现了爬虫的高效率。
Scrapy支持线程池,可以使用 Python 的 threading 模块和 Queue 模块实现多线程爬取。如果需要在Scrapy中使用多线程,可以考虑适当的调整并发请求限制、设置队列容量,避免资源冲突等。
3 Scrapy在分布式上的表现
Scrapy本身支持分布式爬虫,但需要自己手动实现。一般来说,可以使用分布式队列、RPC等技术来实现Scrapy的数据共享。
为了更好地运用Scrapy可以考虑以下措施:
- 使用分布式的队列,如RabbitMQ等,并在所有节点上安装Scrapy,并将请求分发到不同的节点上。
- 使用RPC或Web服务来实现Scrapy节点之间的通信,以及处理共享的数据等。
- 对于稳定性或性能方面的要求比较高的应用,可以使用一些分布式框架,如Hadoop等,并使用Scrapy进行数据爬取,最终将数据存储在分布式存储系统中。
七、Scrapy的适用场景
Scrapy是现今最流行的网络爬虫框架之一它具有高效、可扩展性强、配置简单等优点。下面是Scrapy适用的场景
1. 数据挖掘
Scrapy可以在网络上帮助用户收集和分析数据。例如,可以使用Scrapy抓取并分析股票价格的数据,检索市场上的产品价格和销售情况。同时Scrapy还能帮助将这些数据存储在本地数据库或云服务中,以便进一步的数据挖掘。
2. 搜索引擎优化
Scrapy可以抓取网站有关的信息以便帮助网站优化。例如,可以使用Scrapy抓取并分析关键词的搜索排名情况,并对网站进行相应的优化操作。
3. 自动化测试
Scrapy可以在自动化测试期间模拟网页的行为,并检测应用程序中的异常行为。例如,可以使用Scrapy模拟用户在网站上的操作,以便检测应用当前的测试状态。同时,由于Scrapy具有高度可扩展的测试架构,因此用户可以使用Python编写自己的测试脚本,以满足项目的不同需求。
4. 其他方面的数据采集
Scrapy可应用于各种类型的数据采集项目,如新闻报道、竞争对手产品信息抓取、政府部门和学术研究数据收集等。由于用户可以灵活定义Scrapy爬虫的规则, Scrapy具有适应性很强的特点。
八、Scrapy的应用案例
以下三个应用案例展示了Scrapy在不同场景下的表现
1. 电影网 Top 250 的抓取
电影Top 250是一个电影爱好者喜欢访问的网站。该网站提供了不同类型电影的信息和评分,可以使用Scrapy 抓取整个网站的数据,存储到本地数据库或者进行在线分析处理。下面是用Python实现的相应代码:
# 导入Scrapy类库
import scrapy
# 建立Spider类
class DoubanSpider(scrapy.Spider):
# 定义Spider名称和起始URL地址
name = 'aaa'
start_urls = ['https://movie.aaa.com/top250']
# 定义parse回调函数
def parse(self, response):
# 解析响应内容并爬取所需数据
pass
2. 求职网职位的抓取
求职网提供各类IT职位信息。使用Scrapy可以轻松地抓取所有职位发布信息。下面是用Python实现的相应代码:
# 导入Scrapy类库
import scrapy
# 建立Spider类
class LagouSpider(scrapy.Spider):
# 定义Spider名称和起始URL地址
name = 'qiuzhi'
start_urls = ['https://www.qiuzhi.com/']
# 定义parse回调函数
def parse(self, response):
# 解析响应内容并爬取所需数据
pass
3. 古诗文网的抓取
古诗文网是一家专门汇总古代诗词、曲文、散文等文学作品的网站,使用Scrapy 可以轻松抓取相关的文学作品并存储到本地数据库。 下面是用Python实现的相应代码:
# 导入Scrapy类库
import scrapy
# 建立Spider类
class GushiwenSpider(scrapy.Spider):
# 定义Spider名称和起始URL地址
name = 'gushiwen'
start_urls = ['https://www.gushiwen.org/']
# 定义parse回调函数
def parse(self, response):
# 解析响应内容并爬取所需数据
pass
九、Scrapy的发展方向
在过去的几年Scrapy已经成为了网络爬虫领域里最受欢迎的框架之一。然而不同的应用场景和趋势需要不断优化和改进Scrapy,以下是Scrapy的发展方向:
1. 异步网络库的支持
随着互联网的发展和网络的不断扩容,采取异步编程方式可以极大地提高程序性能。因此Scrapy的下一个发展方向之一是支持异步网络库。针对大规模的请求,Scrapy需要与高效异步网络库(如Tornado, asyncio, Twisted等)进行集成,以便在运行爬虫时能够更好地处理异步请求,大幅提高程序效率。
2. 爬虫数据的机器学习
现如今机器学习在许多领域里都有广泛应用。对于Scrapy来说,采用机器学习技术可以更准确地分析和提取数据。这可以通过对Scrapy爬虫所获取的内容进行训练,从而使爬虫程序自动的识别和提取更具体的信息和数据。从而更快速、更准确地完成数据采集任务。
3. 容错机制的加强
在网络爬虫的实践中由于网络环境的多变及网站采用反爬技术等原因,Scrapy的稳定性有时候还需要进一步加强。一个强大的容错机制可以帮助Scrapy更好的应对这种网络环境下的不稳定因素。针对这个问题,Scrapy社区可以推动开发更多强韧的容错机制,以便程序可以更好的应对各种异常情况。
4. 集成数据可视化工具
Scrapy可以采集大量的数据但如果没有一个清晰的分析和展示平台,数据就无法得到合理的利用。因此,将数据可视化工具集成进来成为了Scrapy的又一个发展方向。通过集成可视化工具(如Matplotlib, Plotly, D3等),Scrapy可以直接在程序内部实现数据可视化的功能,帮助用户更好地理解和分析爬取到的数据。
十、小结回顾
随着互联网的发展网络爬虫技术也越来越成熟。Scrapy作为其中的代表框架,具有极大的优势和广泛的应用。在不断完善和优化Scrapy的过程中相信Scrapy将能够更好地满足各行各业的需求,成为网络爬虫技术的代表和领导者。