Python3网络爬虫开发实战
  • Introduction
  • 0-目录
  • 0.0-前言
  • 0.1-序一
  • 0.3-序二
  • 1-开发环境配置
  • 1.1-Python3的安装
  • 1.2-请求库的安装
  • 1.3-解析库的安装
  • 1.4-数据库的安装
  • 1.5-存储库的安装
  • 1.6-Web库的安装
  • 1.7-App爬取相关库的安装
  • 1.8-爬虫框架的安装
  • 1.9-部署相关库的安装
  • 2-爬虫基础
  • 2.1-HTTP基本原理
  • 2.2-Web网页基础
  • 2.3-爬虫基本原理
  • 2.4-会话和Cookies
  • 2.5-代理基本原理
  • 3-基本库的使用
  • 3.1-使用urllib
  • 3.2-使用requests
  • 3.3-正则表达式
  • 3.4-爬取猫眼电影排行
  • 4-解析库的使用
  • 4.1-XPath的使用
  • 4.2-BeautifulSoup的使用
  • 4.3-pyquery的使用
  • 5-数据存储
  • 5.1-文件存储
  • 5.2-关系型数据库存储
  • 5.3-非关系型数据库存储
  • 6-Ajax数据爬取
  • 6.1-什么是Ajax
  • 6.2-Ajax分析方法
  • 6.3-Ajax结果提取
  • 6.4-分析Ajax爬取今日头条街拍美图
  • 7-动态渲染页面抓取
  • 7.1-Selenium的使用
  • 7.2-Splash的使用
  • 7.3-Splash负载均衡配置
  • 7.4-使用Selenium爬取淘宝商品
  • 8-验证码的识别
  • 8.1-图形验证码的识别
  • 8.2-极验滑动验证码识别
  • 8.3-点触验证码识别
  • 8.4-微博宫格验证码识别
  • 9-代理的使用
  • 9.1-代理的设置
  • 9.2-代理池的维护
  • 9.3-付费代理的使用
  • 9.4-ADSL代理的使用
  • 9.5-使用代理爬取微信公众号文章
  • 10-模拟登录
  • 10.1-模拟登录并爬取GitHub
  • 10.2-Cookies池的搭建
  • 11-APP的爬取
  • 11.1-Charles的使用
  • 11.2-mitmproxy的使用
  • 11.3-mitmdump爬取“得到”App电子书信息
  • 11.4-Appium的使用
  • 11.5-Appium爬取微信朋友圈
  • 11.6-Appium+mitmdump爬取京东商品评论
  • 12-pyspider框架的使用
  • 12.1-pyspider框架介绍
  • 12.2-pyspider基本使用
  • 12.3-pyspider用法详解
  • 13-Scrapy框架的使用
  • 13.1-Scrapy框架介绍
  • 13.2-Scrapy入门
  • 13.3-Selector的用法
  • 13.4-Spider的用法
  • 13.5-Downloader Middleware的用法
  • 13.6-Spider Middleware的用法
  • 13.7-Item Pipeline的用法
  • 13.8-Scrapy对接Selenium
  • 13.9-Scrapy对接Splash
  • 13.10-Scrapy通用爬虫
  • 13.11-Scrapyrt的使用
  • 13.12-Scrapy对接Docker
  • 13.13-Scrapy爬取新浪微博
  • 14-分布式爬虫
  • 14.1-分布式爬虫理念
  • 14.2-Scrapy-Redis源码解析
  • 14.3-Scrapy分布式实现
  • 14.4-Bloom Filter的对接
  • 15-分布式爬虫的部署
  • 15.1-Scrapyd分布式部署
  • 15.2-Scrapyd-Client的使用
  • 15.3-Scrapyd对接Docker
  • 15.4-Scrapyd批量部署
  • 15.5-Gerapy分布式管理
Powered by GitBook
On this page
  • 1. 准备工作
  • 2. 新建项目
  • 3. 添加配置
  • 4. 新建请求
  • 5. 运行
  • 6. 本节代码
  • 7. 结语

Was this helpful?

13.9-Scrapy对接Splash

Previous13.8-Scrapy对接SeleniumNext13.10-Scrapy通用爬虫

Last updated 5 years ago

Was this helpful?

在上一节我们实现了 Scrapy 对接 Selenium 抓取淘宝商品的过程,这是一种抓取 JavaScript 动态渲染页面的方式。除了 Selenium,Splash 也可以实现同样的功能。本节我们来了解 Scrapy 对接 Splash 来进行页面抓取的方式。

1. 准备工作

请确保 Splash 已经正确安装并正常运行,同时安装好 Scrapy-Splash 库,如果没有安装可以参考第 1 章的安装说明。

2. 新建项目

首先新建一个项目,名为 scrapysplashtest,命令如下所示:

scrapy startproject scrapysplashtest

新建一个 Spider,命令如下所示:

scrapy genspider taobao www.taobao.com

3. 添加配置

可以参考 Scrapy-Splash 的配置说明进行一步步的配置,链接如下:。

修改 settings.py,配置 SPLASH_URL。在这里我们的 Splash 是在本地运行的,所以可以直接配置本地的地址:

SPLASH_URL = 'http://localhost:8050'

如果 Splash 是在远程服务器运行的,那此处就应该配置为远程的地址。例如运行在 IP 为 120.27.34.25 的服务器上,则此处应该配置为:

SPLASH_URL = 'http://120.27.34.25:8050'

还需要配置几个 Middleware,代码如下所示:

DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,}

这里配置了三个 Downloader Middleware 和一个 Spider Middleware,这是 Scrapy-Splash 的核心部分。我们不再需要像对接 Selenium 那样实现一个 Downloader Middleware,Scrapy-Splash 库都为我们准备好了,直接配置即可。

还需要配置一个去重的类 DUPEFILTER_CLASS,代码如下所示:

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

最后配置一个 Cache 存储 HTTPCACHE_STORAGE,代码如下所示:

HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

4. 新建请求

配置完成之后,我们就可以利用 Splash 来抓取页面了。我们可以直接生成一个 SplashRequest 对象并传递相应的参数,Scrapy 会将此请求转发给 Splash,Splash 对页面进行渲染加载,然后再将渲染结果传递回来。此时 Response 的内容就是渲染完成的页面结果了,最后交给 Spider 解析即可。

我们来看一个示例,如下所示:

yield SplashRequest(url, self.parse_result,
    args={
        # optional; parameters passed to Splash HTTP API
        'wait': 0.5,
        # 'url' is prefilled from request url
        # 'http_method' is set to 'POST' for POST requests
        # 'body' is set to request body for POST requests
    },
    endpoint='render.json', # optional; default is render.html
    splash_url='<url>',     # optional; overrides SPLASH_URL
)

另外我们也可以生成 Request 对象,关于 Splash 的配置通过 meta 属性配置即可,代码如下:

yield scrapy.Request(url, self.parse_result, meta={
    'splash': {
        'args': {
            # set rendering arguments here
            'html': 1,
            'png': 1,
            # 'url' is prefilled from request url
            # 'http_method' is set to 'POST' for POST requests
            # 'body' is set to request body for POST requests
        },
        # optional parameters
        'endpoint': 'render.json',  # optional; default is render.json
        'splash_url': '<url>',      # optional; overrides SPLASH_URL
        'slot_policy': scrapy_splash.SlotPolicy.PER_DOMAIN,
        'splash_headers': {},       # optional; a dict with headers sent to Splash
        'dont_process_response': True, # optional, default is False
        'dont_send_headers': True,  # optional, default is False
        'magic_response': False,    # optional, default is True
    }
})

SplashRequest 对象通过 args 来配置和 Request 对象通过 meta 来配置,两种方式达到的效果是相同的。

本节我们要做的抓取是淘宝商品信息,涉及页面加载等待、模拟点击翻页等操作。我们可以首先定义一个 Lua 脚本,来实现页面加载、模拟点击翻页的功能,代码如下所示:

function main(splash, args)
  args = {
    url="https://s.taobao.com/search?q=iPad",
    wait=5,
    page=5
  }
  splash.images_enabled = false
  assert(splash:go(args.url))
  assert(splash:wait(args.wait))
  js = string.format("document.querySelector('#mainsrp-pager div.form> input').value=% d;document.querySelector('#mainsrp-pager div.form> span.btn.J_Submit').click()", args.page)
  splash:evaljs(js)
  assert(splash:wait(args.wait))
  return splash:png()
end

我们定义了三个参数:请求的链接 url、等待时间 wait、分页页码 page。然后禁用图片加载,请求淘宝的商品列表页面,通过 evaljs() 方法调用 JavaScript 代码,实现页码填充和翻页点击,最后返回页面截图。我们将脚本放到 Splash 中运行,正常获取到页面截图,如图 13-15 所示。

图 13-15 页面截图

翻页操作也成功实现,如图 13-16 所示即为当前页码,和我们传入的页码 page 参数是相同的。

图 13-16 翻页结果

我们只需要在 Spider 里用 SplashRequest 对接 Lua 脚本就好了,如下所示:

from scrapy import Spider
from urllib.parse import quote
from scrapysplashtest.items import ProductItem
from scrapy_splash import SplashRequest

script = """
function main(splash, args)
  splash.images_enabled = false
  assert(splash:go(args.url))
  assert(splash:wait(args.wait))
  js = string.format("document.querySelector('#mainsrp-pager div.form> input').value=% d;document.querySelector('#mainsrp-pager div.form> span.btn.J_Submit').click()", args.page)
  splash:evaljs(js)
  assert(splash:wait(args.wait))
  return splash:html()
end
"""

class TaobaoSpider(Spider):
    name = 'taobao'
    allowed_domains = ['www.taobao.com']
    base_url = 'https://s.taobao.com/search?q='

    def start_requests(self):
        for keyword in self.settings.get('KEYWORDS'):
            for page in range(1, self.settings.get('MAX_PAGE') + 1):
                url = self.base_url + quote(keyword)
                yield SplashRequest(url, callback=self.parse, endpoint='execute', args={'lua_source': script, 'page': page, 'wait': 7})

我们把 Lua 脚本定义成长字符串,通过 SplashRequest 的 args 来传递参数,接口修改为 execute。另外,args 参数里还有一个 lua_source 字段用于指定 Lua 脚本内容。这样我们就成功构造了一个 SplashRequest,对接 Splash 的工作就完成了。

其他的配置不需要更改,Item、Item Pipeline 等设置与上节对接 Selenium 的方式相同,parse() 回调函数也是完全一致的。

5. 运行

接下来,我们通过如下命令运行爬虫:

scrapy crawl taobao

运行结果如图 13-17 所示。

图 13-17 运行结果

由于 Splash 和 Scrapy 都支持异步处理,我们可以看到同时会有多个抓取成功的结果。在 Selenium 的对接过程中,每个页面渲染下载是在 Downloader Middleware 里完成的,所以整个过程是阻塞式的。Scrapy 会等待这个过程完成后再继续处理和调度其他请求,这影响了爬取效率。因此使用 Splash 的爬取效率比 Selenium 高很多。

最后我们再看看 MongoDB 的结果,如图 13-18 所示。

图 13-18 存储结果

结果同样正常保存到了 MongoDB 中。

6. 本节代码

7. 结语

在 Scrapy 中,建议使用 Splash 处理 JavaScript 动态渲染的页面。这样不会破坏 Scrapy 中的异步处理过程,会大大提高爬取效率。而且 Splash 的安装和配置比较简单,通过 API 调用的方式实现了模块分离,大规模爬取的部署也更加方便。

在这里构造了一个 SplashRequest 对象,前两个参数依然是请求的 URL 和回调函数,另外还可以通过 args 传递一些渲染参数,例如等待时间 wait 等,还可以根据 endpoint 参数指定渲染接口,另外还有更多的参数可以参考文档的说明:。

本节代码地址:。

https://github.com/scrapy-plugins/scrapy-splash#configuration
https://github.com/scrapy-plugins/scrapy-splash#requests
https://github.com/Python3WebSpider/ScrapySplashTest