顯示具有 requests 標籤的文章。 顯示所有文章
顯示具有 requests 標籤的文章。 顯示所有文章

2014年7月31日 星期四

筆記:小型 Web Crawler/Automator 個人工具箱

許多 (browser) automation 的工具都用「鬼」命名 XDDDD
phantomjs, slimerjs, casperjs, ghost.py ...
圖片來自:手滑背單字 的懶惰鬼


#以下為我撰寫小型 web crawler / automator 時常使用的工具與思路


觀察 & 分析

釐清要什麼以後,宜先人工操作網頁,並透過以下方式進行初步觀察:
  • chrome 開發人員工具 => network => 勾選 preserve log => 進行操作
    • 觀察 http 封包的發送
  • chrome 開發人員工具 => 點選左上角搜尋圖示 => 直接點擊欲觀察的網頁元素
    • 在該網頁元素的程式碼部分,可以使用右鍵直接取得 xpath / csspath 的資訊
  • 使用 visual event 觀察頁面上 javascript event 與 UI 的 binding 情況


個人工具箱

我慣用的工具有:(程式執行成本由低到高)
  • requests 函式庫,從 http 的層級解決問題
    • 執行效率最佳
    • 雖然位於底層,但最後不一定會是最繁雜的 solution
      • 可無視許多 application UI 的操作邏輯
    • 若上層的 application 過於複雜,會難以找出合法 http request 的格式
      • 要處理的參數過多會導致需要花費很多時間測試
      • 參數值越依賴 application  的運算,越難生成
      • 隨著頁面跳轉、ajax 呼叫 ... 複雜度會上升
      • 需要自行維持  headers / cookies 的值 ... 手動模仿部分瀏覽器行為
  • mechanize / twill / mechanicalSoup 之類的函式庫,用精簡版的瀏覽器解決問題
    • 執行效率次佳
    • 部分瀏覽器的功能已經被實作,能帶來很大的方便
      • 但是不能執行 javascript ,使得適用範圍小很多
    • 函式庫會附帶一些便捷的 API ,協助填寫表單、點擊連結 ...
    • 適合拿來跑簡單的 functional testing
      • 至少可以不用擔心如何維持瀏覽器的狀態
      • 拿來測 API 也並無不可
  • 使用 phantomjs 等等內嵌 webkit 的工具,透過無視窗介面的瀏覽器解決問題
    • 執行效率欠佳
    • 能執行 javascript ,可高度模擬使用真實瀏覽器的情境
    • 有廣大的社群以此為基礎,開發相關的 testing / automation ... tools
      • casperjs 是高階,極為好用的封裝
        • resurrectio 提供了 chrome plugin,可以人工錄製欲自動化的步驟以後,自動生成以 casperjs 寫的測試程式碼
          • 還在開發初期,頗有機會測試程式碼會出錯,需要人工修改
      • ghost.py 是 python 版本仿 casperjs 的函式庫
        • 依賴 pySlide or pyQT ... 安裝得花功夫
        • 能夠使用的 API 仍比 casperjs 少很多,文件亦不足
        • 優點為:使用 python 寫出的程式碼架構,可讀性比 js 版本高非常多
  • 使用 selenium 驅動瀏覽器,直接人工錄製欲自動化的步驟或進而匯出程式碼
    • 執行效率欠佳,且大都需要視窗環境
      • 驅動真實瀏覽器時,會顯示出視窗,好處是較容易除錯
      • 亦可驅動以 phantomjs 為核心的 ghostdriver,不需顯示出視窗
    • Selenium 的 Python 及各語言 binding,能夠讓開發者在習慣的環境下開發程式
    • 直接使用錄製功能時,記錄下來的動作不一定具備正確重現性
      • 亦即播放時仍可能出錯


使用策略、心得

對於簡單的網頁,特別是無須登入那種,requests 可以說是最佳解
甚至直接使用 pyquery 就能快速完成 crawling + parsing 的工作

但是對於較複雜的網頁,常常會陷入 requests vs. casperjs 的抉擇
當評估用 requests 實作要太多時間,而能接受較慢的執行速度時
casperjs 算是一個比較快速的解法

換言之,以下可能是不錯的使用策略:
  • 需要執行頻率高的 crawling 時,推薦用 requests 
  • 需要執行頻率低或有複雜 application 操作的 automation 時,推薦用 casperjs


實務上,要使用 requests 自幹時
千萬記得到 google / github 找找看有沒有人有做過同樣的事情
即便語言不同,只要能夠看懂 http 相關的操作
也很容易可以寫出自己的版本


反之,即便要走 casperjs 路線
也並不代表會一路順遂,因為仍然有一定的學習成本
  • 高階的 resurrectio 能夠自動產生 casperjs  的  code ,但是不一定 work
  • casperjs 有乍看醜醜,但是其實蠻完善的文件(建議讀完他)
  • 分享幾個使用上的經驗:
    • 網頁會有 popup 視窗的行為,要特別注意
    • 使用 evaluate API 之前,盡可能看看其他 API 是否有提供包好的功能
    • 可以關掉讀取圖片的功能,大幅加速程式速度
    • 我個人會透過擷取圖片功能,事後看各個步驟的執行結果

原本我以為若使用casperjs ,就可以在幾個小時之內馬上完成工作
結果一邊撞 javascript / casperjs 的牆,一邊讀文件後
最後還是花費了超過一天才完成工作 ...
當然,熟悉這個工具以後,會認為付出是划算的


歡迎補完

本文沒有探討 parsing 或是  crawling 的深入議題,僅只是介紹寫寫小 scripts 時的工具
若有朋友知道有什麼好物,請推薦給我啊啊啊!





2013年1月8日 星期二

心得:檢索 PTT 的資料


我所撰寫的一個小玩具 =口=

前言


近期因為新專案的關係,需要嘗試去分析 PTT 特定看板的資訊
於是我陸陸續續撰寫了一些小程式:例如「推文分析器」、「發 P 幣機」、「PTT 尋寶機」
本文將針對「PTT 尋寶機」的開發,分享自己的心得! 以下先簡要的列出一些我對 PTT 的觀察:
  • PTT 的看板與文章,其實都有對應的 Web 網頁 (兩者之間有時間上的誤差)
  • 每篇文章的 Web 網頁中 <pre> … </pre> 之間的內容就是文章原文
  • 每篇文章的 Web Url 都不重複
  • PTT 的 Web 介面,可接受 1~2 秒間隔的 query 頻率(我沒有 try 到底線)
  • PTT 的看板有文章數限制,對應到 Web 網頁的話,大約有 900 頁左右(頁數或變動)
  • 透過 PTT 的 Web 介面連到已刪除的文章時,伺服器會回噴 404
  • PTT 上的控碼跑到 Web 網頁會呈現亂碼,壞掉!
  • 因為有「修文」的情況,所以文章的內文格式會「非、常、不、固、定」

心得


而在製作 「PTT 尋寶機」的實務上,我的心得為:
  • 我採取透過 Web 介面檢索文章,並沒有使用 telnetlib 透過 telnet 進行檢索(懶惰!)
  • 我並沒有使用 Scrapy 來協助抓取文章,而選擇自幹
    • 時間有點趕,沒空玩新玩具了 Orz
    • 加上我之前其實有寫過一些 Checkers 去鎖定交易文 … (爆!)
  • 推薦使用老字號的 httplib2 … 雖然我覺得他也有些小問題 Orz
    • 如果是抓取一般「正常的英文」網頁,其實我推薦使用 pyquery,支援直接填入 Url 當參數,然後就可以使用類似 jquery 的語法直接取得內容,方便度最高
    • 如果是抓取一般「正常的各國」網頁,其實 requests 寫起來很順手,讀取抓到的值時還會根據 header 自動做 decode 的動作
    • 我會建議使用 httplib2 是因為 PTT 的 Web 網頁編碼為 Big-5 ,且可能包含一些壞掉的控碼字元。因此,無論在做 decode 或 encode 時,都必須要手動加上 "ignore" 的選項,不然一遇到那些壞掉的字元就會噴出 exception … 。而 pyquery, requests 在使用上較為不方便(恩 … 後者也是支援讀取 socket 的 raw data 啦 … ),所以我就繼續使用 httplib2 了。( 我猜測 Scrapy 應該會對這種包含壞掉的字元的資料來源有處理的函式?)
  • 對於 parse data 而言:
    • pyquery 很方便,一句話就可以從網頁中抓出文章內容:
      • article = d("div#mainContent div pre").text() #possible be None
    • 對於之後文章的 parsing 我採用「硬幹」 + 「regular expressions」,其實如果後者強的話,就一切都沒問題了 …
  • 對於資料庫文章與 PTT Web 介面文章的同步而言,我採用懶人政策:
    • 每天看看最近 100 篇文章有沒有變動
    • 當有人要下載指定文章時,我才會將該文章同步到最新狀態

補充


資料庫我使用 mongodb,因為我常常亂改欄位(攤手),反正就一個極簡單 collection 而已
我並沒有使用專業檢索用的套件去分析文章,抓出關鍵字等等
我僅只是將資料庫內的文章做一些社交上的簡單統計
然後開了 nginx + gevent + bottle 的簡單組合提供 Web 介面
就完成小玩具了!(其實迴響還不錯 XD)

有趣(發人省思?)的社交分析

過程中,技術困難點大概還是在抓資料這一件事情
雖然我前端也卡了不少時間 … ( 囧rz 我不會寫前端啊啊啊
我是用 google 寫的啊
效能的話,我則是完全沒有優化 XDDDD
玩具的目的很簡單,就只是協助板友備份文章、以及提供查詢自己的資料
以使用率來看,不會有讓伺服器有什麼負擔的可能性
兩天的使用情況是不重複訪客 800 人瀏覽(畢竟是耳機、音響還算小眾市場)
總之,寫個小玩具,能讓我多摸到一些 python 的 module 及被強迫摸前端的東西
然後又能創造出一些價值給板友、鄉民
也算是皆大歡喜了!

新版的個資法實在非常嚴厲
各位如果有撰寫相關程式請務必小心 Orz
雖然我自認此服務,不營利、不為蒐集特定對象而寫且純粹出於善意
但是我還是願意提供反檢索功能 (目前是沒有人跟我說他不要被檢索啦 … )
歡迎有興趣的大大與我一起交流相關技術 ~~

-----------------------------------------------------------------
2014.05.28 本文補充說明:

由於 ptt 的 web 版在 2013 年有多次的改版
所以本文內容已經不適用現在的情況
只能當做是一個記錄

目前 ptt 的 web 版已經是輸出 utf8 編碼的內容
且作者、標題、推文等等資訊都已經被存放在特定的 xpath 路徑之內
所以可以用  pyquery 等套件,用類似 jquery 的方式輕鬆取得內容

又,上述說明了這麼多
其實強者我學長 c3h3 已寫好 crawler 且 open source
需要的朋友請取用!

https://github.com/c3h3/PlaYnlp-Corpus/tree/master/crawlers/ptt_crawler