2016年3月13日 星期日

網頁搜括小工具: 用 extract.php 擷取網頁當中的一小塊

用 php 的 querypath 或 javascript 的 jquery 可以擷取網頁的一小部分, 比方說部落格文章的正文部分。 但是每次都要寫一支用丟即棄的小程式也蠻沒效率的。 所以我寫了一支可以重複使用的小程式 extract.php, 以後只需要在命令列上用一個 css selector 語法就可以擷取網頁的一小塊了。 先前還沒有很好的 3G/4G 上網方案時, 我就是用這個工具下載一堆網頁以便離線瀏覽。 此外, 英文報讀軟體 coolreader 遇到太複雜的 html 檔也會掛掉, 所以就算數據上網不是問題, 這也很有幫助。 再比方說我 備份自己的部落格 (或別人的部落格也可以) 也是用這支小程式。

請先 安裝 querypath 套件, 然後把 extract.php 抓回去放到你的 /usr/bin 底下, 記得要 chmod a+x /usr/bin/extract.php

然後抓幾個實驗用的頁面:

wget -O ckhung.html http://ckhung0.blogspot.tw/2014/10/barrier-of-exit.html
wget -O slashdot.html http://developers.slashdot.org/story/08/02/17/1911249/developers-warned-over-ooxml-patent-risk
wget -O eff.html https://www.eff.org/deeplinks/2013/10/lowering-your-standards
wget -O torrentfreak.html https://torrentfreak.com/microsoft-logs-ip-addresses-to-catch-windows-7-pirates-150504/
wget -O techdirt.html https://www.techdirt.com/articles/20160812/16502635229/why-apple-removing-audio-jack-iphone-would-be-very-very-very-bad-move.shtml

用 css selector 擷取每個頁面的主體部分:

extract.php -s 'div.hentry' < ckhung.html > ckhung2.html
extract.php -s '#main' < eff.html > eff2.html
extract.php -s '.body' < slashdot.html > slashdot2.html
extract.php -s '#main' < torrentfreak.html > torrentfreak2.html
extract.php -s '#maincolumn div.storywrap' < techdirt.html > techdirt2.html 
ls -ltr *.html

可以看到: 處理完之後, eff.html 頁面只剩 60%, ckhung.html 頁面只剩 12%, torrentfreak.html 剩下 17%、 techdirt.html 剩下 3%、 而 slashdot.html 頁面更只剩下不到 0.5% ! 因為 techdirt 跟 slashdot 的留言討論串經常都超級長。

也可以同時抓取好幾段, 只要像 css selector 語法一樣, 用 "," 分隔每個 selector 即可。 例如: extract.php -s '.date-header span, .post-title, .post-footer .published, .post-footer .post-labels' < web-scraping.html 可以一口氣抓出本頁的張貼日期、 標題、 張貼時間、 標籤等等資訊。

如果你經常拿它來簡化對某幾個特定網站的頁面, 不如就把 「搭配這幾個網站所用的 css selector」 直接寫到程式裡面去 (請見程式中的 $n2ss 變數), 這樣以後命令列上只需要提及網站的名字就可以了:

extract.php -p blogger < ckhung.html > ckhung3.html
extract.php -p eff < eff.html > eff3.html
extract.php -p slashdot < slashdot.html > slashdot3.html

擷取出來的一小塊其實並不符合 html 語法。 命令列上可以加上 "-w" 選項, 這樣輸出結果外面會包一層簡單的 html 標籤, 並且加上 charset=UTF-8 的標頭。

[2016/10/10] 反過來說, 如果輸入資料只是一個 html 片段 (而非完整的 html), 那麼建議加上 "-u" 選項, 在處理之前先幫它包一層簡單的 html 標籤。 處理中文的 html 片段文件時一定要加上 "-u" 這樣 utf8 字元才不會變成亂碼。

以後要批次擷取網頁一小塊是不是方便多了呢 :-) 但是要再次提醒大家: 如果要暴力砍站的話, 請務必穿插 sleep 指令讓對方伺服器有喘氣的時間, 不然會很惹人厭的啊!

[2016/10/10] 同樣的 extract.php 在不同版本的 linux 底下好像有時會出現中文亂碼問題, 有時又不會。 目前的解決方式是按照 這個討論串 的做法, 假設輸入檔案採用 utf8 編碼, 然後指定 convert_from_encoding、 convert_to_encoding、 strip_low_ascii 等選項。 也請參考 這篇。 在 lubuntu 16.04 上測試 ok。

2 則留言:

  1. 1. 小改 extract.php: 使用新版 (3.x) 的 QueryPath, 需要 require 的檔案不再是 QueryPath.php 而是 qp.php

    2. 發現類似好物: https://github.com/ericchiang/pup 感覺他有很多功能; 不過好像大部分的功能 QueryPath 都幫我做好了, 所以我的程式才可以那麼短就跟 pup 差不多厲害 :-)

    回覆刪除
  2. Python 的話可以用 selenium 套件 做 web scrapping.
    http://www.seleniumhq.org/

    會寫程式的世界是自由的, 真好, 即使我連 class 或更多 "進階" 內容都還不懂, 就已經有這種感覺了.

    回覆刪除