关于 Python 系列其他文章的传送门
-
《 边爬豆瓣,边学习 Python 爬虫框架 Scrapy 》 您当前所在位置

爬虫框架 Scrapy¶
Scrapy 是用 Python 实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架。
可以应用在包括数据挖掘、信息处理或存储历史数据等一系列的程序中。
Scrapy 使用 Twisted 高效异步网络框架来处理网络通信,可以加快下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。
Scrapy 的架构:

Scrapy Engine
引擎,负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。此组件相当于爬虫的“大脑”,是整个爬虫的调度中心。
Scheduler 调度器
调度器接收从引擎发送过来的 request,并将他们入队,以便之后引擎请求他们时提供给引擎。初始的爬取 URL 和后续在页面中获取的待爬取的 URL 将放入调度器中,等待爬取。同时调度器会自动去除重复的 URL(如果特定的 URL 不需要去重也可以通过设置实现,如 post 请求的 URL)
Downloader 下载器(负责爬取数据)
下载器负责去网站爬取数据,获取页面数据并提供给引擎,而后提供给 spider。
Spiders 爬虫(负责分析)
Spider 是编写的类,作用如下:Scrapy 用户编写用于分析 response 并提取 item(即获取到的 item) 额外跟进的 URL,将额外跟进的 URL 提交给引擎,加入到 Scheduler 调度器中。将每个 spider 负责处理一个特定 (或 一些) 网站。
Item Pipeline(负责处理分析后的一个个 item)
Item Pipeline 负责处理被 spider 提取出来的 item。典型的处理有清理、验证及持久化 (例如存取到数据库中)。当页面被爬虫解析所需的数据存入 Item 后,将被发送到项目管道 (Pipeline),并经过设置好次序的 pipeline 程序处理这些数据,最后将存入本地文件或存入数据库。
以下是 item pipeline 的一些典型应用:
- 清理 HTML 数据;
- 验证爬取的数据 (检查 item 包含某些字段);
- 查重 (或丢弃);
- 将爬取结果保存到数据库中。
Spider 中间件(Spider middlewares)
Spider 中间件,是在引擎和 Spider 之间的特定钩子 (specific hook),处理 spider 的输入(response) 和输出(items 或 requests)。也提供了同样的简便机制,通过插入自定义代码来扩展 Scrapy 功能。
数据流(Data fiow)¶
- 引擎打开一个网站(open a domain),找到处理该网站的 Spider 并向该 spider 请求第一个(批)要爬取的 URL(s);
- 引擎从 Spider 中获取到第一个要爬取的 URL 并加入到调度器 (Scheduler) 作为请求以备调度;
- 引擎向调度器请求下一个要爬取的 URL;
- 调度器返回下一个要爬取的 URL 给引擎,引擎将 URL 通过下载中间件并转发给下载器(Downloader);
- 一旦页面下载完毕,下载器生成一个该页面的 Response,并将其通过下载中间件发送给引擎;
- 引擎从下载器中接收到 Response,然后通过 Spider 中间件发送给 Spider 处理;
- Spider 处理 Response 并返回提取到的 Item 及 (跟进的) 新的 Request 给引擎;
- 引擎将 Spider 返回的 Item 交给 Item Pipeline,将 Spider 返回的 Request 交给调度器;
- (从第二步)重复执行,直到调度器中没有待处理的 request,引擎关闭。
注意:只有当调度器中没有任何 request 了,整个程序才会停止执行。如果有下载失败的 URL,会重新下载。
安装 scrapy¶
1 | 安装 wheel 支持 |
安装好后,可以在命令行使用 scrapy 命令。
Scrapy 开发 – 爬取豆瓣¶
项目编写流程¶
- 创建项目:
- 使用
scrapy startproject proname创建一个 scrapy 项目。
- 使用
- 编写 item:
- 在
items.py中编写 Item 类,明确从 response 中提取的 item。
- 在
- 编写爬虫:
- 编写
spiders/proname_spider.py,即爬取网站的 spider 并提取出 item。
- 编写
- 编写 item pipeline:
- item 的处理,可以存储。
1 创建项目¶
豆瓣书评爬取,标签为“编程”,第一页、第二页链接如下:
1 | https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T |
创建项目,名子:first,执行下面命令:
1 | scrapy startproject first |
并进入目录:
1 | cd first |
会产生如下目录和文件:
1 | first |
解析:
first:外部的 first 目录是整个项目目录,内部的 first 目录是整个项目的全局目录;scrapy.cfg:必须有的重要的项目的配置文件;first项目目录:BOT_NAME:爬虫名;ROBOTSTXT_OBEY=True:是否遵从 robots 协议;USER_AGENT = '':指定爬取时使用的 agent;CONCURRENT_REQEUST = 16:默认 16 个并行;DOWNLOAD_DELAY= 3:下载延时;COOKIES_ENABLED = False:缺省是启用,一般需要登录时才需要开启 cookie;DEFAULT_REQUEST_HEADERS = {}:默认请求头,需要时填写;SPIDER_MIDDLEWARES:爬虫中间件;DOWNLOADER_MIDDLEWARES:下载中间件;'first.middlewares.FirstDownloaderMiddleware': 543 543:优先级越小越高;
ITEM_PIPELINES:管道配置;'firstscrapy.pipelines.FirstscrapyPipeline': 300:item 交给哪一个管道处理,300 优先级越小越高;
- spiders 目录:
__init__.py必须有,可以在这里写爬虫类,也可以写爬虫子模块。
first/settings.py 配置文件参考:
1 | BOT_NAME = 'first' |
注意:一定要更改 User-Agent,否则访问 https://book.douban.com/ 会返回 403。
2 编写 Item¶
在 items.py 中创建 item 的类:
1 | import scrapy |
3 编写爬虫¶
在 spiders 目录下,为爬取豆瓣书评编写爬虫类。
编写的爬虫类需要继承自 scrapy.Spider,在这个类中定义爬虫名、爬取范围、其实地址等。
在 scrapy.Spider 中 parse 方法未实现,所以子类应该实现 parse 方法。该方法传入 response 对象。
爬取读书频道,tag 为 “编程” 的书名和评分:
https://book.douban.com/tag/编程?start=20&type=T
使用 crawl 的爬取子命令,可以生成模板文件 book.py:
1 | $ scrapy genspider -t basic book douban.com |
在模板文件 book.py 的基础上,补充代码如下:
- response 是服务器端 HTTP 的响应,它是
scrapy.http.response.html.HtmlResponse类。
1 | import scrapy |
使用 crawl 就可以爬取,crawl 子命令:
1 | scrapy list |
这样网页就被爬取到本地的 '/tmp/testbook.html' 文件了。
3.1 解析 HTML¶
爬虫获得的内容是 response 对象,可以直接使用解析库来解析。
scrapy 包装了 lxml,父类 TextResponse 类也提供了 xpath 方法和 css 方法,可以混合使用这两套接口解析 HTML。
下面演示如何分析刚才保存的本地 html 文件。
演示示例(这个非框架中的文件,可以随意建个 py 文件进行测试):
1 | from scrapy.http.response.html import HtmlResponse |
3.2 item 封装数据¶
用上面示例中的方法,让 Downloader 去爬取网页,spiders 直接对 HTML 进行分析,并提取出相关的 title 和 rate。
代码实现 spiders/book.py:
1 | import scrapy |
将 books.py 中 BookSpider 改成生成器,只需要把 return items 改造成 yield item ,即由产生一个列表变成 yield 一个个 item。
注意再注意:返回的 item 必须是 scrapy.Item 的子类,一般是先在 items 中定义好的。如果直接 return 字符串,会导致数据无法传给 pipelines 处理,并且控制台出现类型这样的报错 ERROR: Spider must return request, item, or None, got 'str' in <GET http://XXXXXXXX
使用子命令 crawl 可以把 return 的数据保存到 json 文件:
1 | 测试使用,一般情况下应该交给 pipeline 去处理 |
注意:保存后数据已经是 unicode 字符集,汉字的 unicode 表达。下一节有解决办法,搜索 FEED_EXPORT_ENCODING 关键字即可找到。
4 pipeline 处理¶
脚手架帮我们创建了一个 pipelines.py 文件和一个类。
4.1 开启 pipeline¶
1 | # Configure item pipelines |
整数 300 表示优先级,越小越高。取值范围为 0-1000。
4.2 常用方法¶
| 名称 | 参数 | 备注 |
|---|---|---|
process_item(self, item, spider) |
item:爬取的一个个数据;spider:item 的爬取者;每一个 item 处理都调用; 返回一个 Item 对象,或抛出 DropItem 异常;被丢弃的 Item 对象将不会被之后的 pipeline 组件处理。 |
必须 |
open_spider(self, spider) |
spider 表示被开启的 spider; 调用一次。 |
可选 |
__init__(self) |
spider 实例创建时; 调用一次。 |
可选 |
1 | # 常用方法 |
需求实现:通过 pipeline 将爬取的数据存入 json 文件中¶
first/spiders/book.py 文件:
- 在 spider 上添加自定义配置信息
custom_settings,指明 json 文件的保存路径,其他没有改动。
1 | import scrapy |
注意再注意:返回的 item 必须是 scrapy.Item 的子类,一般是先在 items 中定义好的。如果直接 return 字符串,会导致数据无法传给 pipelines 处理,并且控制台出现类型这样的报错 ERROR: Spider must return request, item, or None, got 'str' in <GET http://XXXXXXXX
first/pipelines.py 文件:
- 在
pipelines.py中可以通过spider.settings['filename']获取到spiders/book.py中的自定义配置信息。
1 | from itemadapter import ItemAdapter |
启动爬虫:
1 | scrapy crawl book |
关于字符编码¶
注意:保存后数据已经是 unicode 字符集,即汉字的 unicode 表达。
关于 Unicode 和 UTF-8 有什么区别? - 盛世唐朝的回答 - 知乎
https://www.zhihu.com/question/23374078/answer/69732605
在 setting.py 文件中添加如下设置,再使用 $ scrapy crawl book -o books.json 导出就可以用 UTF-8 保存啦:
1 | FEED_EXPORT_ENCODING = 'utf-8' |