13.9 Scrapy 对接 Splash
在上一节我们实现了 Scrapy 对接 Selenium 抓取淘宝商品的过程,这是一种抓取 JavaScript 动态渲染页面的方式。除了 Selenium,Splash 也可以实现同样的功能。本节我们来了解 Scrapy 对接 Splash 来进行页面抓取的方式。
1. 准备工作
请确保 Splash 已经正确安装并正常运行,同时安装好 Scrapy-Splash 库,如果没有安装可以参考第 1 章的安装说明。
2. 新建项目
首先新建一个项目,名为 scrapysplashtest,命令如下所示:
1 |
scrapy startproject scrapysplashtest |
新建一个 Spider,命令如下所示:
1 |
scrapy genspider taobao www.taobao.com |
3. 添加配置
可以参考 Scrapy-Splash 的配置说明进行一步步的配置,链接如下:https://github.com/scrapy-plugins/scrapy-splash#configuration。 修改 settings.py,配置 SPLASH_URL。在这里我们的 Splash 是在本地运行的,所以可以直接配置本地的地址:
1 |
SPLASH_URL = 'http://localhost:8050' |
如果 Splash 是在远程服务器运行的,那此处就应该配置为远程的地址。例如运行在 IP 为 120.27.34.25 的服务器上,则此处应该配置为:
1 |
SPLASH_URL = 'http://120.27.34.25:8050' |
还需要配置几个 Middleware,代码如下所示:
1 |
DOWNLOADER_MIDDLEWARES = { |
这里配置了三个 Downloader Middleware 和一个 Spider Middleware,这是 Scrapy-Splash 的核心部分。我们不再需要像对接 Selenium 那样实现一个 Downloader Middleware,Scrapy-Splash 库都为我们准备好了,直接配置即可。 还需要配置一个去重的类 DUPEFILTER_CLASS,代码如下所示:
1 |
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter' |
最后配置一个 Cache 存储 HTTPCACHE_STORAGE,代码如下所示:
1 |
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage' |
4. 新建请求
配置完成之后,我们就可以利用 Splash 来抓取页面了。我们可以直接生成一个 SplashRequest 对象并传递相应的参数,Scrapy 会将此请求转发给 Splash,Splash 对页面进行渲染加载,然后再将渲染结果传递回来。此时 Response 的内容就是渲染完成的页面结果了,最后交给 Spider 解析即可。 我们来看一个示例,如下所示:
1 |
yield SplashRequest(url, self.parse_result, |
在这里构造了一个 SplashRequest 对象,前两个参数依然是请求的 URL 和回调函数,另外还可以通过 args 传递一些渲染参数,例如等待时间 wait 等,还可以根据 endpoint 参数指定渲染接口,另外还有更多的参数可以参考文档的说明:https://github.com/scrapy-plugins/scrapy-splash#requests。 另外我们也可以生成 Request 对象,关于 Splash 的配置通过 meta 属性配置即可,代码如下:
1 |
yield scrapy.Request(url, self.parse_result, meta={ |
SplashRequest 对象通过 args 来配置和 Request 对象通过 meta 来配置,两种方式达到的效果是相同的。 本节我们要做的抓取是淘宝商品信息,涉及页面加载等待、模拟点击翻页等操作。我们可以首先定义一个 Lua 脚本,来实现页面加载、模拟点击翻页的功能,代码如下所示:
1 |
function main(splash, args) |
我们定义了三个参数:请求的链接 url、等待时间 wait、分页页码 page。然后禁用图片加载,请求淘宝的商品列表页面,通过 evaljs() 方法调用 JavaScript 代码,实现页码填充和翻页点击,最后返回页面截图。我们将脚本放到 Splash 中运行,正常获取到页面截图,如图 13-15 所示。 图 13-15 页面截图 翻页操作也成功实现,如图 13-16 所示即为当前页码,和我们传入的页码 page 参数是相同的。 图 13-16 翻页结果 我们只需要在 Spider 里用 SplashRequest 对接 Lua 脚本就好了,如下所示:
1 |
from scrapy import Spider |
我们把 Lua 脚本定义成长字符串,通过 SplashRequest 的 args 来传递参数,接口修改为 execute。另外,args 参数里还有一个 lua_source 字段用于指定 Lua 脚本内容。这样我们就成功构造了一个 SplashRequest,对接 Splash 的工作就完成了。 其他的配置不需要更改,Item、Item Pipeline 等设置与上节对接 Selenium 的方式相同,parse() 回调函数也是完全一致的。
5. 运行
接下来,我们通过如下命令运行爬虫:
1 |
scrapy crawl taobao |
运行结果如图 13-17 所示。 图 13-17 运行结果 由于 Splash 和 Scrapy 都支持异步处理,我们可以看到同时会有多个抓取成功的结果。在 Selenium 的对接过程中,每个页面渲染下载是在 Downloader Middleware 里完成的,所以整个过程是阻塞式的。Scrapy 会等待这个过程完成后再继续处理和调度其他请求,这影响了爬取效率。因此使用 Splash 的爬取效率比 Selenium 高很多。 最后我们再看看 MongoDB 的结果,如图 13-18 所示。 图 13-18 存储结果 结果同样正常保存到了 MongoDB 中。
6. 本节代码
本节代码地址:https://github.com/Python3WebSpider/ScrapySplashTest。
7. 结语
在 Scrapy 中,建议使用 Splash 处理 JavaScript 动态渲染的页面。这样不会破坏 Scrapy 中的异步处理过程,会大大提高爬取效率。而且 Splash 的安装和配置比较简单,通过 API 调用的方式实现了模块分离,大规模爬取的部署也更加方便。