• 第七部分(二) 动态渲染页面爬取(Splash的安装和使用、Scrapy的安装、Docker的安装、Scrapy-Splash的安装,在Windows及Linux平台的安装)


    二、 Splash的使用

    Splash是一个JavaSscript渲染服务,一个带有HTTP API的轻量级浏览器,同时对接了Python中的Twisted和QT库。使用它同样可以实现动态渲页面的抓取。

    Splash可以实现下面的这些功能:
    异步方式处理多个网页渲染过程;
    获取渲染后的页面的源代码或截图;
    通过关闭图片渲染或者使用Adblock规则来加快页面渲染速度;
    可执行特定的 JavaScript 脚本;
    可通过 Lua 脚本来控制页面渲染过程;
    获取渲染的详细过程并通过HAR(HTTP Archive)格式呈现。


    在使用之前先进行Scrapy及Scrapy-Splash的安装。

    1、 Scrapy的安装
    Scrapy是非常强大的爬虫框架,依赖的库较多,至少要依赖的库有 Twisted 14.0 、lxml 3.4 和 pyQpenSSL 0.14。不同的平台依赖的库也不相同,在安装前确保一些基本库已经正确安装好。

    对于Windows,Python是使用Anaconda安装的,可使用conda命令安装Scrapy,如果不是,需要一步步安装Scrapy。
    conda install Scrapy

    CentOS7安装 Scrapy:
    首先确保依赖库已经安装,运行如下命令:
    yum groupinstall -y development tools
    yum install -y epel-release libxslt-devel libxml2-devel openssl-devel

    没有报错,接着用 pip 安装 Scrapy:
    pip3 install Scrapy
    安装的时候报下面这个错误:
    No matching distribution found for Twisted>=13.1.0 (from Scrapy)
    下载Twisted这个安装包:
    wget https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2
    tar -xjvf Twisted-18.9.0.tar.bz2
    cd Twisted-18.9.0/
    python3 setup.py install
    执行这条命令报下面的错误:
    src/twisted/test/raiser.c:4:20: 致命错误:Python.h:没有那个文件或目录
    解决方法,安装 python-dev:
    yum install python-devel
    再次执行安装命令,安装Twisted成功:
    python3 setup.py install
    接下来再一次用 pip 命令安装 Scrapy,这次提示安装成功:
    pip3 install Scrapy

    安装完成后在命令行执行 scrapy 后可以看到可用命令选项。

    下面是一些与scrapy相关的网站:
    scrapy官方网站:https://scrapy.org
    scrapy官方文档:https://docs.scrapy.org
    scrapy PyPI:https://pypi.python.org/pypi/Scrapy
    scrapy GitHub:https://github.com/scrapy/scrapy
    scrapy 中文文档:https://scrapy-chs.readthedocs.io/zh_CN/0.24/
    python wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

    2、 安装Docker
    在安装Splash之前,还需要安装Docker才行。Docker是一种部署方式,叫作Docker集群部署。可将爬虫制作为Docker镜像,在主机上安装了Docker,就可直接运行爬虫,不用担心环境配置、版本问题。

    Docker是一种容器技术,可以将应用和环境等进行打包,形成一个独立的、类似于iOS 的App形式的“应用” 。这个应用可以直接被分发到任意一个支持Docker 的环境中, 通过简单的命令即可启动运行。Docker 是一种最流行的容器化实现方案,和虚拟化技术类似,它极大地方便了应用服务的部署;又与虚拟化技术不同,它以一种更轻量的方式实现了应用服务的打包。使用Docker ,可以让每个应用彼此相互隔离,在同一台机器上同时运行多个应用,不过它们彼此之间共享同一个操作系统。Docker 的优势在于,它可以在更细的粒度上进行资源管理,也比虚拟化技术更加节约资源。

    用Docker大规模部署爬虫系统会大提高效率。所以需要先安装Docker容器。
    与Docker相关的网站如下:
    官方网站:https://www.docker.com
    GitHub:https://github.com/docker
    Docker Hub: https://hub.docker.com
    官方文档:https://docs.docker.com
    DaoCloud: http://www.daocloud.io
    中文社区:http://www.docker.org.cn
    中文教程:http://www.runoob.com/docker/docker-tutorial.html
    推荐书籍:https://yeasy.gitbooks.io/docker_practice

    windows 10系统安装Docker,在官方网站上下载 Docker for Windows安装包进行安装。安装完成后在命令行输入 docker 会看到命令选项信息,表示安装成功。

    CentOS7 安装Docker,使用下面的一条命令即可完成,安装过程较慢:
    curl -sSL https://get.docker.com/ | sh

    3、 Scrapy-Splash的安装
    Scrapy-Splash是一个Scrapy中支持JavaScript渲染的工具。安装分为两部分。一个是Splash服务的安装,通过Docker安装,安装后会启动一个Splash服务,可通过它的接口来实现JavaScript页面的加载。另一个是Scrapy-Splash的Python库的安装,安装后可在Scrapy中使用Splash服务。

    与Scrapy-Splash相关的连接如下:
    Github: https://github.com/scrapy-plugins/scrapy-splash
    PyPI: https://pypi.python.org/pypi/scrapy-splash
    使用说明:https://github.com/scrapy-plugins/scrapy-splash#configuration
    Splash官方文档:http://splash.readthedocs.io

    安装splash,Scrapy-Splash要使用HTTP API进行页面渲染,所以需要安装Splash来提供渲染服务,通过docker安装,在用docker安装前,需要在命令行先登录docker,登录命令是:
    docker login,根据提示输入用户名和密码,用户名和密码是官方网站上注册的用户名和密码,注意用户名不是注册时的邮箱。
    windows下登录成功后执行安装命令如下:
    docker run -p 8050:8050 scrapinghub/splash

    安装完成后可以看到Splash已经在8050端口上运行的输出结果,这时打开 http://localhost:8050/ 可以看到Splash的主面。如图2-1所示。
    图2-1 Splash运行主页面
    图2-1 Splash运行主页面

    Splash可以直接安装在运程服务器上,在运程服务器上以守护态运行Splash即可,这样在中断与运程服务器连接后,不会终止Splash服务的运行。命令如下所示,命令中的 -d 参数表示将Docker容器以守护态运行。
    docker run -d -p 8050:8050 scrapinghub/splash

    3.1、 在CentOS7安装Scrapy-Splash,提示下面错误
    docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
    解决办法:
    systemctl daemon-reload
    systemctl restart docker
    service docker status # 查看运行情况
    systemctl status docker.service # 执行这个命令也是查看运行情况

    配置镜像加速器,加速文件是在 /etc/docker/daemon.json,如果没有就创建该文件,执行下面这条命令:
    curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
    重启docker:
    systemctl restart docker
    运行安装命令:
    docker run -p 8050:8050 scrapinghub/splash
    执行完成安装命令后,在 windows 的浏览器地址栏可以正常访问 http://192.168.64.50:8050,此时可以正常使用 splash。

    不同平台配置镜像加速方法参考:
    http://guide.daocloud.io/dcs/daocloud-9153151.html

    4、 Scrapy-Splash的安装
    安装完Splash后,接下来就安装其对应的Python库,使用pip命令进行安装,如下所示:
    pip3 install scrapy-splash

    5、 Splash简单使用
    在图2-1的Splash主页面中,右上方有的输入框中输入 https://www.baidu.com,点击 Render me开始渲染,如果如图2-2所示。
    图2-2 Splash渲染百度页面
    图2-2 Splash渲染百度页面

    可以看出,网页的返回结果呈现了渲染图、HAR加载统计数据、网页源代码。在HAR结果中可以看到,Splash执行了整个网页的渲染过程,包括CSS、JavaScript的加载等过程,呈现的页面与在浏览器中得到的结果是一样的。这个过程是由图2-1中Splash运行主页面中的一段Lua语言写的脚本控制的。代码如下:
    function main(splash, args)
    assert(splash:go(args.url))
    assert(splash:wait(0.5))
    return {
    html = splash:html(),
    png = splash:png(),
    har = splash:har(),
    }
    end

    这段Lua语言脚本的意思是:
    splash:go(args.url):先调用go()方法加载页面;
    splash:wait(0.5):再调用wait()方法等待一定时间
    return返回源代码、截图和HAR信息。

    通过上面了解到,Splash通过Lua脚本控制页面的加载过程,加载过程完全模拟浏览器,最后返回各种样式的结果。

    6、 Splash Lua脚本
    用Splash来模拟类似Chrome、PhangtomJS的操作。先了解下Splash Lua脚本的入口和执行方式。

    6.1、 Lua入口及返回值
    看下面这个简单的Lua脚本:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    splash:wait(0.5)
    local title=splash:evaljs("document.title")
    return {title=title}
    end

    将段代码粘贴到图2-1中的代码编辑区域后点击Render me! 进行测试。结果是返回网页的标题。代码中 evaljs() 方法中传入的是JavaScript脚本,document.title返回的是网页标题,接着将这个结果赋值给一个title变量,随后将其返回。

    代码定义的方法名称叫作main(),这个名称是固定的,Splash默认会调用这个方法。该方法的返回值可以是字典形式,也可以是字符串形式,最后都会转化为 Splash HTTP Response,例如下面这样:
    function main(splash)
    return {hello="python"} # 返回字典形式的内容
    end
    function main(splash)
    return "python" # 返回字符串形式的内容
    end

    6.2、 异步处理
    Splash支持异步处理,但是这里没有显式指明回调方法,其回调的跳转是在Splash内部完成的。示例如下:
    function main(splash, args)
    local example_urls = {"www.baidu.com", "www.taobao.com", "www.zhihu.com"}
    local urls = args.urls or example_urls
    local results = {}
    for index, url in ipairs(urls) do
    local ok, reason = splash:go("https://" .. url)
    if ok then
    splash:wait(2)
    results[url] = splash:png()
    end
    end
    return results
    end

    运行这段代码后返回的是3个站点的截图。代码中的wait()方法相当于Python中的sleep(),参数是等待秒数。在splash执行到此方法时,它会转而去处理其他任务,然后在指定的时间过后再回来继续处理。要注意的是,Lua脚本中字符串拼接和Python不同,它使用的是(..)操作符,而不是(+)。这里在加载时做了异常检测。go()方法会返回加载页面的结果状态,如果页面出现 4xx 或 5xx 状态码,ok 变量就为空,就不会返回加载后的图片。Lua 脚本语法参考:
    http://www.runoob.com/lua/lua-basic-syntax.html

    7、 Splash对象属性
    前面的Lua代码中,main()方法的第一个参数是splash,这个对象非常重要,类似于Selenium中的WebDriver对象,可以调用它的一些属性和方法来控制加载过程。下面来看下它的属性。

    7.1、 args属性
    获取加载时配置的参数,如URL,如果为GET请求,可获取GET请求参数;如果为POST请求,可获取表单提交的数据。Splash也支持使用第二个参数直接作为args,例如这样:

    function main(splash, args)
    local url = args.url
    end
    第二个参数 args 相当于 splash.args属性,上面代码等价于这样:
    function main(splash, args)
    local url = splash.args.url
    end

    7.2、 js_enabled属性
    Splash的JavaScript执行开关,可设置为true或false来控制是否执行JavaScript代码,默认是true。如果设置为false时,调用evaljs()方法执行JavaScript时就会抛出异常。如下所示:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    splash.js_enabled=false
    local title=splash:evaljs("document.title")
    return {title=title}
    end

    执行这段代码,抛出下面的错误提示信息:
    {
    "description": "Error happened while executing Lua script",
    "type": "ScriptError",
    "info": {
    "source": "[string "function main(splash, args) ..."]",
    "js_error_message": null,
    "splash_method": "evaljs",
    "line_number": 4,
    "message": "[string "function main(splash, args) ..."]:4: unknown JS error: None",
    "type": "JS_ERROR",
    "error": "unknown JS error: None"
    },
    "error": 400
    }

    7.3、 resource_timeout属性
    设置加载的超时时间,单位是秒。设置为 0 或 nil (相当于Python中的None)表示不检测超时。这个属性适合用在加载较慢的网页,如果超过某个时间无响应,忽略异常即可。示例如下:

    function main(splash)
    splash.resource_timeout = 0.1
    assert(splash:go('https://www.taobao.com'))
    return splash.png()
    end

    执行代码后的错误信息如下所示:
    {
    "description": "Error happened while executing Lua script",
    "type": "ScriptError",
    "info": {
    "line_number": 3,
    "source": "[string "function main(splash) ..."]",
    "type": "LUA_ERROR",
    "message": "Lua error: [string "function main(splash) ..."]:3: network5",
    "error": "network5"
    },
    "error": 400
    }

    7.4、 images_ enabled属性
    设置图片是否可加载,默认是可加载。禁用该属性可节省网络流量并提高网页加载速度。要注意的是,禁用图片加载可能会影响JavaScript渲染,因为外层DOM节点的高度会受影响,进而影响DOM节点的位置。因此,JavaScript对图片节点有操作的话,其执行就会受到影响。

    另外要注意的是,Splash使用了缓存。如果一开始加载出来了图片,然后禁用图片加载,再重新加载页面,之前加载好的图片可能还会显示出来,这时重启Splash即可。示例如下:

    function main(splash, args)
    splash.images_enabled = false
    assert(splash:go('https://www.jd.com'))
    return {png=splash:png()}
    end

    7.5、 plugins _enabled属性
    控制浏览器插件(如Flash插件)是否开启,默认是false,不开启。可使用下面方式开启或关闭该属性:
    splash.plugins_enabled = true/false

    7.6、 scroll_position属性
    设置此属性可控制页面上下或左右滚动。是一个常用的属性,示例如下:
    function main(splash, args)
    assert(splash:go('https://www.taobao.com'))
    splash.scroll_position = { y=400 }
    return { png=splash:png() }
    end

    代码中 y=400 控制页面向下滚动400像素值。要控制左右滚动,可传入 x 参数,如下所示:
    splash.scroll_position = {x=100, y=200}

    8、 Splash对象的方法
    Splash对象有下面这些方法。

    8.1、 go()方法
    请求某个链接,可模拟GET和POST请求,支持传入请求头、表单等数据,其用法如下:
    ok, reason = splash:go{url, baseurl=nil, headers=nil, http_method="GET", body=nil, formdata=nil}
    go()方法参数说明:
    url: 请求的URL。
    baseurl: 可选参数,默认为空,表示资源加载相对路径。
    headers: 可选参数,默认为空,表示请求头。
    http_method: 可选参数,默认为GET,同时支持POST。
    body: 可选参数,默认为空,发POST请求时的表单数据,使用的Content-type为application/json
    formdata: 可选参数,默认为空,POST的时候的表单数据,使用的Content-type为application/x-www-form-urlencode。

    该方法返回结果是结果ok和原因reason的组合,如果ok为空,代表网页加载出现错误,此时reason变量中包含了错误的原因。否则表示页面加载成功。示例如下:

    function main(splash, args)
    local ok, reason = splash:go{"http://httpbin.org/post", http_method="POST", body="name=Michael"}
    if ok then
    return splash:html()
    end
    end

    这段代码模拟POST请求,并传入POST的表单数据,如果成功,则返回页面源代码。运行结果如下所示,在下面结果中有"name": "Michael",表示成功实现了POST请求并发送表单数据。
    <html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">{
    "args": {},
    "data": "",
    "files": {},
    "form": {
    "name": "Michael"
    },
    "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en,*",
    "Content-Length": "12",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "Origin": "null",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
    },
    "json": null,
    "origin": "183.221.39.12, 183.221.39.12",
    "url": "https://httpbin.org/post"
    }
    </pre></body></html>

    8.2、 wait()方法
    控制页面的等待时间,用法如下:
    ok, reason = splash:wait{time, cancel_on_redirect=false, cancel_on_error=true}
    wait()方法参数说明:
    time: 等待的秒数。
    cancel_on_redirect:可选参数,默认为false,表示如果发生了重定向就停止等待,并返回重定向结果。
    cancel_on_error:可选参数,默认为false,表示如果发生加载错误,就停止等待。
    返回结果同样是结果ok和原因reason的组合。示例如下:
    function main(splash)
    splash:go("https://www.taobao.com")
    splash:wait(2)
    return {html=splash:html()}
    end

    8.3、jsfunc()方法
    该方法可以直接调用JavaScript定义的方法,但是所调用的方法需要用双中括号包围,这相当于实现了 JavaScript 方法到 Lua 脚本的转换。例如下面这样:

    function main(splash, args)
    local get_div_count = splash:jsfunc([[
    function () {
    var body = document.body;
    var divs = body.getElementsByTagName('div');
    return divs.length;
    }
    ]])
    splash:go("https://www.taobao.com")
    return ("There are %s DIVs"):format(get_div_count())
    end
    运行结果如下:
    There are 562 DIVs

    这段代码中,先声明一个JavaScript方法,在页面加载成功后调用此方法计算页面中的div节点个数。有关 JavaScript 到 Lua 脚本的转换细节,参考官方文档:
    https://splash.readthddocs.io/en/stable/scripting-ref.html#splash-jsfunc

    8.4、 evaljs()方法
    可执行JavaScript代码并返回最后一条JavaScript语句的返回结果,用法如下:
    result = splash:evaljs(js)
    比如用来获取标题的方法:
    local title = splash:evaljs("document.title")

    8.5、 runjs()方法
    该方法可执行JavaScript代码,它与evaljs()的功能类似,但是更偏向于执行某些动作或声明某些方法。例如:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    assert(splash:runjs("foo = function (){ return 'michael' }"))
    local result = splash:evaljs("foo()")
    return result
    end

    这里用runjs()先声明一个JavaScript定义的方法,然后通过evaljs()来调用得到的结果。输出是:michael

    8.5、 autoload()方法
    可设置页面访问时自动加载的对象,用法如下所示:
    ok, reason = splash:autoload{source_or_url, source=nil, url=nil}
    参数说明如下:
    source_or_url:JavaScript代码或者JavaScript库连接。
    source:JavaScript代码。
    url:JavaScript库连接

    该方法只负责加载JavaScript代码或库,不执行任何操作。如果要执行操作,可调用evaljs()或runjs()方法。示例如下:
    function main(splash, args)
    splash:autoload([[
    function get_document_title(){
    return document.title;
    }
    ]])
    splash:go("https://www.zhihu.com")
    return splash:evaljs("get_document_title()")
    end

    这段代码中调用了autoload()方法声明一个JavaScript方法,之后通过 evaljs() 方法执行此 JavaScript 方法,运行结果是:
    知乎 - 有问题,上知乎

    此外,还可用 autoload() 方法加载某些库,如 jQuery,示例如下:
    function main(splash, args)
    assert(splash:autoload("https://code.jquery.com/jquery-1.12.4.min.js"))
    assert(splash:go("https://www.sina.com.cn"))
    local version = splash:evaljs("$.fn.jquery")
    return 'JQuery version:' .. version
    end

    运行结果是:JQuery version:1.7.2,这个版本号是新浪网站的。

    8.6、 call_later()方法
    该方法通过设置定时任务和延迟时间来实现任务延时执行,并且可以在执行前通过 cancel() 方法重新执行定时任务。示例如下:
    function main(splash, args)
    local snapshots = {}
    local timer = splash:call_later(function()
    snapshots["a"] = splash:png()
    splash:wait(1.0)
    snapshots["b"] = splash:png()
    end, 0.2)
    splash:go("https://www.taobao.com")
    splash:wait(3)
    return snapshots
    end

    这段代码中设置了一个定时任务,0.2 秒时获取网页截图,然后等待1秒,即 1.2秒时再次获取网页截图,访问的是淘宝首页。

    8.7、 http_get()方法
    可模拟发送HTTP的GET请求,用法如下所示:
    response = splash:http_get{url, headers=nil, follow_redirects=true}
    参数说明如下:
    url:请求URL。
    headers:可选参数,默认为空,请求头。
    follow_redirects:可选参数,表示是否启动重定向,默认为true。
    示例如下:
    function main(splash, args)
    local treat = require("treat")
    local response = splash:http_get("http://httpbin.org/get")
    return {
    html=treat.as_string(response.body),
    url=response.url,
    status = response.status
    }
    end

    运行结果如下所示:
    Splash Response: Object
    html: String (length 343)
    {
    "args": {},
    "headers": {
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en,*",
    "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
    },
    "origin": "183.221.39.12, 183.221.39.12",
    "url": "https://httpbin.org/get"
    }
    status: 200
    url: "http://httpbin.org/get"

    8.8、 http_post()方法
    模拟发送POST请求,比http_get()方法多一个body参数,用法如下:
    response = splash:http_post{url, headers=nil, follow_redirects=true, body=nil}
    参数说明如下:
    url:请求的URL
    headers:可选参数,默认为空,请求头。
    follow_redirects:可选参数,表示是否启动自动重定向,默认为true。
    body:可选参数,即表单数据,默认为空。
    示例如下所示:
    function main(splash, args)
    local treat = require("treat")
    local json = require("json")
    local response = splash:http_post{"http://httpbin.org/post",
    body=json.encode({job="michael"}),
    headers={["content-type"]="application/json"}
    }
    return {
    html=treat.as_string(response.body),
    url=response.url,
    status=response.status
    }
    end

    运行结果如下所示,从输出可看出,成功模拟提交了POST请求并发送了表单数据(body数据):
    Splash Response: Object
    html: String (length 521)
    {
    "args": {},
    "data": "{"job": "michael"}",
    "files": {},
    "form": {},
    "headers": {
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en,*",
    "Content-Length": "18",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1"
    },
    "json": {
    "job": "michael"
    },
    "origin": "183.221.39.12, 183.221.39.12",
    "url": "https://httpbin.org/post"
    }
    status: 200
    url: "http://httpbin.org/post"

    8.9、 set_content()方法
    设置页面的内容,用法示例如下所示:
    function main(splash)
    assert(splash:set_content("<html><body><h1>helle world!</h1></body></html>"))
    return splash:png()
    end

    运行这段代码,输出是含有 helle world! 字样的页面截图。

    8.10、 html()方法
    获取网页小源代码,即简单又常用的方法。
    function main(splash, args)
    splash:go("http://httpbin.org.get")
    return splash:html()
    end

    8.11、 png()方法
    获取PNG格式的网页截图,示例如下:
    function main(splash, args)
    splash:go("https://www.taobao.com")
    return splash:png()
    end

    8.12、 jpeg()方法
    获取JPEG格式的网页截图,示例如下:
    function main(splash, args)
    splash:go("https://www.taobao.com")
    return splash:jpeg()
    end

    8.13、 har()方法
    获取页面加载过程描述,示例如下:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    return splash:har()
    end

    运行结果会显示页面加载过程中每个请求记录的详情。

    8.13、 url()方法
    获取当前正在访问的URL,示例如下:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    return splash:url()
    end
    运行结果是:https://www.baidu.com/

    8.14、 get_cookies()方法
    获取当前页面的Cookies,示例如下:
    function main(splash, args)
    splash:go("https://www.baidu.com")
    return splash:get_cookies()
    end
    运行结果大致内容如下所示:
    Splash Response: Array[7]
    0: Object
    domain: ".baidu.com"
    expires: "2087-04-12T05:03:05Z"
    httpOnly: false
    name: "BAIDUID"
    path: "/"
    secure: false
    value: "FFA9DF2148D3AED2188D59DFC19D6733:FG=1"
    1: Object
    domain: ".baidu.com"
    expires: "2087-04-12T05:03:05Z"
    httpOnly: false
    name: "BIDUPSID"
    path: "/"
    secure: false
    value: "FFA9DF2148D3AED2188D59DFC19D6733"
    2: Object
    domain: ".baidu.com"
    expires: "2087-04-12T05:03:05Z"
    httpOnly: false
    name: "PSTM"
    path: "/"
    secure: false
    value: "1553478538"

    8.15、 add_cookie()方法
    为当前页面添加Cookie,使用方法如下:
    cookies = splash:add_cookie{name, value, path=nil, domain=nil, expires=nil, httpOnly=nil, secure=nil}
    各个参数代码Cookie的各个属性。
    function main(splash, args)
    splash:add_cookie{"sessioned", "12345michael", "/", domain="http://example.com"}
    splash:go("http://example.com/")
    return {splash:html(), splash:get_cookies()}
    end

    8.16、 clear_ cookies()方法
    清除所有的Cookies,示例如下:
    function main(splash)
    splash:go("https://www.baidu.com/")
    splash:clear_cookies()
    return splash:get_cookies()
    end
    代码中先用clear_cookies()方法清除cookies,再用get_cookies()方法获取,此时获取到cookies为空的。

    8.17、 get_viewport_size()方法
    获取当前浏览器页面的大小,即宽高,示例如下:
    function main(splash)
    splash:go("https://www.baidu.com/")
    return splash:get_viewport_size()
    end
    运行结果如下所示:
    Splash Response: Array[2]
    0: 1024
    1: 768

    8.18、 set_viewport_size()方法
    设置当前浏览器页面的大小,即宽高,用法如下:
    splash:set_viewport_size(sidth, height)
    需要访问一个宽度自适应的页面才能看到效果:
    function main(splash)
    splash:set_viewport_size(400, 700)
    assert(splash:go("https://www.baidu.com"))
    return splash:png()
    end

    8.19、 set_viewport_full()方法
    设置浏览器全屏显示,示例如下:
    function main(splash)
    splash:set_viewport_full()
    assert(splash:go("https://www.baidu.com"))
    return splash:png()
    end

    8.20、 set_user_agent()方法
    设置浏览器的User_Agent,示例如下:
    function main(splash)
    splash:set_agent('Splash')
    splash:go("http://httpbin.org/get")
    return splash:html()
    end
    将浏览器的User-Agent设置为Splash,运行结果中有:"User-Agent": "Splash"。可以看到User-Agent被成功设置。

    8.21、 set_custom_headers()方法
    设置请求头,示例如下:
    function main(splash)
    splash: set_custom_headers({
    ["User-Agent"] = "Splash",
    ["Site"] = "Splash",
    })
    splash:go("http://httpbin.org/get")
    return splash:html()
    end
    代码中设置了请求头中的User-Agent 和 site 属性,运行结果中有:"Site": "Splash", "User-Agent": "Splash",表示设置
    成功。

    8.22、 select()方法
    该方法可以选中符合条件的第一个节点,如果有多个节点符合条件,则只会返回一个,其参数是CSS选择器。示例如下:
    function main(splash)
    splash:go("https://www.baidu.com")
    cfz = splash:select("#kw")
    cfz:send_text("Python")
    splash:wait(1)
    return splash:png()
    end
    代码中go()方法访问百度,选中搜索框后调用 send_text() 方法填入关键字后返回网页截图。

    8.23、 select_all()方法
    选中所有符合条件的节点,参数是CSS选择器。示例如下:
    function main(splash)
    local treat = require('treat')
    assert(splash:go("http://quotes.toscrape.com/"))
    assert(splash:wait(0.5))
    local texts = splash:select_all('.quote .text')
    local results = {}
    for index, text in ipairs(texts) do
    results[index] = text.node.innerHTML
    end
    return treat.as_array(results)
    end

    代码中用CSS选择器选中了节点的正文内容,随后遍历所有节点,将将其中文本获取下来。运行结果省略。

    8.24、 mouse_click()方法
    模拟鼠标点击操作,传入的参数为坐标值 x 和 y。也可以直接选中某个节点,然后调用此方法,示例如下:
    function main(splash)
    splash:go("https://www.baidu.com")
    input = splash:select("#kw") --获取输入框
    input: send_text("Splash") --输入文本Splash
    submit = splash:select("#su") --获取点击按钮
    submit:mouse_click() --点击
    splash:wait(3)
    return splash:png()
    end

    在这段代码中,选中页面的输入框,输入文本,然后选中“提交”按钮,接着调用 mouse_click() 方法提交查询,并返回截图。运行结果省略。

    更详细的 Splash API操作,Splash 对象的所有API操作,参考官方文档:
    https://splash.readthedocs.io/en/stable/scripting-ref.html
    针对页面元素的 API 操作,参考下面这个官方文档:
    https://splash.readthedocs.io/en/stable/scripting-element-object.html

    9、 Splash API调用
    Splash提供的一些 HTTP API 接口,可利用Splash渲染页面,并与Python程序结合使用抓取 JavaScript 渲染的页面。调用这些接口传入相应的参数即可。

    9.1、 render.html接口
    用于获取 JavaScript 渲染的页面的HTML代码,接口地址是Splash 的运行地址加此接口名称,例如 http://localhost:8050/render.html。
    可用 curl 进行测试,在命令行下输入下面的代码:
    curl http://localhost:8050/render.html?url=https://www.baidu.com
    这行代码给此接口传递一个 url 参数来指定渲染的 URL,返回结果即页面渲染后的源代码。用Python来实现,代码如下:
    import requests
    url = "http://localhost:8050/render.html?url=https://www.baidu.com"
    response = requests.get(url)
    print(response.text)
    通过这几行代码就可输出百度页面渲染后的源代码。

    此接口还可以指定其它参数,如通过 wait 参数指定等待秒数,在需要确保页面完全加载出来时,可增加等待时间。例如:
    import requests
    url = "http://localhost:8050/render.html?url=https://www.taobao.com&wait=5"
    response = requests.get(url)
    print(response.text)
    这样的请求时间会变长,这里是等待5秒过后获取淘宝的源代码。注意参数是以 &wait=5 这种方式出现在 URL 中。

    此外,接口还支持代理设置、图片加载设置、Headers 设置、请求方法设置,详细情况参考官方文档:
    https://splash.readthedocs.io/en/stable/api.html#render-html

    9.2、 render.png接口
    获取网页截图,需要指定图形的大小,可通过 width 和 height 来控制宽高,返回的是 PNG 格式的图片二进制数据。示例如下:
    curl http://localhost:8050/render.png?url=https://www.taobao.com&wait=3&width=1000&height=700

    这行代码中使用 render.pgn 接口,设置了等待参数 wait ,另外通过 width 和 height 设置了页面大小。

    利用 Python 实现,可将返回的二进制数据保存为PNG格式的图片,如下面方法所示:
    import requests
    url = "http://localhost:8050/render.png?url=https://www.taobao.com&wait=3&width=1000&height=700"
    response = requests.get(url)
    with open('taobao.png', 'wb') as f:
    f.write(response.content)
    通过wait参数设置页面渲染的程度,并且返回渲染后的截图。

    更多详细参数设置参考官方文档:
    https://splash.readthedocs.io/en/stable/api.html#render-png

    9.3、 render.jpeg接口
    与 render.png 接口类似,返回的是 JPEG 格式的图片二进制数据。该接口多了 quality 参数设置图片质量。

    9.4、 render.har接口
    获取页面加载的 HAR 数据,用法如下:
    curl http://localhost:8050/render.har?url=https://www.jd.com&wait=5
    该行代码返回结果非常多,是一个 JSON 格式的数据,其中包含页面加载过程中的 HAR 数据。

    9.5、 render.json接口
    该接口包含了前面接口的所有功能,返回结果是 JSON 格式,用法如下:
    curl http://localhost:8050/render.json?url=https://www.baidu.com
    输出结果如下所示:
    {'requestedUrl': 'https://www.baidu.com/', 'geometry': [0, 0, 1024, 768], 'url': 'https://www.baidu.com/', 'title': '百度一下,你就知道'}

    从输出可知,这里以 JSON 形式返回相应请求数据。还可以传入不同的参数控制其返回结果。比如传入 html=1,返回结果会增加源代码数据;传入 png=1,返回结果会增加页面 PNG 截图数据;传入 har=1,会获得页面 HAR 数据。例如:
    curl http://localhost:8050/render.json?url=https://www.baidu.com&html=1&har=1
    这种方式返回的结果会包含网页源代码和 HAR 数据。

    更多参数设置,参考官方文档:
    https://splash.readthedocs.io/en/stable/api.html#render-json

    9.6、 execute接口
    这个是最重要也是最强大的接口,前面的很多 Splash Lua 脚本的操作,用此接口便可实现与 Lua 脚本的对接。render.html 和render.png 等这些接口对于一般的 JavaScript 渲染页面可满足要求,但要实现一些交互操作就无能为力,这时就需要使用 execute接口。先写一个简单的 Lua 脚本,直接返回数据:
    function main(splash)
    return 'hello python'
    end

    接下来将此脚本转化为 URL 编码后的字符串(在页面上执行这段脚本可进行转化),拼接到 execute 接口后面,如下所示:
    curl http://localhost:8050/execute?lua_source=function+main%28splash%29%0D%0A++++return+%27hello+python%27%0D%0Aend
    运行结果输出: hello python

    通过 lua_source 参数传递转码后的 Lua 脚本,通过 execute 接口获取了最终脚本的执行结果。上面这些操作可用 Python 来实现,代码如下:

    import requests
    from urllib.parse import quote
    lua = '''
    function main(splash)
    return 'hello python'
    end
    '''
    url = 'http://localhost:8050/execute?lua_source=' + quote(lua)
    response = requests.get(url)
    print(response.text)

    运行结果输出为:hello python
    这段代码中用三引号将 Lua 脚本包括起来,用 urllib.parse 模块的 quote() 方法将脚本进行 URL 转码,随后构造 Splash 请求 URL,将其作为 lua_source 参数传递,这样运行结果就会显示 Lua 脚本执行后的结果。再来看另一个实例:

    import requests
    from urllib.parse import quote
    lua = '''
    function main(splash, args)
    local treat = require("treat")
    local response = splash: http_get("http://httpbin.org/get")
    return {
    html = treat.as_string(response.body),
    url = response.url,
    status = response.status
    }
    end
    '''
    url = "http://localhost:8050/execute?lua_source=" + quote(lua)
    response = requests.get(url)
    print(response.text)

    输出结果如下所示:
    {"html": "{ "args": {}, "headers": { "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en,*", "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/602.1 (KHTML, like Gecko) splash Version/9.0 Safari/602.1" },
    "origin": "183.221.39.19, 183.221.39.19", "url": "https://httpbin.org/get" } ",
    "status": 200, "url": "http://httpbin.org/get"}
    从输出可知,返回结果是 JSON 形式,成功获取了请求的 URL、状态码和网页源代码。通过这种方式,可将 Lua 脚本与 Python 进行对接,所有网页的动态渲染、模拟点击、表单提交、页面滑动、延时等待后的一些结果均可自由控制,获取页面源码和截图都能实现。

    利用 Python 和 Splash 可以实现 Javascript渲染的页面抓取,同时有强大的渲染功能,并且不需要浏览器即可渲染,使用很方便,不像 Selenium 需要使用浏览器。
  • 相关阅读:
    css3
    如何去把数据渲染到页面
    js中的正则
    12.4
    react-router HashRouter和BrowserHistory的区别
    react 路由使用react-router-dom
    react 中的 三大重要属性state refs props
    在Vue中如何快速实现小球动画
    模块化 require.js 入门教学(前端必看系列)
    如何把设计稿中px值转化为想要的rem值
  • 原文地址:https://www.cnblogs.com/Micro0623/p/10643999.html
Copyright © 2020-2023  润新知