Scrapy框架-通過scrapy_splash解析動態渲染的資料

中乘風發表於2018-07-13

前言

對於那些通過JS來渲染資料的網站,我們要解析出它的html來才能取到想要的資料,通常有兩種解決辦法:
1、通過selenim呼叫瀏覽器(如chrome firefox等)來爬取,將解析的任務交給瀏覽器。
2、通過splash來解析資料,scrapy可以直接從splash的【空間】中拿到渲染後的資料。

這裡介紹scrapy_splash

有個坑

根據它的文件,我們可以知道它依賴於Docker服務,所以你想要使用scrapy_splash就需要先安裝docker並跑起來。再根據它的文件進行安裝、啟動等操作(其實這裡有個坑):

$ docker run -p 8050:8050 scrapinghub/splash

專案啟動的時候,如果通過這個命令啟動,他其實預設給你啟動的是http://127.0.0.1:8050/,你可以用瀏覽器開啟來看,看到這個頁面就算正常啟動了

splash正常啟動頁面-通過瀏覽器訪問

所以,它的官方示例程式碼下一句程式碼(在settings.py中配置):

 SPLASH_URL = `http://192.168.59.103:8050`

你在使用的時候如果按照這句來寫,是無法連線splash的,必須寫成:

"SPLASH_URL": `http://127.0.0.1:8050`

其他的配置可以按照文件來寫。

程式碼中的使用

如果使用動態settings配置(避免影響其他爬蟲)的話,可以在具體的spider檔案中新增:

custom_settings = {
    """ 動態settings配置 """
    "SPLASH_URL": `http://127.0.0.1:8050`,
    "DOWNLOADER_MIDDLEWARES": {
    `scrapy_splash.SplashCookiesMiddleware`: 723,
    `scrapy_splash.SplashMiddleware`: 725,
    `scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware`: 810,
    },
    "SPIDER_MIDDLEWARES": {
    `scrapy_splash.SplashDeduplicateArgsMiddleware`: 100,
    },
    "DUPEFILTER_CLASS": `scrapy_splash.SplashAwareDupeFilter`,
    "HTTPCACHE_STORAGE": `scrapy_splash.SplashAwareFSCacheStorage`,
}
name = `tsinghua`
allowed_domains = [`tv.tsinghua.edu.cn`]
start_urls = [`http://tv.tsinghua.edu.cn/publish/video/index.html`]

然後再使用的時候,需要將哪個動態頁面解析,就要用SplashRequest來發起請求,而不是之前的scrapy.Request來發起,其他的如callback、meta、url都是一樣的(如果不一樣請以文件為準)下面我放出清華大學視訊站的解析程式碼:

def parse(self, response):
    """
    獲取導航連結, 自動爬取所有導航url, 交給parseList方法
    為了獲取js渲染的翻頁, 這裡用scrapy-splash的SplashRequest來構造請求, 以獲得js渲染後的的html資料
     """
    totalNav = response.css(`#nav li`)
    for i in totalNav:
        urls = i.css(`a::attr("href")`).extract_first()
        yield SplashRequest(url=parse.urljoin(response.url, urls), args={"wait": 1}, callback=self.parseList)

def parseList(self, response):
    """ 在列表頁獲取詳情頁的url以及視訊封面, 傳遞給parseDetails方法, 最終獲取視訊和封面等資訊 """
    totalUrl = response.css(`.picnewslist2.clearfix .clearfix `)
    for i in totalUrl:
        urls = parse.urljoin(response.url, i.css(`.contentwraper figcaption a::attr("href")`).extract_first())
        imagesUrl = parse.urljoin(response.url, i.css(`.picwraper img::attr("src")`).extract_first())
        yield Request(url=urls, meta={"imagesUrl": imagesUrl}, callback=self.parseDetails)

    """ 
    翻頁操作 
        藉助scrapy-splash來解析js渲染的html
        取出"上一頁, 下一頁"的頁碼, 通過re正則來匹配其中的數值
        對url進行判斷, 是否是第一頁。根據url構造不同的下一頁nexPageUrl
        最後藉助scrapy-splash繼續解析下一頁的html
    """
    nextPageList = response.css(`a.p::attr("onclick")`).extract()
    if len(nextPageList) >= 2:
        matchRule = re.search(`d+`, nextPageList[1])
        if matchRule:
            nextPageNumber = matchRule.group(0)
            thisPageRule = re.search(`index_d+`, response.url)
            if thisPageRule:
                thisPageNumber = thisPageRule.group(0).replace(`index_`, ``)
                nexPageUrl = response.url.replace(thisPageNumber, nextPageNumber)
                yield SplashRequest(url=nexPageUrl, callback=self.parseList)
            else:
                nexPageUrlJoin = `index_` + nextPageNumber
                nexPageUrl = response.url.replace(`index`, nexPageUrlJoin)
                yield SplashRequest(url=nexPageUrl, callback=self.parseList)

def parseDetails(self, response):
    """ 抽取視訊詳情, 交給對應item進行序列化 """
    imagesUrl =response.meta[`imagesUrl`]
    loaders = tsinghuaVedioItemLoader(item=tsinghuaVedioItem(), response=response)
    loaders.add_css("title", "article.article h1::text")  # 標題
    loaders.add_css("articleContent", `#play_mobile source::attr("src")`)  # 內容
    loaders.add_value("imagesUrl", imagesUrl)  # 視訊封面地址
    loaders.add_value("articleType", 2)  # 型別:視訊
    loaders.add_value("addtime", datetime.now())
    loaders.add_value("schoolName", "清華大學")
    loaders.add_value("schoolID", 6)
    items = loaders.load_item()
    yield items


相關文章