python scrapy 使用教程
Scrapy介绍
Scrapy 是一个应用程序框架,用于抓取网站和提取结构化数据,这些数据可用于广泛的有用应用程序,如数据挖掘、信息处理或历史存档。
Scrapy爬虫框架的优点主要有以下几点:
- 便于提取数据:内置支持 selecting and extracting 使用扩展的CSS选择器和XPath表达式从HTML/XML源中获取数据,并使用正则表达式提取助手方法。
- 便于获取数据的持久化:内置支持 generating feed exports 以多种格式(json、csv、xml)存储在多个后端(ftp、s3、本地文件系统),以及可以通过自定义中间间将获取的数据保存在数据库。
- 便于扩展和自定义:Scrapy本身内置了很多用于处理爬虫过程中的中间件:
cookie和会话处理
HTTP功能,如压缩、身份验证、缓存
用户代理欺骗
robots.txt
爬行深度限制
等等,并支持构建属于自己的中间件,满足不同的业务需求。 - 支持高并发:Scrapy高并发的实现是基于Twisted库实现的并发。Twisted是一个基于事件驱动的Python网络框架,它使用非阻塞的I/O模型,能够处理大量并发连接,从而实现高性能的网络应用。Scrapy利用Twisted库的异步网络引擎,可以同时发送多个请求并处理多个响应,实现了高效的并发处理能力。但是过高的并发会给爬取的网站带来很大的压力。
Scrapy 安装
支持的Python版本
Scrapy需要python3.6+,CPython实现(默认)或pypy7.2.0+实现
安装 Scapy
如果用 Anaconda 或 Miniconda ,您可以从 conda-forge 频道,它有针对Linux、Windows和macOS的最新软件包。
使用 conda 安装Scrapy 运行:
conda install -c conda-forge scrapy
使用pip 安装:
pip install Scrapy
Scrapy 依赖包
Scrapy 是用纯python编写的,它依赖于几个关键的python包(以及其他包):
- lxml 一个高效的XML和HTML解析器
- parsel ,一个写在lxml上面的html/xml数据提取库
- w3lib ,用于处理URL和网页编码的多用途帮助程序
- twisted 异步网络框架
- cryptography 和 pyOpenSSL ,处理各种网络级安全需求
以上包不需要手动安装,安装Sccrapy的时候会自动安装,了解即可。
Scrapy构成及运行原理
了解Scrapy的构成和运行原理是很有必要的,有助于在使用scrapy的过程中解决遇到的各种问题,只有知其所以然,用起来才能得心应手。
Scrapy架构
以下是scrapy架构图:
通过架构图得知Scrapy是由以下几个部分组成的。
- Scrapy engine (引擎):引擎就像爬虫的大脑,控制整个爬取过程的核心,负责调度和协调其他组件的工作。
- Scheduler (调度器):调度器负责接收引擎传递过来的请求,将其放入队列中,并在引擎请求时返回。正是因为调度器在队列中处理请求,才能够实现并发爬虫。
- Downloader (下载器):负责下载引擎传递的请求,并将下载好的响应返回给引擎。
- Middleware (中间件):处理引擎和下载器之间的请求和响应,可以用来进行请求的过滤、响应的处理等操作。
- Spiders (爬虫):在spiders中编写我们的爬虫代码,主要的爬取逻辑在这里实现。
- Item、Pipline (项和管道) :在item中定义我们要获取数据的结构,在pipline中用于进一步处理提取的数据等,使得爬取过程更加简单和高效。
Scrapy 如何运转
现在我们知道了Scrapy的组成,那么它是如何运转的呢,运转的大致流程如下:
- 当启动我们的Scrapy爬虫时,引擎首先从 Spider 中获取初始请求。
- 然后引擎将获取到的请求发送给调度器进行处理。
- 调度器将请求排队并通过下载器中间件发送给下载器。
- 下载器将下载的响应返回给引擎。
- 然后引擎将响应发送给蜘蛛进行解析并生成新的请求。
如果在以上流程中设置了蜘蛛中间件,就可以在蜘蛛处理请求和生成响应的过程中进行干预。它可以用来处理请求和响应、添加新的请求、修改 Item 对象等。
如果设置了下载器中间件,就可以在下载请求和处理响应的过程中进行干预。它可以用来修改请求和响应、处理异常、设置代理等。
tips:
这个过程循环执行,直到没有新的请求可以发送为止。最后,蜘蛛将处理完的数据通过管道组件进行处理和存储。
构建Scrapy项目
在开始抓取之前,我们必须建立一个新的项目。通过命令行输入要在其中存储代码并运行的目录:
scrapy startproject scrapy_project
scrapy_project是我们创建的项目名,scrapy将创建一个 scrapy_project 目录包含以下内容
scrapy_project/
scrapy.cfg # scrapy配置文件
scrapy_project/ # scrapy_projec包
__init__.py
items.py # 定义spider数据结构的文件
middlewares.py # 项目中间件文件
pipelines.py # 项目管道文件
settings.py # 项目设置文件
spiders/ # 爬虫文件夹
__init__.py
开始编写Spider
在spiders文件夹下新建一个py文件,在文件中定义一个爬虫类,这个类必须继承自 scrapy.Spider 并设置一个名为 name 的类变量为我们的爬虫名:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
page = response.url.split("/")[-2]
filename = f'quotes-{page}.html'
with open(filename, 'wb') as f:
f.write(response.body)
self.log(f'Saved file {filename}')
可以看到在QuotesSpider类中定义了一些属性和方法:
- name :标识蜘蛛。它在一个项目中必须是唯一的,即不能为不同的爬行器设置相同的名称。
- start_requests() :必须返回请求的可迭代(您可以返回请求列表或编写生成器函数),爬行器将从该请求开始爬行。后续请求将从这些初始请求中相继生成。
- parse() :将被调用以处理为每个请求下载的响应的方法。Response参数是 TextResponse 它保存页面内容,并具有进一步有用的方法来处理它。
这个 parse() 方法通常解析响应,将抓取的数据提取为字典,还查找要遵循的新URL并创建新请求 (Request )。
启动爬虫
首先进入到项目根目录,在命令行中输入以下命令来启动爬虫:
scrapy crawl quotes
此命令将会启动我们的Spider,并向quotes.toscrape.com这个网站发起请求,并将获取到的html页面保存在文件中。
此时控制台会打印类似以下字符:
... (omitted for brevity)
2024-01-01 21:24:05 [scrapy.core.engine] INFO: Spider opened
2024-01-01 21:24:05 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2024-01-01 21:24:05 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2024-01-01 21:24:05 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)
2024-01-01 21:24:05 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
2024-01-01 21:24:05 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/2/> (referer: None)
2024-01-01 21:24:05 [quotes] DEBUG: Saved file quotes-1.html
2024-01-01 21:24:05 [quotes] DEBUG: Saved file quotes-2.html
2024-01-01 21:24:05 [scrapy.core.engine] INFO: Closing spider (finished)
...
引擎做了什么
首先运行start_requests方法,yield scrapy.Request 方法返回的 Request 对象 。在接收到每个请求的响应后,它会实例化 Response 对象,并调用与请求关联的回调方法(在本例中, parse 方法)将响应作为参数传递。
这里的Request 和Response 对象是Scrapy框架封装的对象,与requests库的使用方法有区别。
启动请求方法的快捷方式
可以不实现 start_requests() 方法,该方法生成 scrapy.Request 对象,可以只定义一个 start_urls 具有URL列表的类属性。然后,此列表将由的默认实现使用 start_requests() 要为爬行器创建初始请求:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
def parse(self, response):
page = response.url.split("/")[-2]
filename = f'quotes-{page}.html'
with open(filename, 'wb') as f:
f.write(response.body)
这个 parse() 方法来处理这些URL的每个请求,即使我们还没有显式地告诉Scrapy这样做。发生这种情况是因为 parse() 是Scrapy的默认回调方法,在没有显式分配回调的情况下为请求调用该方法。
这种方法作为了解即可,一般不会用到
提取数据
Scrapy 提供了非常方便的 Scrapy Shell 供我们在解析数据的时候方便我们调试提取数据的方法,在Scrapy Shell中支持xpath和css解析,非常的方便。
只需要在命令行中输入:
scrapy shell “http://quotes.toscrape.com/page/1/”
Shell将会输出以下类似内容:
[ ... Scrapy log here ... ]
2024-01-01 12:09:27 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
[s] Available Scrapy objects:
[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s] crawler <scrapy.crawler.Crawler object at 0x7fa91d888c90>
[s] item {}
[s] request <GET http://quotes.toscrape.com/page/1/>
[s] response <200 http://quotes.toscrape.com/page/1/>
[s] settings <scrapy.settings.Settings object at 0x7fa91d888c10>
[s] spider <DefaultSpider 'default' at 0x7fa91c8af990>
[s] Useful shortcuts:
[s] shelp() Shell help (print this help)
[s] fetch(req_or_url) Fetch request (or URL) and update local objects
[s] view(response) View response in a browser
我个人建议在提取数据的时候使用Xpath选择器,因为XPath表达式是非常强大的,是抓取选择器的基础。实际上,CSS选择器在引擎盖下转换为xpath。虽然不如css选择器那么流行。
未完待续。。。