Scrapy對接Splash

在上一節我們實現了Scrapy對接Selenium抓取淘寶商品的過程,這是一種抓取JavaScript渲染頁面的方式,除了使用Selenium還有Splash同樣可以達到同樣的功能,本節我們來了解下Scrapy對接Splash來進行頁面抓取的方式。

環境準備

首先在這之前請確保已經正確安裝好了Splash並正常運行,同時安裝好了ScrapySplash庫。

開始

接下來我們首先新建一個項目,名稱叫做scrapysplashtest,命令如下:

scrapy startproject scrapysplashtestn

隨後新建一個Spider,命令如下:

scrapy genspider taobao www.taobao.comn

隨後我們可以參考ScrapySplash的配置說明進行一步步的配置,鏈接如下:github.com/scrapy-plugi

修改settings.py,首先將SPLASH_URL配置一下,在這裡我們的Splash是在本地運行的,所以可以直接配置本地的地址:

SPLASH_URL = http://localhost:8050n

如果Splash是在遠程伺服器運行的,那此處就應該配置為遠程的地址,例如如果運行在IP為120.27.34.25的伺服器上,則此處應該配置為:

SPLASH_URL = http://120.27.34.25:8050n

接下來我們還需要配置幾個Middleware,代碼如下:

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

在這裡配置了三個Downloader Middleware和一個Spider Middleware,這是ScrapySplash的核心部分,配置了它們我們就可以對接Splash進行頁面抓取,在這裡我們不再需要像對接Selenium那樣實現一個Downloader Middleware,ScrapySplash庫都為我們準備好了,直接配置即可。

接著還需要配置一個去重的類DUPEFILTER_CLASS,代碼如下:

DUPEFILTER_CLASS = scrapy_splash.SplashAwareDupeFiltern

最後還需要配置一個Cache存儲HTTPCACHE_STORAGE,代碼如下:

HTTPCACHE_STORAGE = scrapy_splash.SplashAwareFSCacheStoragen

配置完成之後我們就可以利用Splash來抓取頁面了,例如我們可以直接生成一個SplashRequest對象並傳遞相應的參數,Scrapy會將此請求轉發給Splash,Splash對頁面進行渲染載入,然後再將渲染結果傳遞迴來,此時Response的內容就是渲染完成的頁面結果了,最後交給Spider解析即可。

示例用法如下:

yield SplashRequest(url, self.parse_result,n args={n # optional; parameters passed to Splash HTTP APIn wait: 0.5,n # url is prefilled from request urln # http_method is set to POST for POST requestsn # body is set to request body for POST requestsn },n endpoint=render.json, # optional; default is render.htmln splash_url=<url>, # optional; overrides SPLASH_URLn)n

在這裡構造了一個SplashRequest對象,前兩個參數依然是請求的URL和回調函數,另外還可以通過args傳遞一些渲染參數,例如等待時間wait等,還可以根據endpoint參數指定渲染介面,另外還有更多的參數可以參考文檔的說明:github.com/scrapy-plugi

另外我們也可以生成Request對象,關於Splash的配置通過meta屬性配置即可,代碼如下:

yield scrapy.Request(url, self.parse_result, meta={n splash: {n args: {n # set rendering arguments heren html: 1,n png: 1,n # url is prefilled from request urln # http_method is set to POST for POST requestsn # body is set to request body for POST requestsn },n # optional parametersn endpoint: render.json, # optional; default is render.jsonn splash_url: <url>, # optional; overrides SPLASH_URLn slot_policy: scrapy_splash.SlotPolicy.PER_DOMAIN,n splash_headers: {}, # optional; a dict with headers sent to Splashn dont_process_response: True, # optional, default is Falsen dont_send_headers: True, # optional, default is Falsen magic_response: False, # optional, default is Truen }n})n

兩種方式達到的效果是相同的。

本節我們要做的抓取是淘寶商品信息,涉及到頁面載入等待、模擬點擊翻頁等操作,所以這裡就需要Lua腳本來實現了,所以我們在這裡可以首先定義一個Lua腳本,來實現頁面載入、模擬點擊翻頁的功能,代碼如下:

function main(splash, args)nn args = {nn url="https://s.taobao.com/search?q=iPad",nn wait=5,nn page=5nn }nn splash.images_enabled = falsenn assert(splash:go(args.url))nn assert(splash:wait(args.wait))nn 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)nn splash:evaljs(js)nn assert(splash:wait(args.wait))nn return splash:png()nnendn

在這裡我們定義了三個參數,請求的鏈接url、等待時間wait、分頁頁碼page,然後將圖片載入禁用,隨後請求淘寶的商品列表頁面,然後通過evaljs()方法調用了JavaScript代碼實現了頁碼填充和翻頁點擊,最後將頁面截圖返回。我們將腳本放到Splash中運行一下,正常獲取到了頁面截圖:

可以看到翻頁操作也成功實現,如圖所示即為當前頁碼,和我們傳入的頁碼page參數是相同的:

所以在這裡我們只需要在Spider裡面用SplashRequest對接這個Lua腳本就好了,實現如下:

from scrapy import Spidernfrom urllib.parse import quotenfrom scrapysplashtest.items import ProductItemnfrom scrapy_splash import SplashRequestnnscript = """nfunction main(splash, args)n splash.images_enabled = falsen assert(splash:go(args.url))n assert(splash:wait(args.wait))n 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)n splash:evaljs(js)n assert(splash:wait(args.wait))n return splash:html()nendn"""nnclass TaobaoSpider(Spider):n name = taobaon allowed_domains = [www.taobao.com]n base_url = https://s.taobao.com/search?q=nn def start_requests(self):n for keyword in self.settings.get(KEYWORDS):n for page in range(1, self.settings.get(MAX_PAGE) + 1):n url = self.base_url + quote(keyword)n yield SplashRequest(url, callback=self.parse, endpoint=execute, args={lua_source: script, page: page, wait: 7})n

在這裡我們把Lua腳本定義成長字元串,通過SplashRequest的args來傳遞參數,同時介面修改為execute,另外args參數里還有一個lua_source欄位用於指定Lua腳本內容,這樣我們就成功構造了一個SplashRequest,對接Splash的工作就完成了。

其他的配置不需要更改,Item、Item Pipeline等設置同上節對接Selenium的方式,同時parse回調函數也是完全一致的。

接下來我們通過如下命令運行爬蟲:

scrapy crawl taobaon

由於Splash和Scrapy都支持非同步處理,我們可以看到同時會有多個抓取成功的結果,而Selenium的對接過程中每個頁面渲染下載過程是在Downloader Middleware裡面完成的,所以整個過程是堵塞式的,Scrapy會等待這個過程完成後再繼續處理和調度其他請求,影響了爬取效率,因此使用Splash爬取效率上比Selenium高出很多。

因此,在Scrapy中要處理JavaScript渲染的頁面建議使用Splash,這樣不會破壞Scrapy中的非同步處理過程,會大大提高爬取效率,而且Splash的安裝和配置比較簡單,通過API調用的方式也實現了模塊分離,大規模爬取時部署起來也更加方便。

本節源代碼:github.com/Python3WebSp

作者:崔慶才 Python愛好者社區專欄作者 授權原創發布,請勿轉載,謝謝。

出處:Scrapy對接Splash

配套視頻教程:Python3爬蟲三大案例實戰分享:貓眼電影、今日頭條街拍美圖、淘寶美食 Python3爬蟲三大案例實戰分享

公眾號:Python愛好者社區(微信ID:python_shequ)

小編個人微信:tsdatajob ,來不急回復的,可以加小編微信溝通,謝謝。

推薦閱讀:

Scrapy中xpath如何提取細節標籤
【記錄】Scrapy模擬登錄cookie失效問題
關於在pycharm中配置導入路徑的問題?
如何利用python爬取靜態網頁數據?

TAG:scrapy | Python | Python3x |