文章目录
-
- 爬取动态页面
-
- (一)Splash 渲染引擎
-
- 1. render.html 端点
- 2. execute 端点
- 3. 常用属性与方法
-
- (1)Splash 对象的属性
- (2)Splash 对象的方法
- (二)安装 Scrapy-Scrapy
-
- 1. 安装 splash 服务器
- 2. 安装 Scrapy-Splash 库
- 3. plash Lua 脚本
- (三)在 Scrapy 中使用 Splash
-
- 1. 页面分析
- 2. 新建项目
- 3. 添加配置
- 4. 编写爬虫
-
- (1)编写 quotes.py
- (2)修改 item.py
- (3)修改 pipeline.py
- 5. 运行爬虫
爬取动态页面
在之前章节中,我们爬取的都是静态页面中的信息,静态页面的内容始终不变,爬取相对容易,但在现实中,目前绝大多数网站的页面都是动态页面,动态页面中的部分内容是浏览器运行页面中的 JavaScript 脚本动态生成的,爬取相对困难,这一章来学习如何爬取动态页面。
先来看一个简单的动态页面的例子,
(一)Splash 渲染引擎
Splash 是 Scrapy 官方推荐的 JavaScript 渲染引擎,它是使用 Webkit 开发的轻量级无界面浏览器,提供基于 HTTP 接口的 JavaScript 渲染服务,支持以下功能:
- 为用户返回经过渲染的 HTML 页面或页面截图。
- 并发渲染多个页面。
- 关闭图片加载,加速渲染。
- 在页面中执行用户自定义的 JavaScript 代码。
- 执行用户自定义的渲染脚本(lua),功能类似于 PhantomJS。
Splash功能丰富,包含多个服务端点,由于篇幅有限,这里只介绍其中两个最常用的端点:
- render.html 提供 JavaScript 页面渲染服务。
- execute:执行用户自定义的渲染脚本(lua),利用该端点可在页面中执行 JavaScript 代码。
Splash文档地址:http://splash.readthedocs.io/en/latest/api.html
1. 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 代码,在页面渲染前执行 |
这里仅列出部分常用参数,详细内容参见官方文档。
2. 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
3. 常用属性与方法
接下来,看一下splash对象常用的属性和方法。
(1)Splash 对象的属性
属性 | 描述 |
---|---|
splash.args | 用户传入参数的表,通过该属性可以访问用户传入的参数,如splash.args.url、splash.args.wait |
splash.js_enabled | 用于开启/禁止 JavaScript 渲染,默认为 true |
splash.images_enabled | 用于开启/禁止图片加载,默认为 true |
(2)Splash 对象的方法
-
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:evaljs 方法
调用格式:splash:evaljs(snippet)
方法功能:在当前页面下,执行一段JavaScript代码,并返回最后一句表达式的值。
-
splash:runjs 方法
调用格式:splash:runjs(snippet)
方法功能:在当前页面下,执行一段JavaScript代码,与evaljs方法相比,该函数只执行 JavaScript 代码,不返回值。
-
splash:url 方法
调用格式:splash:url()
方法功能:获取当前页面的 url。
-
splash:html 方法
调用格式:splash:html()
方法功能:获取当前页面的HTML文本。
-
splash:get_cookies 方法
调用格式:splash:get_cookies()
方法功能:获取全部Cookie信息。
(二)安装 Scrapy-Scrapy
掌握了 Splash 渲染引擎的基本使用后,我们继续学习如何在 Scrapy 中调用 Splash 服务,Python 库的 scrapy-splash 是非常好的选择。
Scrapy-Splash 的安装分为两部分:一个是 Splash 服务的安装,具体通过 Docker 来安装服务,运行服务会启动一个 Splash 服务,通过它的接口来实现JavaScript 页面的加载;另外一个是 Scrapy-Splash 的 Python 库的安装,安装后就可在 Scrapy 中使用 Splash 服务了。
1. 安装 splash 服务器
$ docker pull scrapinghub/splash
$ docker run -d --name splash -p 8050:8050 scrapinghub/splash
2. 安装 Scrapy-Splash 库
使用 pip 安装 scrapy-splash:
$ pip install scrapy-splash
3. plash Lua 脚本
在安装了 splash 服务器之后,我们需要在 Virtualbox 设置中添加端口转发:

运行 splash 服务后,通过web页面访问服务的8050端口(可以在VirtualBox虚拟机的浏览器中访问,也可以在本地浏览器中访问)
http://127.0.0.1:8050/
即可看到其web页面,如下图:
上面有个输入框,默认是http://google.com,我们可以换成想要渲染的网页如:https://www.baidu.com,然后点击
Render me
按钮开始渲染。
(三)在 Scrapy 中使用 Splash
这里我们可以观察一个典型的供我们练习爬虫技术的网站:quotes.toscrape.com/js/。
1. 页面分析
在浏览器中打开 http://quotes.toscrape.com/js,显示如下图所示。页面中有10条名人名言,每一条都包含在一个
<div class="quote">
元素中。
现在,我们在
scrapy shell
环境下尝试爬取页面中的名人名言:
$ scrapy shell http://quotes.toscrape.com/js/
...
>>> response.css('div.quote')
[]
从结果看出,爬取失败了,在页面中没有找到任何包含名人名言的
<div class="quote">
元素。这些
<div class="quote">
就是动态内容,从服务器下载的页面中并不包含它们(所以我们爬取失败),浏览器执行了页面中的一段 JavaScript 代码后,它们才被生成出来,如下图所示:
阅读代码可以了解页面动态生成的细节,所有名人名言信息被保存在数组 data 中,最后的 for 循环迭代 data中的每项信息,使用 document.write 生成每条名人名言对应的
<div class="quote">
元素。
上面是动态网页中最简单的一个例子,数据被硬编码于 JavaScript 代码中,实际中更常见的是 JavaScript 通过HTTP 请求跟网站动态交互获取数据(AJAX),然后使用数据更新 HTML 页面。爬取此类动态网页需要先执行页面中的 JavaScript 代码渲染页面,再进行爬取。因此,我们进行爬取页面信息之前首先需要用 splash 渲染页面。
2. 新建项目
在项目环境中讲解 scrapy-splash 的使用,创建一个 Scrapy 项目,取名为scrapy_splash:
$ scrapy startproject scrapy_quotes
然后,在 scrapy_splash 项目目录下使用
scrapy genspider
命令创建Spider:
$ cd scrapy_quotes/scrapy_quotes
$ scrapy genspider quotes quotes.toscrape.com
在 scrapy.cfg 同级目录,创建 bin.py,用于启动 Scrapy 项目,内容如下:
# 在项目根目录下新建:bin.py
from scrapy.cmdline import execute
# 第三个参数是爬虫程序名
execute(['scrapy', 'crawl', 'quotes',"--nolog"])
创建好的项目树形目录如下:
$ tree
.
├── bin.py
├── scrapy.cfg
└── scrapy_quotes
├── __init__.py
├── items.py
├── middlewares.py
├── pipelines.py
├── settings.py
└── spiders
├── __init__.py
└── quotes.py
3. 添加配置
在项目配置文件 settings.py 中对 scrapy-splash 进行配置,添加内容如下:
# Splash服务器地址
SPLASH_URL = 'http://127.0.0.1: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,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
ITEM_PIPELINES = {
'scrapy_quotes.pipelines.ScrapyQuotesPipeline': 300,
}
编写 Spider 代码过程中,使用 scrapy_splash 调用 Splash 服务非常简单,scrapy_splash 中定义了一个 SplashRequest 类,用户只需使用
scrapy_splash.SplashRequest
(替代
scrapy.Request
)提交请求即可。下面是 SplashRequest 构造器方法中的一些常用参数:
参数 | 功能 |
---|---|
url | 与 scrapy.Request 中的 url 相同,也就是待爬取页面的 url |
headers | 与 scrapy.Request 中的 headers 相同 |
cookies | 与 scrapy.Request 中的 cookies 相同 |
args | 传递给 Splash 的参数(除url以外),如wait、timeout、images、js_source等 |
cache_args | 如果 args 中的某些参数每次调用都重复传递并且数据量较大,此时可以把该参数名填入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 的地址 |
4. 编写爬虫
(1)编写 quotes.py
在这个案例中,我们只需使用 Splash 的 render.html 端点渲染页面,再进行爬取即可实现 QuotesSpider,代码如下:
import scrapy
from scrapy_splash import SplashRequest
from ..items import QuotesItem
class QuotesSpider(scrapy.Spider):
name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
def start_requests(self):
for url in self.start_urls:
yield SplashRequest(url, args={'images': 0, 'timeout': 60})
def parse(self, response):
authors = response.css('div.quote small.author::text').extract()
quotes = response.css('div.quote span.text::text').extract()
item = QuotesItem()
item['authors'] = authors
item['quotes'] = quotes
yield from (dict(zip(['author', 'quote'], item)) for item in zip(authors, quotes))
next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
if next_url:
url = response.urljoin(next_url)
yield SplashRequest(url, args={'images': 0, 'timeout': 60})
上述代码中,使用 SplashRequest 提交请求,在 SplashRequest 的构造器中无须传递 endpoint 参数,因为该参数默认值便是
'render.html'
。使用
args
参数禁止
Splash
加载图片,并设置渲染超时时间。
另外,上述代码除了以上这种写法,还可以进行如下编写:
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': 8})
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()
item = QuotesItem()
item['authors'] = author
item['quotes'] = quote
yield item
next_url = response.css('li.next > a::attr(href)').extract_first()
if href:
url = response.urljoin(next_url)
yield SplashRequest(url, args={'images': 0, 'timeout': 8})
(2)修改 item.py
import scrapy
class QuotesItem(scrapy.Item):
authors = scrapy.Field()
quotes = scrapy.Field()
(3)修改 pipeline.py
import json
class ScrapyQuotesPipeline:
def __init__(self):
self.f = open("quotes.json", 'wb')
def process_item(self, item, spider):
# 读取item中的数据 并换行处理
content = json.dumps(dict(item), ensure_ascii=False) + ',\n'
self.f.write(content.encode('utf=8'))
return item
def close_spider(self, spider):
# 关闭文件
self.f.close()
5. 运行爬虫
运行 bin.py,等待 1 分钟,就会生成文件
quotes.json
。
上述文章内容如有错误,欢迎各位读者在评论区留言!