偶然看到 PythonWebkit 這個專案, 作者花了兩週左右的時間寫好 WebKit 的 python 介面, 於是操作 DOM 不再是 javascript 的專利, 透過 PythonWebkit 就能用 python 操作 DOM。作者自稱除一些小功能外, 這個專案已到可用的階段:
The current status is that the SVG 2D Canvas element is not available, and CSS Styles have to be modified through the style.setProperty function (rather than being accessible as python properties). Other than these gotchas, the Python Webkit bindings are in a useable state,
providing full W3C compliant access to the full set of DOM functionality (except SVG Canvas) as provided by Webkit, and ordinarily only accessible via javascript.
不過相關文件超少, 花了些時間才裝成功, 也增加了一些編原始碼的功力。
背景介紹
WebKit 的歷史很有意思, 有興趣的人自行參照連結。在 Linux 上目前有兩種 port, 一個是用 GTK+ 實作的WebKitGTK+, 另一個是 QT 的實作版本。
PyWebKitGtk 是另外一個基於 WebKitGTK+ 的專案, 透過 WebKit 提供的 gobject binding 提供 python 介面, 但沒有提供直接操作 DOM 的功能。雖然有些補救方案, PythonWebkit 的作者覺得直接改出 python 介面比較省事, 效率也較好, 該頁有寫詳細的設計考量。所以, PythonWebkit 是以 WebKitGTK+ 為底, 修改部份內容直接提供 python 的 DOM API。
剛開始作者用 PyWebKitGtk 的 python module "webkit", 藉此省掉寫 python module 的步驟, 然後加上 patch 讓 webkit (python module) 能取出 DOM。也就是說, PythonWebkit 依賴 PyWebKitGtk; PyWebKitGtk 依賴 WebKitGTK+; WebKitGTK+ 是 WebKit 在 Linux 下的實作版本之一。
後來作者自己重寫一個 python module "pywebkitgtk", 就不用再裝 PyWebKitGtk 了。網路上找 PythonWebkit 的文件, 有部份會提到 PyWebKitGtk, 但現在已用不到它了。弄清楚這堆名稱差不多的專案, 花了我一些時間 ..., 搞清楚後也滿有成就感的。
上面是簡化的說法, 實際上 PythonWebkit 不是直接依賴 WebKitGTK+, 也可以改用基於 DirectFB 的 PyWebkitDFB。由於這還在實驗階段, 我就沒試了。
安裝方式
主要參考 PythonWebkit 的說明, 另外需要參照 WebKitGTK+ 的安裝說明, 了解它用到的套件。在 Ubuntu 下的安裝步驟如下:
1. 升級到 11.04, 11.04 的 glib 版本才夠新 (>= 2.27.4)
不然會出現
configure: error: You need the GLib dev tools in your path
2. 安裝編譯 WebKitGTK+ 需要的套件
注意: 最後三個指令不能用 aptitude, 只有用 apt-get 才能確保移除後仍保有需要的設定
$ apt-get install python-dev python-ply
$ apt-get build-essential
$ apt-get build-dep libwebkit-1.0-2
$ apt-get install libwebkit-1.0-2
$ apt-get remove libwebkit-1.0-2
在 Ubuntu 下自己編原始碼時, 似乎滿常用這招的, 透過原有的套件管理系統裝好相依的套件、取得需要的 header 等資源, 藉此減少手動操作部份。
3. 取得 PythonWebkit 原始碼, 這步會花很長的時間, 好像會到一小時吧
$ git clone git://git.savannah.gnu.org/pythonwebkit.git
4. 切換到 branch python_codegen
$ git checkout python_codegen
注意: 千萬不要加 "-b", 我不熟 git 參數, 誤加了 -b, 變成開一個新的 local branch 叫 python_codegen, 而不是取出遠端的 branch python_codegen。git 指令請參考《Git 版本控制系統(2) 開 branch 分支和操作遠端 repo.》。
有取對 branch 的話, grep 整個目錄應該會找到 pywebkitgtk 相關檔案; 反之則否。pywebkitgtk 是 PythonWebkit 產生的 python module, 作用和 PyWebKitGtk 的 webkit 差不多, 不過多了取得 DOM 的方法 (web view 的 GetDomDocument())。
5. 開始編譯
$ mkdir build
$ cd build
$ ../autogen.sh
$ make
我的電腦是三年前買的, 在 VM 裡用雙 CPU 編的話 (配合 make -j 2), 大概四十多分鐘可編完。
執行 autogen.sh 時, 若發現缺了東西, 可用 aptitude search 加關鍵字, 或 apt-file search xxx.h 查看看放在那個套件, 通常是要裝一些 X-dev 的 package。印象中我好像有少了 pygtk。有問題時, 記得用 dpkg -l 和 WebKitGTK+ 的 Dependencies 對一下, 看有沒有少裝什麼。然後再重跑 autogen.sh。
6. 檢查結果
編好的東西放在 .libs/ 下, 不知是不是某種慣例。上網查 make install 的討論, 似乎沒有明確的方法可以找到會裝那些東西進系統。幸運的話, 可以用 checkinstall 裝成 deb 檔, 就能用 dpkg -L 查裝了什麼, 也能用 dpkg -r 移除。不過我用 checkinstall 的結果, 似乎比直接 make install 多裝了不少東西, 最後直接用 make install。
回到正題, 先切到 .libs 下看編完的東西是否可用。.libs/ 下應該要看到 libwebkit 的 so 檔, 還有 pywebkitgtk.so。接著在 .libs/ 目錄下執行 ipython, 依序打入:
import gtk
import pywebkitgtk
url = "http://www.gnu.org/software/pythonwebkit"
wv = pywebkitgtk.WebView(1024,768, url=url)
doc = wv.GetDomDocument()
ns = doc.getElementsByTagName('h2')
ns.length # 9
ns.item(0).innerHTML # 'Why is this important - why is it a "big deal"?'
注意, 上述程式要在 X Window 下執行。import gtk 後, 在產生 WebView 物件時會跳出視窗。若沒有 import gtk 的話, 不會有任何錯誤訊息, 但取得的 doc 裡面卻沒任何東西, ns.length 會是 0。
7. 安裝到系統
$ sudo make install
然後檢查 /usr/local/lib 是否有在 ldconfig 路徑中:
$ grep -R "/usr/local/lib" /etc/ld.so.conf.d/
若沒有的話, 在 /etc/ld.so.conf.d/ 下新增一個檔案:
$ sudo echo "/usr/local/lib" > /etc/ld.so.conf.d/webkit.conf
最後是更新 ldconfig 的 cache:
$ sudo ldconfig
不確定原本 /etc/ld.so.conf.d/ 下就有 /usr/local/lib 的情況是否要執行 ldconfig, 好像要執行才會 ok, 但看文件說, 一般 make install 時應該會自動執行這個動作。
之後就能在任何地方直接 import pywebkitgtk 啦。
用法
附上一個小例子: 取出 TechCrunch 文章有多少人按 Facebook 的 like:
import os
import gtk
import pywebkitgtk
wv = pywebkitgtk.WebView(10, 10, url='http://techcrunch.com/2011/09/25/clinging-to-friction-some-thoughts-on-facebooks-f8/')
os.sleep(10) # Wait Facebook plugin to be loaded.
dom = wv.GetDomDocument()
iframes = dom.getElementsByClassName('fb_ltr')
for i in range(iframes.length):
e = iframes.item(i)
ns = e.contentDocument.getElementsByClassName('connect_widget_button_count_count')
if ns.length > 0:
print ns.item(0).firstChild.nodeValue # Count of "FB like"
TechCrunch 的推文數大多是用 AJAX 載入到 iframe 裡, 用普通的方法無法抓到內容。上面的作法相當粗糙, 就將就用啦, 這不是本篇的重點。
產生 wv 後會看到 console 出現一堆訊息, 大概是載入頁面後, 以及執行完後續 AJAX 時產生的。之後再來看看這些訊息是否有害。
PythonWebkit 沒有多提相關 API, 因為作者為了方便大家查文件, 讓 PythonWebkit 和原本的 DOM API 100% 相容 (目前只有 CSS 部份有一點不同), 所以, 直接學 DOM 就可以了。配合 ipython 直接操作, 還滿容易試的。
Trouble shooting
1. configure: error: You need the GLib dev tools in your path
參考 這篇, 要用新版 glib, Ubuntu 要到 11.04 才符合。我鐵齒的從 10.04 一路試, 直到 11.04 才真的 ok。不過 libsoup 用 2.24 也 OK, 不需要像 PythonWebkit 說要用最新版才行(2.29.90), 不確定之後會有什麼問題。
2. 編譯時出現 "error: webkitmarshal.h: No such file or directory"
參照這篇, 執行
$ touch ./Source/WebKit/gtk/webkitmarshal.list
然後重跑 make。
3. 無法 import pywebkitgtk / 沒有編出 pywebkitgtk.so
記得在取得 repository 後要先執行 "git checkout pyhton_codegen" 才執行 ../autogen.sh, 千萬不要寫成 "git checkout -b pyhton_codegen"
4. import pywebkitgtk 時說找不到 libwebkitgtk
In [1]: import pywebkitgtk
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
/usr/local/lib/<ipython-input-1-8dab5af5d34b> in <module>()
----> 1 import pywebkitgtk
ImportError: libwebkitgtk-python-1.0.so.0: cannot open shared object file: No such file or directory
先用 ldd 檢查路徑是否確實有問題:
$ ldd /usr/local/lib/python2.7/dist-packages/pywebkitgtk.so | grep libwebkitgtk
libwebkitgtk-python-1.0.so.0 => not found
出現 "not found" 表示 pywebkitgtk.so 找不到 libwebkitgtk-python-1.0.so.0。接著檢查 /etc/ld.so.conf.d/* 有沒有含 /usr/local/lib (當然, 要先確定你的 libwebkit-python-1.0.so 有裝到那)
將 /usr/local/lib 加到 ldconfig 的路徑後, 執行 sudo ldconfig。
另外看到有人提到在編譯前這麼做:
$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
不過我試了沒成功, 之後有空再來看 pkgconfig 是什麼。
其它參考資料和雜項心得
- Program Library HOWTO: 動態函式庫的相關知識, 有提到 LD_LIBRARY_PATH 和 LD_DEBUG, 必要時可協助除錯。
-
作者個人網站, 有他的 e-mail。原本想說再試不出來就寄信問看看。話說找這個也花了點時間, PythonWebkit 整篇都沒提到他是誰啊 ..., 在這封信才看到多一點情報。
-
有問題時, 可以看看 commit log, 了解主要的修改為何。從這次的經驗, 發覺除系統安裝的知識外, 操作 VCS 的知識和讀 commit (changeset) 的能力也很重要。