用 Spynner 來抓 8Comic 的漫畫 (1): 基本技術

Posted by TJ Wei on 星期一, 11月 24, 2014 with No comments


需要套件

  • Spynner (需要 PyQt4 或 PySide, autopy)
  • IPython notebook (因為這個範例是用 IPython notebook 示範,不然跳過 IPython 相關部份也行) 安裝 先安裝 Python, IPython notebook, PyQT4

在 mac 下(如果 autopy 安裝不起來):

  • 先安裝 Qt。 brew 的話, brew install qt 即可。
  • 安裝 PyQt 。 brew, pip 都行。
  • easy_install -N spynner
  • 在適當的地方, touch autopy.py,如 touch /usr/local/lib/python2.7/site-packages/autopy.py。假裝有 autopy 就行了,因為 autopy 其實用不到。

在 windows 下:

  • 安裝 python(x,y) 2.7
  • 接下來打開 IPython 然後輸入 !easy_install spynner(win8 可用搜尋找到 IPython)
  • 最後,打開 IPython notebook,按下 New notebook 開始。
In []:
# This is for windows 
# on linux, simply sudo easy_install spynner in command line
!easy_install spynner 
# restart the kernel

先 import 所有我們將會用到的東西

In []:
import spynner
import os, sys

# 下面這行是 IPython 相關
from IPython.display import display, Image
In []:

再來我們試試看建立瀏覽器

browser = spynner.Browser(debug_level=spynner.ERROR, debug_stream=sys.stderr)
如果看起來什麼事情都沒發生,那大概就對了。 spynner 已經在背景建立了一個 webkit 瀏覽器(叫做 browser)。
通常我們不需要 browser 真的被顯示出來,不過為了方便了解發生了什麼事情,我們先讓它能夠被顯示。
In []:
browser.show() # 告訴  browser,要它之後不要隱身
# 為了避免法律上的疑慮,這裡你要自己找到適當的 url,把 ???? 換掉
base_url = 'http://???.com/show/????-????.html?ch='  
browser.load( base_url+'1')
這時候,成功的話,一個瀏覽器會跳出來,顯示漫畫第 1 話的封面。
瀏覽器能夠改變大小,但是看來像是當掉一樣,沒有回應。
這其實是好事,因為我們希望能夠完全控制瀏覽器,所以先凍結它,再慢慢來蹂躪它。
接下來,我們要把封面圖的 url 抓出來。
In []:
browser.load_jquery(True)   #  spynner 內建有 jquery,用這個 method 載入,比較方便。
img_url = str(browser.runjs('$("#TheImg").attr("src")').toString())
print img_url
# 當然不用 jquery 也可以
img_url = str(browser.runjs('document.getElementById("TheImg").getAttribute("src")').toString())
print img_url
上面先用 runjs 跑 javascript 得到一個結果。
這個結果是一個 Qt (C++)物件,可能是數字、字串或者物件。因為我們知道我們要的是字串,所以用 .toString 讓他成為一個 Qt 字串。
最後,再用 str 轉成 Python 字串。

抓圖

知道了圖片的 url, 那要如何將圖片抓下來呢?
可以用 browser.download(img_url, outfd=fd) 直接下載到檔案裏面。
不過這裡先直接在 IPython notebook 裡面秀一下圖片。
In []:
# 直接顯示 url 看看
display(Image(url=img_url, width=200))
In []:
# 先用 browser 抓下圖檔內容, 然後顯示
display(Image(data=browser.download(img_url), width=200))
漫畫每一頁的 url 格式是 .......ch=M-N 其中 M, N 是數字, 分別是卷數及頁數, 所以現在我們只要知道有幾頁就行了。
一般來說,可以從 html 內容中找到資訊。 8comic 控制 UI 的 javascript 就有這個資訊了,我們直接利用。
一樣先用 runjs 得到 ps 這個 javascript 變數的內容, 然後轉成整數。
因為 toInt 的結果包含一些額外資訊,所以我們用 [0] 取出數字。
In []:
total_pages = browser.runjs('ps').toInt()[0] 
print total_pages
所以我們用一個迴圈把每一頁都抓下來吧
In []:
book_no = 1
for page in range(1, total_pages+1):
    browser.load("{}{}-{}".format(base_url, book_no, page))
    img_url = str(browser.runjs('document.getElementById("TheImg").getAttribute("src")').toString())
    print page, img_url
    display(Image(url=img_url, width=100))
    continue
    # 上面只是顯示每一頁的圖片
    # 如果你現在就想真的抓檔案下來, 把上面那個 continue 註解掉
    with open("{}-{}.jpg".format(book_no, page), "wb") as f:
        browser.download(img_url, outfd=f)
        print "File saved in", os.getcwd()

到這裡為止,基本的功能已經有了,下一篇將會討論一些細節問題。


題外話。
寫這篇一部份是因為「如何用 Python 抓網站內容」的詢問度一直很高,另一部分是因為發現  JComicDownloader 無法抓 8Comic 的圖。我看了一下,覺得這是一個不錯的例子。




Categories: