用 Spynner 來抓 8Comic 的漫畫 (2): 用 Widget 美化介面

Posted by TJ Wei on 星期六, 11月 29, 2014 with No comments

已經可以抓了,還有什麼問題?

  • browser 瀏覽頁面時,已經顯示圖了。之後,又再 download 一次,浪費頻寬。
  • 介面不夠美觀,無法看到進度。

頻寬問題

概念上,有兩個方向。 一是既然 browser 顯示了圖片,表示 browser 有這份圖,我們跟 browser 要就好了。另一個剛好相反,告訴瀏覽器,不要顯示圖片,把圖片的 url 交給我們即可。
這兩個方向各有利弊,以現在這個應用來說,我選擇第二個。原因有三:

  •  QtWebKit 有選項讓你這樣做。 
  • 這樣可行。 browser 仍然會傳回正確的圖片 url。 
  • 可以順便擋住廣告圖片。

介面問題

因為我們用使用 IPython notebook,所以使用 IPython notebook 的 interactive widget。

接下來,一樣先 import 所有我們將會用到的東西

In []:
import spynner
import os, sys
from PyQt4.QtWebKit import QWebSettings # 用來設定 QtWebKit
# 下面是 IPython 相關
from IPython.display import display, Image
from IPython.html.widgets import ImageWidget, IntProgressWidget

再來是建立瀏覽器,並且設定不要載入圖片

In []:
base_url = 'http://new.comicvip.com/show/cool-5614.html?ch='
# 建立瀏覽器
browser = spynner.Browser(debug_level=spynner.ERROR, debug_stream=sys.stderr)

# 建立一個 webview,並且設定不要自動載入圖片
browser.create_webview()
settings = browser.webview.settings()
settings.setAttribute(QWebSettings.AutoLoadImages, False)

# 要下載第一本
book_no = 1

# 取得總頁數
browser.load(base_url+str(book_no))
total_pages = browser.runjs('ps').toInt()[0] 

再來是建立 Interactive Widget

In []:
# 建立 Image Widget 用來顯示圖片預覽
img = ImageWidget()
img.set_css("height", 300) # 讓圖片不要太大

# 顯示下載進度的 Progress bar
progress = IntProgressWidget(min=1, value=1, max=total_pages)

下載

跟之前一樣,不過外加進度顯示
In []:
# 顯示之前建立的 Widget
display(progress)
display(img)

# 建立一個下載目錄
dir_name = "download/{:02d}".format(book_no)
if not os.path.exists(dir_name):
            os.makedirs(dir_name) 
print "Download to {}/{}".format(os.getcwd(), dir_name)
sys.stdout.flush()

# 開始下載
for page in range(1, total_pages+1):
    # 取得 image url
    browser.load("{}{}-{}".format(base_url, book_no, page))
    img_url = str(browser.runjs('document.getElementById("TheImg").getAttribute("src")').toString())
    # 下載圖片
    fn = "{}/{:03d}.jpg".format(dir_name, page)
    with open(fn, "wb") as f:
        browser.download(img_url, outfd=f)
    
    # 更新 Widget 的狀態
    progress.description = "%d/%d"%(page, total_pages)
    progress.value = page
    img.value = Image(filename=fn).data

看起來還不錯?

這樣似乎看起來還不錯,沒有幾行程式碼,邊抓還能邊看 preview。 盯著朝著目標奔跑的 progress bar,有種莫名的療癒效果。而且沒有重複抓圖,省了不少頻寬。似乎探索期已經結束,接下來只要把這些程式碼封裝起來,方便重複使用就行了。
但仔細一看,還是有不少問題:

  •  如果用 browser.show() 打開瀏覽器,會發現還是有許多廣告被載入。我們希望這些廣告也不見。 
  • 用了一段時間之後,發現機器越來越慢,仔細一看,記憶體使用量大得驚人。相當詭異。 
第一個問題的成因很單純,有些廣告是文字廣告。第二個問題就比較棘手一點了, QWebSettings.AutoLoadImages 有 bug,會造成記憶體無法回收的問題: http://stackoverflow.com/questions/21357157/is-there-any-solution-for-the-qtwebkit-memory-leak 仔細查看討論之後,會發現這個問題目前基本無解,要等 Qt 解決。(不然就只能很醜的 fork,然後 close process。
那怎麼辦? 放棄這個方案? 碰到無解的問題,如果是駭客(hacker),要深入系統,修正 bug,釋放記憶體。身為自造者(maker),那要捨棄壞掉的系統,自幹一個。
但不過是抓個漫畫,這樣搞,未免太累了點。下一篇,我們要用廢客(faker)的方式來解決問題。那就是,假裝解決問題就行了。
In []:



Categories: