scrapy 動態網頁處理——爬取鼠繪海賊王最新漫畫


簡介

scrapy是基於python的爬蟲框架,易於學習與使用。本篇文章主要介紹如何使用scrapy爬取鼠繪漫畫網海賊王最新一集的漫畫。

源碼參見:https://github.com/liudaolufei/crawl-comic

網站分析

鼠繪海賊王網站網址為:http://www.ishuhui.com/comics/anime/1

漫畫鏈接無法直接從原始網頁中得到,需要點擊對應的話數,鏈接才會顯示出來,如下圖所示:

獲取鏈接后即可獲得海賊王漫畫的網頁地址,網頁如下:

原始的網頁沒有漫畫的圖片鏈接,需要點擊圖中的“連頁模式”,之后網頁才會顯示漫畫的具體內容,此時的漫畫內容也是動態加載的,隨着窗口不斷往下,圖片會一張張加載出來。

工具

scrapy: 爬蟲框架

splash: 動態網頁處理

環境安裝

scrapy安裝

已安裝anaconda,python3.7版本。

創建scrapy虛擬環境並激活

1 conda create -n scrapy
2 conda activate scrapy

使用pip安裝scrapy以及用於與splash交互的scrapy-splash

1 pip install scrapy scrapy-splash

在使用scrapy的ImagePipeline的時候需要第三方庫PIL,安裝PIL

1 pip install pillow

splash安裝

splash服務是通過docker啟用的,所以要先安裝docker,docker安裝參見:https://docs.docker.com/install/

使用docker安裝splash

1 docker pull scrapinghub/splash

啟用splash服務

1 docker run -p 8050:8050 scrapinghub/splash

scrapy爬蟲

生成scrapy項目並生成爬蟲

1 scrapy startproject crawlComics
2 cd crawlComics
3 scrapy genspider shuhui http://www.ishuhui.com/anime/1

此時在spiders文件夾下有了shuhui.py,這是爬蟲的基礎模板。

修改基礎設置(setting.py)

使用splash的設定

 1  # Splash服務器地址
 2  SPLASH_URL = 'http://localhost:8050'
 3  # 開啟Splash的兩個下載中間件並調整HttpCompressionMiddleware的次序
 4  DOWNLOADER_MIDDLEWARES = {
 5      'scrapy_splash.SplashCookiesMiddleware': 723,
 6      'scrapy_splash.SplashMiddleware': 725,
 7      'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 727,
 8  }
 9  # 設置去重過濾器
10  DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'

user-agent

1 USER_AGENT = "Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko)"

使用ImagePipelines保存圖片的相關設定

1 # 圖片存儲位置
2 IMAGES_STORE = "/home/luoheng/comics/"
3 # 啟用ImagesPipeline來下載圖片
4 ITEM_PIPELINES = {
5     'crawlComics.pipelines.RenameImagesPipeline': 500, 
6 }

添加item

item是scrapy中傳輸數據的基本單位,可以簡單的理解為字典。

scrapy的ImagePipelines是為下載圖片專門設計的,它接受一個包含鍵值“image_urls”和“images”的item。ImagePipelines在得到item后,會自動根據image_urls中包含的鏈接下載對應的圖片,並放置到上文設定的IMAGE_STORE中。

故為了使用ImagePipelines,在items.py中添加一個符合條件的item:

1 class ImageItem(scrapy.Item):
2     image_urls = scrapy.Field()
3     images = scrapy.Field()
4     # store picture names
5     image_names = scrapy.Field()

前兩項是為了滿足ImagePipelines的要求設置的,最后一項用於設置下載圖片的名字,因為ImagePipelines默認使用hash值對圖片進行命名,不具可讀性。

實現爬蟲

爬蟲主要的工作流程

1. 在http://www.ishuhui.com/anime/1頁面點擊最新漫畫按鈕,返回響應結果

2. 從響應結果中獲取海賊王最新漫畫的具體網址

3. 在最新漫畫的網址中點擊“連頁模式”,並將網頁拉到最下方,以使所有圖片的鏈接加載出來,返回響應結果

4. 從響應結果中獲取圖片的鏈接,使用ImagePipelines下載

導入item與splash

1 from scrapy_splash import SplashRequest
2 from ..items import ImageItem

SplashRequest用於與splash服務進行交互,在這里主要用了splash的“execute”功能,它接受的幾個參數如下:

1 SplashRequest(url, callback=self.parse, endpoint="execute", args={"lua_source": lua_script})

url是原始網址,callback是回調函數,即處理返回的response的函數,endpoint指明使用的功能,這里使用的是“execute”,即執行腳本的功能,最后一個args用於指定用於執行的腳本,這里的腳本是用lua語言寫成的,在Python中以字符串形式存在。

點擊最新漫畫

 1 click_latest_comic = """
 2 function main(splash)
 3     # 打開網頁
 4     splash:go(splash.args.url)
 5     # 等待網頁加載
 6     splash:wait(2)
 7     # 點擊最新漫畫(由class ant-tag-red識別)
 8     splash:runjs("document.getElementsByClassName('ant-tag-red')[0].click()")
 9     # 等待網頁加載
10     splash:wait(0.1)
11     # 返回網頁結果
12     return splash:html()
13 end
14 """
15 # 返回點擊最新漫畫后的響應結果
16 yield SplashRequest(url, callback=self.show_all, endpoint="execute", args={"lua_source": click_latest_comic})

打開具體網址,並加載所有漫畫圖片

show_all_pictures = """
function main(splash)
    # 打開網頁
    splash:go(splash.args.url)
    # 等待網頁加載
    splash:wait(2)
    # 點擊連頁模式(由class z-page識別)
    splash:runjs("document.getElementsByClassName('z-page')[0].click()")
    # 等待網頁加載
    splash:wait(0.1)
    # 將網頁拉到最下方,使所有圖片的鏈接加載出來
    splash:runjs("window.scrollTo(0, document.body.scrollHeight)")
    # 等待網頁加載
    splash:wait(0.1)
    # 返回響應結果
    return splash:html()
end
"""

def show_all(self, response):
    # 獲取漫畫具體網址
    target = response.css(".m-comics-num-link").xpath("@url").extract_first()
    # target是相對網址,它的前綴是http://www.hanhuazu.cc
    new_url = "http://www.hanhuazu.cc" + target
    # 返回已經加載了所有圖片鏈接的網頁
    yield SplashRequest(new_url, endpoint="execute", args={"lua_source": show_all_pictures}) 

使用ImageItem保存圖片鏈接並傳送給ImagePipelines下載

 1 def parse(self, response):
 2     # 所有圖片
 3     comics = response.css("div img")
 4     # src屬性中保存所有圖片鏈接,alt屬性保存圖片名字
 5     comics_picture, comics_name = comics.xpath("@src").extract(), comics.xpath("@alt").extract()
 6     # 構建ImageItem並返回給ImagePipelines
 7     images = ImageItem()
 8     # 保存圖片鏈接
 9     images["image_urls"] = comics_picture
10     # 保存圖片鏈接與圖片名字的映射,用於下載圖片的命名
11     images["image_names"] = dict(zip(comics_picture, comics_name))
12     # 返回后,ImagePipelines會自動下載圖片
13     return images

修改ImagePipelines,覆蓋默認命名

 1 class RenameImagesPipeline(ImagesPipeline):
 2     """to rename the images properly"""
 3 
 4 
 5     def process_item(self, item, spider):
 6         # add names
 7         self.item_names = item["image_names"]
 8         info = self.spiderinfo
 9         requests = arg_to_iter(self.get_media_requests(item, info))
10         dlist = [self._process_request(r, info) for r in requests]
11         dfd = DeferredList(dlist, consumeErrors=1)
12         return dfd.addCallback(self.item_completed, item, info)
13 
14     def file_path(self, request, response=None, info=None):
15         return self.item_names[request.url]

ImagesPipeline的file_path用於計算圖片的命名,但是由於圖片名字保存在item的image_names屬性中,而file_path並不直接接受item參數,所以需要將item["image_names"]保存到self.item_names中,如此file_path才可訪問到名字。

因此將ImagesPipeline的process_item方法的代碼完整復制過來,並在最前面將image_names保存到self.item_names當中,在file_path方法中只用簡單調用self.item_names[request.url]即可獲取圖片名字。

運行爬蟲

1 scrapy crawl shuhui

完成后,在comics文件夾中可以看到爬取的圖片。

總結

以爬取鼠繪海賊王漫畫為例,本文簡單介紹了使用scrapy+splash處理動態網頁的方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM