當前位置:
首頁 > 知識 > 漫畫喵的100行Python代碼逆襲

漫畫喵的100行Python代碼逆襲

點擊上方

Python開發

」,選擇「置頂公眾號」


關鍵時刻,第一時間送達!



小喵的嘮叨話:這次的博客,講的是使用python編寫一個爬蟲工具。為什麼要寫這個爬蟲呢?原因是小喵在看完《極黑的布倫希爾特》這個動畫之後,又想看看漫畫,結果發現各大APP都沒有資源,最終好不容易找到一個網站可以看,但是由於網速太渣,看起來額外的費勁。這時候如果能提前下載下來就好了。


先上項目地址(github):https://github.com/miaoerduo/cartoon-cat 。歡迎大家隨時

fork

star

和指教。


原因就是這樣,作為技術喵,任何問題都不能阻礙一顆愛漫畫的心。所以問題就來了,挖掘機技修哪家強?


在bing上搜索Python、爬蟲框架。找到大家常用的框架。


Scrapy似乎是個很不錯的選擇。至於相對於其他框架的優點,小喵沒有細查,至少這個框架是之前聽過的。但是在實現的時候發現有一些問題,scrapy不能直接抓取動態的頁面。小喵需要抓取的網站的漫畫都是使用Ajax生成的。需要自己分析各種數據,這個有點麻煩。


那麼有沒有可以渲染頁面的工具呢?像瀏覽器一樣的?有。


這裡介紹兩個工具:


PhantomJs,可以理解是一個瀏覽器。不過它沒有界面,我們可以通過js的代碼模擬用戶的行為。這就要求了解它的api並有js基礎了。


Selenium,這是個瀏覽器自動化測試框架。它依賴於瀏覽器(這個瀏覽器也可以是PhantomJs),通過Selenium可以模擬用戶的行為。而且有Python介面,所以相對簡單一些。


我們這個爬蟲使用selenium + phantomjs來實現。

喲,這個爬蟲軟體應該有個響噹噹的名字。。。就叫

漫畫喵

吧,英文名

Cartoon Cat


下面我們一點點的介紹這個爬蟲的實現過程吧。


一、初生-環境搭建


小喵這裡選用Python作為開發語言,框架是selenium。原因是python經常用來寫爬蟲,selenium可以用來模擬用戶行為,PhantomJs是可選的,不過小喵最終會在一個伺服器上運行,所以也是需要的。


為了不影響本機上的python,我們還需要使用virtualenv來創建一個獨立的python環境。具體步驟如下:


1、安裝virtualenv


virtualenv是一個常用的用來創建python環境的工具。小喵用這個有兩個原因,一是為了不污染本機的環境,二是在本機直接安裝庫的時候出了一個許可權的問題。


virtualenv的安裝十分簡單,使用pip工具就可以安裝。

  1. pip install virtualenv


待程序執行結束,你就會開心的發現自己已經有了virtualenv這個工具了。


2、創建python環境


virtualenv的使用非常的方便。


建立新的運行環境: 

virtualenv 

<

env

-

name

>


進入相應的獨立環境: 

source 

<

env

-

path

>

/bin/

activate


執行完第一個指令後,就會創建成功一個python環境,執行第二個指令後,就會發現命令行的起始位置有變化。這時候python、pip等工具就變成使用這個新環境的了,當然也可以使用 

which python

 來查看。


3、安裝selenium


進入新環境後,pip安裝的依賴庫都會在新環境中安裝,不會影響主機自身的python。使用pip 安裝selenium:

  1. pip install selenium


至此,我們的基本環境就搭建完了。


4、安裝PhantomJs


這個只在從官網上下載就可以:http://phantomjs.org/download.html

小喵的本地實驗環境是Mac,所以下載了Mac版本。解壓之後就可以使用。


二、尋覓-搜資源


小喵想看的這個漫畫貌似各大網站都沒有資源,在費了九牛二虎之力後,終於找到了一個網站!http://www.tazhe.com/mh/9170/。


每個網站的結構都不相同,因此都需要定製一套爬蟲程序。本文的爬蟲就只能針對這個漫畫網站使用,喵粉們需要爬其他網站的話,需要自己做相應的修改。


三、分析-資源解析


這裡需要解析兩個頁面,一個是漫畫的首頁,比如前面的:http://www.tazhe.com/mh/9170/


另一個就是具體章節的頁面。


1,首頁


為了減小圖片的大小,小喵把窗口做了縮放。首頁大致是這個樣子。


圖1 漫畫首頁


各類信息十分的明了。我們關注的就是下面的漫畫列表。通過Chrome強大的

審查元素

的功能,我們立刻就能定位到章節的位置。(

對著感興趣的位置- >右鍵 -> 審查

 就能找到)



圖2 章節的節點


可以看到,章節所在的區域的 

id

是 

play_0

,學過前端的童鞋都應該知道,一個頁面中 

id

通常唯一標示一個節點。因此如果我們能夠獲取這個頁面的話,查找 

id

為 

play_0

的節點就能一下子縮小搜索範圍。


而每個章節的信息都是一個 

a

標籤,標籤的 

href

是對應章節的具體網址,標籤的文本部分是章節名。這樣相對關係就得出了: 

div

#play_0 > ul > li > a


首頁的分析就到此結束。


2、章節頁面


我們隨意打開一個具體章節的頁面。比如:http://www.tazhe.com/mh/9170/1187086.html


引入眼帘的是一個很乾凈的頁面(簡直是漫畫界的清流,好多漫畫網站上全部是廣告)。


我們把

滑鼠放在圖片這個區域->右鍵->審查


咦,我們的右鍵怎麼按不了?


其實呢,這個現象在小說網站上遇到的機會會更多。當我們看到比較優美的文字或是炫酷的圖片,都會下意識的選中->右鍵->保存。而很多時候,這些資源都是有版權的。並不應該隨意的傳播(狠狠的打了自己的臉/(ㄒoㄒ)/~~)。因此限制滑鼠右鍵會是一個很簡單卻有效的辦法。


那麼我們如何繞過這個陷阱呢?


很簡單,我們不用右鍵即可。打開瀏覽器的開發者工具選項,找到elements這個選項。可以看到一個複雜的結構(其實和上面審查元素之後的結果一樣)。之後不斷的選中標籤,當標籤被選中時,左側頁面中對應的位置會有藍色。多試幾次,最終就能找到對應的位置。



圖3 漫畫圖片


這是一個 

img

標籤,對應的 

id

是 

qTcms_pic

。這樣找到這個 

id

,就能找到這個 

img

標籤,根據 

src

就能找到圖片的具體URI地址。


接下來是找到下一張圖片的地址。這時候需要查看下一頁這個按鈕的內容。用相同的方法,很容易定位成功。



圖4 下一頁


小喵本來是用scrapy來做爬蟲的,看到這裡的時候就果斷放棄了。我們分析一下,選中的 

a

標籤的代碼如下:

  1. <a

    class

    =

    "next"

    href

    =

    "javascript:a_f_qTcms_Pic_nextUrl_Href();"

    title

    =

    "下一頁"

    ><span>

    下一頁

    </span></a>


比較簡單的網站,"下一頁"可以用真的 

a

標籤和 

href

屬性來做。這樣的好處是實現比較簡單,壞處是一旦得到網頁源碼就能很容易的解析。而像scrapy這樣的爬蟲工具只能抓取靜態的代碼(動態的需要自己分析ajax,有點麻煩)。而顯然這裡的頁面是動態的,使用了ajax來實現。所以光是得到網頁源碼並不能真的得到圖片,而是必須讓其中的js代碼運行才可以。所以我們才需要使用瀏覽器或者PhantomJs這樣的可以執行js代碼的工具。


上面的 

a

標籤的代碼告訴了我們很多信息。首先是告訴了我們,這個節點的位置,通過 

next

這個類名可以方便的找到該節點(其實有兩個類名為 

next

的按鈕,另一個在下面,但是功能都一樣)。其次,當這個按鈕被點擊時會調用: 

a_f_qTcms_Pic_nextUrl_Href

()

 這個js函數。難道我們需要再研究這個函數?


不用。因為PhantomJs的角色就是一個瀏覽器。我們只需要向真正的用戶一樣點擊一下這個 

next

按鈕,就會進入下一個頁面。/* 感受到這個工具的強大了嗎? */


3、判斷章節的末尾


最後一個問題就是,如何判斷這個章節結束了?


我們跳到章節的最後一頁,然後再次點擊"下一頁",這時候會出現一個彈窗。



圖5 最後一頁


多次試驗之後,我們會發現,只有在最後一頁的時候才會彈出這個彈窗,這樣的話,我們每抓取完一頁,點擊一次"下一頁",判斷有無彈窗就知道是不是最後一頁了。在右側的開發者工具中我們能夠看到,這個彈窗是一個 

id

為 

msgDiv

的 

div

(而且它的出現和消失是通過增減節點來實現的,另一種實現方法是將 

display

設成 

none

和 

block

,這種情況可以根據display的屬性來判斷)。所以我們判斷這個節點存不存在就行了。


至此,兩種頁面的解析都完成了。下一步就開始我們的代碼實現吧。


四、逆襲----代碼實現


1,selenium的簡單用法

  1. from

    selenium

    import

    webdriver

  2. browser

    =

    webdriver

    .

    Firefox

    ()

  3. # browser = webdriver.Safari()

  4. # browser = webdriver.Chrome()

  5. # browser = webdriver.Ie()

  6. # browser = webdriver.PhantomJs()

  7. browser

    .

    get

    (

    "http://baidu.com"

    )

  8. print

    browser

    .

    title

  9. # do anything you want


上面是一個簡單的例子,第一步import依賴的庫。


第二步,獲得一個瀏覽器實例。selenium支持多種瀏覽器。使用firefox之外的瀏覽器都需要下載驅動(selenium本身自帶了firefox的驅動)。驅動下載地址:https://pypi.python.org/pypi/selenium 。驅動下載完之後將它的路徑加入到PATH里,確保驅動程序能夠被訪問到。或者顯式的把驅動程序的地址當參數傳入。像下面一樣調用:

  1. browser

    =

    webdriver

    .

    PhantomJs

    (

    "path/to/phantomjs"

    )


第三步,用get的方式打開網頁。


最後,通過browser對象來解析和處理頁面。


2,獲取章節的鏈接信息


在上面的解析頁面的時候,我們知道了章節信息的位置: 

div

#play_0 > ul > li > a

。這樣就可以解析出章節信息。browser支持一大堆的選擇器。大大簡化我們查找節點的工作。

  1. from

    selenium

    import

    webdriver

  2. if

    __name__

    ==

    "__main__"

    :

  3.    driver

    =

    "path/to/driver"

    # 驅動地址

  4.    browser

    =

    webdriver

    .

    PhantomJS

    (

    driver

    )

    # 瀏覽器實例

  5.    main_page

    =

    "http://www.tazhe.com/mh/9170/"

  6.    browser

    .

    get

    (

    main_page

    )

    # 載入頁面

  7.    

    # 解析出章節的元素節點

  8.    chapter_elem_list

    =

    browser

    .

    find_elements_by_css_selector

    (

    "#play_0 ul li a"

    )

    # 通過css選擇器找出章節節點

  9.    chapter_elem_list

    .

    reverse

    ()

     

    # 原本的章節是倒敘的

  10.    chapter_list

    =

    []

  11.    

    for

    chapter_elem

    in

    chapter_elem_list

    :

  12.        

    # 元素的text和href屬性分別就是章節的名稱和地址

  13.        chapter_list

    .

    append

    ((

    chapter_elem

    .

    text

    ,

    chapter_elem

    .

    get_attribute

    (

    "href"

    )))

  14.    

    # chapter_list 就是章節的信息


3,給定一個章節的地址,章節中的圖片


這一步涉及到節點的獲取、模擬滑鼠的點擊以及資源的下載。selenium的點擊實現特別的人性化。只需要獲取節點然後調用 

click

()

方法就搞定。資源的下載網上有許多教程,主要有兩個方法,通過模擬

右鍵另存為

,和

獲取url用其他工具下載。

考慮到這裡的右鍵不一定可用,而且操作有一點點複雜。小喵選用了第二種方案。

  1. from

    selenium

    import

    webdriver

  2. from

    selenium

    .

    common

    .

    exceptions

    import

    NoSuchElementException

  3. import

    os

  4. from

    os

    import

    path

    as

    osp

  5. import

    urllib

  6. # 一個簡單的下載器

  7. download

    (

    url

    ,

    save_path

    ):

  8.    

    try

    :

  9.        

    with

    open

    (

    save_path

    ,

    "wb"

    )

    as

    fp

    :

  10.            fp

    .

    write

    (

    urllib

    .

    urlopen

    (

    url

    ).

    read

    ())

  11.    

    except

    Exception

    ,

    et

    :

  12.        

    print

    (

    et

    )

  13. if

    __name__

    ==

    "__main__"

    :

  14.    driver

    =

    "path/to/driver"

    # 驅動地址

  15.    browser

    =

    webdriver

    .

    PhantomJS

    (

    driver

    )

    # 瀏覽器實例

  16.    chapter_url

    =

    "http://www.tazhe.com/mh/9170/1187061.html"

  17.    save_folder

    =

    "./download"

  18.    

    if

    not

    osp

    .

    exists

    (

    save_folder

    ):

  19.        os

    .

    mkdir

    (

    save_folder

    )

  20.    image_idx

    =

    1

  21.    browser

    .

    get

    (

    chapter_url

    )

    # 載入第一個頁面

  22.    

    while

    True

    :

  23.        

    # 根據前文的分析,找到圖片的URI地址

  24.        image_url

    =

    browser

    .

    find_element_by_css_selector

    (

    "#qTcms_pic"

    ).

    get_attribute

    (

    "src"

    )

  25.        save_image_name

    =

    osp

    .

    join

    (

    save_folder

    ,

    (

    "%05d"

    %

    image_idx

    )

    +

    "."

    +

    osp

    .

    basename

    (

    image_url

    ).

    split

    (

    "."

    )[-

    1

    ])

  26.        download

    (

    image_url

    ,

    save_image_name

    )

    # 下載圖片

  27.        

    # 通過模擬點擊載入下一頁,注意如果是最後一頁,會出現彈窗提示

  28.        browser

    .

    find_element_by_css_selector

    (

    "a.next"

    ).

    click

    ()

  29.        

    try

    :

  30.            

    # 找尋彈窗,如果彈窗存在,說明這個章節下載完畢,這個大循環也就結束了

  31.            browser

    .

    find_element_by_css_selector

    (

    "#bgDiv"

    )

  32.            

    break

  33.        

    except

    NoSuchElementException

    :

  34.            

    # 沒有結束彈窗,繼續下載

  35.            image_idx

    +=

    1


五、終焉-寫在後面


至此,漫畫喵的設計思路和主要的代碼實現都介紹完了。上面的代碼只是用來示意,小喵自己下載漫畫用的代碼是另一套。github的地址是:https://github.com/miaoerduo/cartoon-cat 。項目只有100多行。不過也用了小喵不少的一段時間。


博客寫完了~小喵的漫畫也下完了~



圖6 下載好的漫畫





  • 作者

    :喵耳朵 



  • 原文:http://www.miaoerduo.com



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊成為Java大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

爬蟲可能都會用,但是背後的架構你真的懂了嗎?
python 淺談正則的常用方法

TAG:Python開發 |