2017年7月8日 星期六

cmdlr 3.0

命令行漫畫訂閱工具 cmdlr 3.0 版已經推出。這是一個徹底重寫的版本,一鼓作氣解決 2.0 版中各種瑣碎難忍的問題。總之隨意拿去玩吧。


基本特色


  • 訂閱導向:能長期持續追蹤目標書籍狀態,偵測更新。
  • 支援 Linux命令行:丟到樹莓派上跑毫無問題。
    • 註:所有組件均跨平台,但我沒有為 Windows 與 Mac 做測試,應該能跑但無法保證可靠性,想用請自行評估。

2.0 版的部落格文見:漫畫訂閱器:cmdlr

新特徵


以下是 3.0 閃亮亮的新特徵:

完全的非同步


2.0 版中也有最基本的非同步能力,但這種能力只被用來抓取單卷中的圖片,想同時抓取跨卷的圖片還做不到。

到了 3.0 版,cmdlr 終於可以非同步處理幾乎任何東西——不但前述跨卷的圖片能分散抓取,連分析器需要的 html 也同樣能分散取用,還能非同步執行分析任務。

而在這樣的基礎上,各網站的分析器模塊,仍能精確限制對目標網站的同時抓取數,換句話說,在瘋狂抓取的同時,仍不會對目標網站造成過大負擔,降低本地 IP 被封的風險。

順便一提,3.0 的非同步機制完全基於嶄新的協程技術寫成的,並沒有顯式使用到任何額外線程與進程,系統開銷低到讓人想笑。

分散式的 metadata


在 2.0 版的實作中,cmdlr 將數萬卷的 metadata 集中存放在 sqlite 資料庫中,導致每次提交資料就必須鎖住資料庫,成了一項讓人煩惱的效能瓶頸。

同時,因為 2.0 版資料庫格式為二進位檔案,一般工具不能閱讀,故為了維護、查找,必須在命令行中建構大量操作與查詢接口,不但處理麻煩又缺乏靈活性,還平白無故增加一堆代碼,結果把命令行界面選項搞得超複雜不說,連想將資料夾移個位置都做不到。

在 3.0 版中,metadata 改採 yaml 格式存放,此格式為純文字,且肉眼可讀。此外,每個資料夾中都有各自的 metadata 檔案。

因此,像是取消訂閱這種操作,只要簡單地將資料夾刪除即可,改變資料夾的名稱與放置位置同樣靠檔案瀏覽器就能做到,根本不用操作專屬接口。如果有特殊需求,還可以自行用 find, grep, awk 發明各種 pipe 魔法,與外部工具直接對接,隨意查找資料或做各種維護。「追蹤狀態」能輕易透過直覺維持、解除、重建。具體說來,用戶可自由透過外部程式搬移檔案、讀取檔案、刪除檔案,而不用知道本程式是否存在——實際訂閱狀態會隨檔案存在與否及位置遷移而自動重新計算。

這種便利與彈性也是我最近編寫工具時的方向,對我比較熟悉的朋友在 lolinote 專案中大概就能感覺到了。

當然,有一好沒兩好,因為 metadata 採用分散式存放,導致啟動時必須讀取並解析大量小檔案,隨著訂閱量變多,啟動時間會拉長,尤其是 pyyaml 解析相當吃 CPU,效能更傷。在樹莓派 3 下用 python3.6 進行測試,針對 1500 項訂閱,將所有訂閱表列出來的時間需要約 25 ~ 30 秒!主要時間是耗在 yaml 解析上(已使用較快的 yaml.CLoader 解析組件)

不過樹莓派這方面本來就慢,如果用一般電腦,反應時間會再縮短大約 15 倍就是了。

為了解決這個問題,我替 metadata 建構了完全透明免維護的快取系統。將上述樹莓派啟動時間進一步縮減到 7 秒左右。話說樹莓派 3 還真的出乎意料很慢,profiling 指出其中至少 2 秒是用在模組 import 上 orz ……嘛,再想加速就要寫 Lazy Import 子系統了,但難保不會干擾分析器開發者,搞太複雜也不好,就先這樣。

全新的分析器框架


在 2.0 版中,想要開發新的分析器(支援新網站的插件),開發者將被迫定義一卡車多到讓人惱火的接口。

包括生成分析器代碼、url 與本地代碼轉換函數、說明文輸出等等共有 8 個之多,且這些接口還必須被包覆在自定義類別底下,並繼承特定物件,一層包一層……寫起來有夠讓人沮喪的說。

最小化的開發接口


簡化能簡化的一切正是 3.0 的目標。在 3.0 中搞分析器再也不用那麼麻煩了!

想開發一個分析器,非定義不可的接口 / 變數僅僅只有 3 個而已,每個接口 / 變數都有明確的定義與文件不說,還完全隱藏了非同步分析原本會有的複雜性,寫起來和寫同步程式一樣容易。簡單的分析器不用百行就能寫完,不算空行 50 行內也不難,如果採用 README 中提供的通用模板,依網站而異,甚至可透過填空的方式在十行內處理掉全部解析邏輯。

除此之外還有些可選接口,允許開發者在需要時可做一些更精微的控制,但平常沒用到時,當他們不存在也 OK。預設值就已經完整可用了。 

輕鬆修復、分享、自建分析器


在此之上,測試或分享自訂的分析器也變得更簡單了。

分析器開發者或收到自定義分析器的用戶,不用存取系統路徑,而可以自由指定「額外的分析器資料夾」——任何放在指定資料夾中的 .py 檔案或是 python package,都會被視為額外被加入的分析器。就算不提交給上游(我),也可以輕鬆和眾人交換使用,讓 cmdlr 支援任何想支援的網站,想支援多少都沒問題。

順便一提,如果新模塊與內建模塊同名,則會「遮蔽」內建的模塊,因此也可以透過這種特性自己輕鬆修 bug 來對應網站結構變化,不用等上游修復,有興趣自己就能輕鬆搞定。當然我也很歡迎各種拉取請求 (Pull Request) 的。

錯誤偵測與防呆


在分析器開發維護中,一項常見的問題就是:如果我做錯了,我能不能盡快得知錯誤?

在新版的分析器架構中,cmdlr 會檢測幾乎所有的回傳值與輸出結果,確保所有結果滿足格式要求。所以如果自行開發的分析器回傳值不完整,或有問題,開發者幾乎必然會馬上知道。好比說像是下面這些:

  1. 應該回傳 url 的地方,回傳的不是合法的 url。
  2. 應該回傳至少一個值的列表,內容卻是空的。
  3. 傳入了不被允許的空字串。
  4. 缺了某幾個欄位。

像是這些問題,系統都會在第一時間偵測到並清晰報錯。這不但對新手開發者方便,還讓因為網站改版而導致的錯誤不容易被意外忽視。比方說原本爬得到卷列表的地方,現在因為網站改版爬不到了,回傳卷數為空,這類事情都會報錯,清清楚楚。錯誤不是靜默忽略,而是顯而易見的。

除此之外,系統還會對許多回傳結果進行過濾操作。這包括 html entities 解碼、清除字串開頭結尾處的空白字元、確保可能用作檔名的字串在檔案系統中合法(如果不合法自動轉換)等這類常會被忘記的繁瑣動作,框架會自動幫開發者處理好。

功能豐富,擴展性強


分析器允許分析超過一個以上的來源頁面。比方說,標題在 A 頁,卷清單在 B 頁,需要爬找兩頁以上的資料才能產生完整的 metadata,這種狀況能輕易處理掉。

可透過外部 nodejs 運行環境,來解碼 javascript 片段,在某種程度上對抗代碼混淆器。

可以任意竄改 header,偽造 cookies 與 referer,並保持 session。

如果有必要,還允許分析器開發者將 cpu 密集操作分派到其他進程,甚至可以輕鬆外接無頭瀏覽器來處理動態網頁(雖然因為無頭瀏覽器太笨重不怎麼推薦)。

從用戶端取得設定值也毫無問題。爬取需要登入的特定網站,或是允許用戶對分析器執行策略進行客製化設定,這種事並非不可能。

所有這些能力都已經準備妥當,開箱既用。當然要是用不上也不會造成干擾,當作不存在即可。

大幅提昇可維護性


2.0 版的程式碼有許多維護性方面的問題。這些問題在 3.0 中都被解決了。包括下面這些:

  1. 2.0 分析器引入的方式又笨又怪。3.0 重寫了。
  2. 徹底分離命令行界面與核心模塊。未來如有需要,套層皮就能直上 GUI(雖然我用不到應該不會去寫它)。
  3. 使用 logging 模塊集中處理 log。
  4. 在功能、效能、靈活性大幅升級的基礎上,原碼行數只有 2.0 版的 4/5。
  5. 詳盡的文件。

這次會寫新版本,說起來有一半就是因為舊程式碼太讓人 OTZ,難以進一步擴展提昇效能的緣故,這次算是基本把它搞定了。

另外,隨著這次升級,版本管理系統從 Mercurial 轉移到 git,並在 github 上發佈。畢竟 git 與 github 都比較流行,有興趣參與開發的朋友應該也會比較好上手才對。

退化

3.0 開發過程中的一大目標就是盡量簡化,在此目標下,有些特性被捨棄了。

不再支援圖片資料夾


抓取後的檔案將會被立刻包裝為無壓縮的 cbz 檔案。用戶再也無法直接看到一張一張獨立的 .png、.jpg 檔案躺在資料夾裡了。我相信在上千個資料夾中躺著數萬圖片不是好作法,也沒必要。關於 cbz 檔案的說明,請詳見過去的舊文:整理漫畫圖檔的小工具: ftcbz

如果您一定要看到未被包裝的圖檔,使用解壓縮程式直接解壓縮 cbz 檔案即可。不過如果您的看圖程式不支援 cbz,還是強烈建議直接換個看圖程式比較快。

不再內建支援簡繁轉換功能


主要是轉換效果不夠滿意。在想通怎麼做比較好之前,不打算實作相關功能。

如何安裝


首先您需要 python3.5 以上版本。然後執行以下命令:

pip3 install cmdlr

想從舊版本升級上來的人請打

pip3 install cmdlr --upgrade

如此一來就安裝完成了。

簡單使用說明


詳細說明請見 README.mdcmdlr --help 命令。

如何訂閱書籍


請直接運行

cmdlr <URL>  # 訂閱

即可訂閱 URL 指向的那本書。並將這本書像是作者、書名、有幾話等資訊保存下來。

前述 <URL> 指的是一本書的「入口頁面」,cmdlr 透過各式各樣分析器,能支援不同網站。(當前支援的網站還請參考 README 檔案)

用戶也可以透過以下指令取得更詳細的分析器資訊:
 
cmdlr -a  # 取得分析器名稱列表
cmdlr -a <analuzer_name> # 取得個別分析器的說明

如何抓取


訂閱後,運行

cmdlr -d

則會開始抓取圖片。

如何更新 metadata


每過一段時間,cmdlr 分析到的資料可能會過時,這時就可以透過

cmdlr -m

重新分析所有訂閱的書籍,取得網站上的最新資料。這可看出卷數是否有起變化。



許多命令都可以複合使用。如更新資料與抓取指令:

cmdlr -md

這邊提到的只是最簡單的用法,有興趣可以讀 README.mdcmdlr -h 看更多細節。

如何設定


那麼,抓下來的檔案被存到哪裡去了呢?

執行過 cmdlr 程式一次後,cmdlr 就會自動在您的系統中產生預設設定檔。

您可以在 $XDG_CONFIG_HOME/cmdlr/config.yaml 找到您的設定檔,一般來說,這個位置就是 ~/.config/cmdlr/config.yaml。

設定檔內容大約長得像下面這樣:

dirs:
- ~/comics
disabled_analyzers: []
extra_analyzer_dir: ''
max_concurrent: 10 

其中有很多選項,但別的不管(細節請看 README),最重要的選項就是 dirs 選項。這個選項是個陣列,說明「哪些目錄下的書要被追蹤」。書籍被放在這些目錄下面,就意謂著「訂閱中」;而書籍從這些目錄下消失,意謂取消訂閱(追蹤)

如果有需要,您可以指定好幾個目錄,像是下面這樣:

dirs:
- ~/comics/new
- ~/comics/scifi
- /mount/external_disk/fantasy

在分析本地現有訂閱時,您指定的目錄將會被掃描。注意:掃描只有一層而已,並不會遞歸下去,這是出於效能考量做出的取捨。列表中第一個目錄比較特殊,是輸入目錄;新訂閱的書籍,將會直接被塞進這個目錄中(上例中的 ~/comics/new)。

透過這個設定,您可以知道剛訂閱的東西被存放到哪裡去了。

專案在這裡



有 bug 請盡量用 issue tracker 提交。也歡迎 PR!

沒有留言:

張貼留言

☆每日吐嘈,有益身心☆
…不過還是請手下留情別太狠啊。