Splash渲染引擎

Splash渲染引擎

​ Splash是Scrapy官方推荐的JavaScript渲染,它是使用WebKit开发的轻量级无界面浏览器,提供基于HTTP接口的JavaScript渲染服务,支持以下功能:

  • 为用户返回经过渲染的HTML页面或页面截图。
  • 并发渲染多个页面
  • 关闭图片加载,加速渲染
  • 在页面中执行用户自定义的JavaScript代码。
  • 执行用户自定义的渲染脚本(lua),功能类似于PhantomJS。

首先安装Splash,通过Docker安装。

$ docker run -p 8050:8050 -p 8051:8051 scrapinghub/splash

或者通过docker-compose.yml

splash:
  container_name: splash
  image: scrapinghub/splash
  restart: always
  ports:
    - "8050:8050"
    - "8051:8051"

详情可见:https://www.jianshu.com/p/1ab7f03f4e5a

安装完成后,在本机的8050和8051端口开启Splash服务。

​ Splash功能丰富,包含多个服务端点。这里只介绍两个最常用的端点:

  • render.html:提供JavaScript页面渲染服务
  • execute:执行用户自定义的渲染脚本(lua),利用该端点可在页面中执行JavaScript代码。

​ Splash文档地址:http://splash.readthedocs.io/en/latest/api.html

render.html端点

​ JavaScript页面渲染服务是Splash中最基础的服务。

服务端点 render.html
请求地址 http://localhost:8050/render.html
请求方式 GET/POST
返回类型 html

​ render.html端点支持的参数如下表所示。

参数 是否必选 类型 描述
url 必选 string 需要渲染页面的url
timeout 可选 float 渲染页面超时时间
proxy 可选 string 代理服务器地址
wait 可选 float 等待页面渲染的时间
images 可选 integer 是否下载图片,默认为1
js_source 可选 string 用户自定义JavaScript代码,在页面渲染前执行

​ 这里仅列出部分常用参数,详细内容参见官方文档。

​ 下面是使用requests库调用render.html端点服务对页面:http://quotes.toscrape.com/js/进行渲染的示例代码。

>>> import requests
>>> from scrapy.selector import Selector
>>> splash_url = 'http://localhost:8050/render.html'
>>> args = {'url':'http://quotes.toscrape.com/js', 'timeout':'5', 'image':0}
>>> response  =  requests.get(splash_url, params=args)
>>> sel = Selector(response)
>>> texts = sel.css('div.quote span.text::text').extract()
>>> for text in texts:
...     print text
...
“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
“It is our choices, Harry, that show what we truly are, far more than our abilities.”
“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”
“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”
“Try not to become a man of success. Rather become a man of value.”
“It is better to be hated for what you are than to be loved for what you are not.”
“I have not failed. I've just found 10,000 ways that won't work.”
“A woman is like a tea bag; you never know how strong it is until it's in hot water.”
“A day without sunshine is like, you know, night.”

​ 在上述代码中,依据文档中的描述设置参数url、timeout、images,然后发送HTTP请求到服务接口地址。从运行结果看出,页面渲染成功,我们爬取到了页面中的10条名人名言。

execute端点

​ 在爬取某些页面时,我们想在页面中执行一些用户自定义的JavaScript代码,例如,用JavaScript模拟点击页面中的按钮,或调用页面中的JavaScript函数与服务器交互,利用Splash的execute端点提供的服务可以实现这样的功能。

服务端点 execute
请求地址 http://localhost:8050/execute
请求方式 POST
返回类型 自定义

execute端点支持的参数如下表所示。

参数 必选/可选 类型 描述
lua_source 必选 string 用户自定义的lua脚本
timeout 可选 float 渲染页面超时时间
proxy 可选 string 代理服务器地址

​ 我们可以将execute端点的服务看做一个可用lua语言编程的浏览器,功能类似于PhantomJS。使用时需传递一个用户自定义的lua脚本给Splash,该lua脚本中包含用户想要模拟的浏览器行为,例如:

  • 打开某URL地址的页面
  • 等待页面加载及渲染
  • 执行JavaScript代码
  • 获取HTTP响应头部
  • 获取Cookie

下面是使用requests库调用execute端点服务的示例代码:

>>> import requests
>>> import json
>>> lua_script = '''
... function main(splash)
...     splash:go("http://example.com")
...     splash:wait(0.5)
...     local title = splash:evaljs("document.title")
...     return {title=title}
... end
... '''
>>> splash_url = 'http://localhost:8050/execute'
>>> headers = {'content-type':'application/json'}
>>> data = json.dumps({'lua_source':lua_script})
>>> response = requests.post(splash_url, headers=headers, data=data)
>>> response.content
'{"title": "Example Domain"}'
>>> response.json()
{u'title': u'Example Domain'}

​ 用户自定义的lua脚本中必须包含一个main函数作为程序入口,main函数被调用时会传入一个splash对象(lua中的对象),用户可以调用该对象上的方法操纵Splash。例如,在上面的例子中,先调用go方法打开某页面,再调用wait方法等待页面渲染,然后调用evaljs方法执行一个JavaScript表达式,并将结果转换为相应的lua对象,最终Splash根据main函数的返回值构造HTTP响应返回给用户,main函数的返回值可以是字符串,也可以是lua中的表(类似于Python字典),表会被编码成json串。

​ 接下来,看一下splash对象常用的属性和方法。

  • splash.args属性:用户传入参数的表,通过该属性可以访问用户传入的参数,如splash.args.url、splash.args.wait。
  • splash.js_enabled属性:用于开启/禁止JavaScript渲染,默认为True。
  • splash.images_enabled属性:用于开启/禁止图片加载,默认为True。
  • splash:go方法:splash:go{url, baseurl=nil, headers=nil, http_method="GET", body= nil, formdata=nil}类似于在浏览器中打开某url地址的页面,页面所需资源会被加载,并进行JavaScript渲染,可以通过参数指定HTTP请求头部、请求方法、表单数据等。
  • splash:wait方法:splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}等待页面渲染,time参数为等待的秒数。
  • splash:evajs方法:splash:evajs(snippet)在当前页面下,执行一段JavaScript代码,并返回最后一句表达式的值。
  • splash:runjs方法:splash:runjs(snippet)在当前页面下,执行一段JavaScript代码,与evajs方法相比,该函数只执行Javasc代码,不返回值。
  • splash:url方法:splash:url()获取当前页面的url。
  • splash:html方法:splash:html()获取当前页面的HTML文本。
  • splash:get_cookies方法:splash:get_cookies()获取全部Cookie信息

在Scrapy中使用Splash

​ 掌握了Splash渲染引擎的基本使用后,我们继续学习如何在Scrapy中调用Splash服务,Python库的scrape-splash是非常好的选择。

​ 使用pip安装scrape-splash。

$ pip install scrapy-splash

​ 在项目环境中讲解scrapy-splash的使用,创建一个Scrapy项目,取名为splash_examples:

$ scrapy startproject splash_examples

​ 首先在项目配置文件settings.py中对scrappy-splash进行配置,添加内容如下:

# Splash服务器地址
SPLASH_URL = 'http://localhost:8050'

# 开启Splash的两个下载中间件并调整HttpCompressionMiddleware的次序
DOWNLOADER_MIDDLEWARES = {
    'scrapy_splash.SplashCookiesMiddleware': 723,
    'scrapy_splash.SplashMiddleware': 725,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
# 设置去重过滤器
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

# 用来支持cache_args(可选)
SPIDER_MIDDLEWARES = {
    'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}

​ 编写Spider代码过程中,使用scrapy_splash调用Splash服务非常简单,scrapy_splash中定义了一个SplashRequest类,用户只需要使用scrapy_splash.SplashRequest(替代scrapy.Request)提交请求即可。下面是SplashRequest构造器方法中的一些常用参数。

  • url:与scrapy.Request中的url相同,也就是待爬取页面的URL(注意,不是Splash服务器地址)
  • headers:与scrapy.Request中的headers相同。
  • cookies:与scrapy.Request中的cookies相同。
  • args:传递给Splash的参数(除URL以外),如wait、timeout、images、js_source等。
  • cache_args:如果args中的某些参数每次调用都重复传递并且数据量较大(例如一段JavaScript代码),此时可以把该参数名填入cache_args列表中,让Splash服务器缓存该参数,如(SplashRequest(url,args={'js_source':js, 'wait':0.5}, cache_args=['js_source']))。
  • endpoint:Splash服务端点,默认为'render.html',即JavaScript页面渲染服务,该参数可以设置为'render.json'、'render.har'、'render.png'、'render.jpeg'、'execute'等,有些服务端点的功能我们没有讲解,详细内容可以查阅文档。
  • splash_url:Splash服务器地址,默认为None,即使用配置文件中的SPLASH_URL的地址。

​ 现在,大家已经对如何在Scrapy中使用Splash渲染引擎爬取动态页面有了一定了解,接下来我们在已经配置了Splash使用环境的splash_examples项目中完成两个实战项目。

项目实战:爬取toscrape中的名人名言

项目需求

​ 爬取网站http://quotes.toscrape.com/js中的名人名言信息。

页面分析

​ 该网站的页面已在本章开头部分分析过,大家可以看相关内容。

编码实现

​ 首先,在splash_examples项目目录下使用scrape gensipder命令创建Spider:

$ scrapy genspider quotes quotes.toscrape.com

​ 在这个案例中,我们只需使用Splash的render.html端点渲染页面,再进行爬取即可实现QuotesSpider,代码如下:

# -*- coding: utf-8 -*-
import scrapy
from scrapy_splash import SplashRequest


class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/js/']

    def start_requests(self):
        for url in self.start_urls:
            yield SplashRequest(url, args={'images': 0, 'timeout': 3})
        pass

    def parse(self, response):
        for sel in response.css('div.quote'):
            quote = sel.css('span.text::text').extract_first()
            author = sel.css('small.author::text').extract_first()
            yield {'quote': quote, 'author': author}
        href = response.css('li.next>a::attr(href)').extract_first()
        if href:
            url = response.urljoin(href)
            yield SplashRequest(url, args={'images': 0, 'timeout': 3})

        pass

​ 上述代码中,使用SplashRequest提交请求,在SplashRequest的构造器中无须传递endpoint参数,因为该参数默认值便是'render.html'。使用args参数禁止Splash加载图片,并设置渲染超时时间。

​ 运行爬虫,观察结果:

$ scrapy crawl quotes -o quotes.csv
名人名言

成功爬取了10个页面中的100条名人名言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容