崔庆才python3爬虫-13章 Scrapy框架的使用-Spider对接Splash


崔庆才python3爬虫-13章 Scrapy框架的使用-Spider对接Splash

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

准备工作

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

创建项目

1
2
3
4
5
首先新建一个项目,名 为 scrapysplashtest,命令如下所示:
scrapy startproject scrapysplashtest

新建一个Spider,命令如下所示:
scrapy genspider taobao www.taobao.com

添加配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  可以参考Scrapy-Splash的配置说明进行一步步的配置,链接如下:https://github.com/scrapyplugins/scrapy-splash#configurationo
修改 settings.py,配置SPLASH_URL 在这里我们的Splash是在本地运行的,所以可以直接配置本地的地址:
SPLASH_URL = 'http://localhost:8050'

如果Splash是在远程服务器运行的,那此处就应该配置为远程的地址。例如运行在IP为120.27.34.25的服务器上,则此处应该配置为:
SPLASH_URL = 'http://12O.27.34.25:8050'

还需要配置几个Middleware,代码如下所示:
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downlomdermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
1
2
3
4
5
6
7
这里配置了三个Downloader Middleware和一个Spider Middleware, 这是Scrapy-Splash的核心部分。我们不再需要像对接Selenium那样实现一个Downloader Middleware, Scrapy-Splash库都为我们准备好了,直接配置即可。

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

最后配置一个Cache存储HTTPCACHE_STORACE,代码如下所示:
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

新建请求

SplashRequest

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

我们来看一个示例,如下所示:
yield SplashRequest(uil, self.parse_resuIt,
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
)
1
这里构造了一个SplashRequest对象,前两个参数依然是请求的URL和回调函数。另外我们还可以通过args传递一些渲染参数,例如等待时间wait等,还可以根据endpoint参数指定渲染接口。更多参数可以参考文档说明:https://github.eom/scrapy-plugins/scrapy-splash#requestso

Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
另外我们也可以生成Request对象, Splash的配置通过m eta属性配置即可,代码如下:
yield scrapy.Request(url, self.parse_resuIt, 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 diet 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, defauIt is True
}
})
1
2
SplashRequest对象通过args来配置和Request对象通过meta来配置,两种方式达到的效果是相同的。
本节我们要做的抓取是淘宝商品信息,涉及页面加载等待、模拟点击翻页等操作。我们可以首先定义一个Lua脚本,来实现页面加载、模拟点击翻页的功能,代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 = st ring, forma t( "document. querySelec to:r('#mains:rp叩ager 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中运行,正常获取到页面截图

翻页操作也成功实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
我们只需要在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(l, 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})
1
2
3
4
我们把Lua脚本定义成长字符串,通过SplashRequest的args来传递参数,接口修改为execute
另外, args参数里还有一个lua_source字段用于指定Lua脚本内容。这样我们就成功构造了一个SplashRequest,对接Splash的工作就完成了。

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

运行

1
2
3
4
scrapy crawl taobao  

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

结语

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

没有伞的孩子,必须努力奔跑!

Typewriter Mode** 已开启。

可以在视图菜单中关闭

不再显示关闭

本文标题:崔庆才python3爬虫-13章 Scrapy框架的使用-Spider对接Splash

文章作者:TTYONG

发布时间:2020年03月22日 - 17:03

最后更新:2022年03月21日 - 13:03

原始链接:http://tianyong.fun/%E5%B4%94%E5%BA%86%E6%89%8Dpython3%E7%88%AC%E8%99%AB-13%E7%AB%A0(13.9)%20%20Scrapy%E6%A1%86%E6%9E%B6%E7%9A%84%E4%BD%BF%E7%94%A8-%20Scrapy%E5%AF%B9%E6%8E%A5Splash.html

许可协议: 转载请保留原文链接及作者。

多少都是爱
0%