使用 Mechanize 模擬網頁行為

David Lai
Practicode
Published in
7 min readJun 18, 2017

不曉得大家有沒有想過使用程式來自動操作網頁行為呢?

一開始會對這個主題有興趣的原因很單純,我想要一支程式能自動每天早上和女朋友發訊息說「早安」,這樣除了能維持一個貼心男友的形象,還可以多睡一點不會被嫌冗,於是我開始投入對於 Mechanize 的研究,而這篇文章就會帶著大家走過完成一支自動打招呼程式的基本步驟。

我們可以想像,要完成「發送 FB 早安訊息給朋友」應該有以下步驟:

  1. 連上 Facebook 並登入自己的帳號
  2. 打開訊息夾,找到要發送訊息的朋友並點擊進入
  3. 輸入「早安」訊息並按下送出

那麼我們就一個個來吧!

首先創建我們的打招呼程式檔案:

# say_morning.rb
require "mechanize"
fb_agent = Mechanize.new
fb_agent.get("https://m.facebook.com") do |page|
## some code...
end

一開始先引入 library,並使用 Mechanize class 建立一個物件,而這個物件就會成為你和網頁產生互動的代理人。

接著將此 agent 連線上 Facebook,通常我們打開瀏覽器瀏覽都是使用 GET,因此在這我們也使用 GET 到 https://m.facebook.com ,而為什麼不是常用的 https://www.facebook.com 呢?是因為前者是手機版的網頁,比較沒有繁雜的動態 JS 會擾亂我們要爬的東西,與後者相比相對單純的多。

在 block 裡面的 page 就是我們連線上的結果,偷偷用 pry 來看一下它是什麼:

使用 pry 來看 page 是什麼東西,底下還有許多東西沒有截到

可以看見 page是一個 Mechanize::Page 的物件,而裡面的內容有 url, title, links 等等東西,這時候我們用無痕瀏覽器打開 https://m.facebook.com 來比對一下:

使用無痕連上 m.facebook.com

可以發現它們其實是一樣的東西,有一樣的 title, url, 建立新帳號等等,表示我們的程式成功連上 Facebook 了!

接著我們就要輸入帳號密碼並點下登入,仔細觀察這個 Mechanize::Page 物件可以發現它的下方有一個表單的物件 Mechanize::Form

Mechanize::Page 中的 Mechanize::Form 物件

在 fields 中有兩個的 name 分別為 email 以及 pass,這個其實就是讓我們輸入帳號密碼的欄位,因此我們可以用程式把它填上:

# say_morning.rb
require "mechanize"
fb_agent = Mechanize.new
fb_agent.get("https://m.facebook.com") do |page|
page = page.form_with(method: "POST") do |form|
form.email = "example@gmail.com"
form.pass = "example_password"
end.submit
end

可以利用 .form_with() 來找尋.submit 方法來提交這個帳號密碼的表單,那麼提交後會得到什麼呢?

使用 pry 打開 login 來看看

不太知道是什麼沒關係,我們就用無痕視窗來登入看看對照就知道了:

原來是問我們要不要儲存密碼,既然 Mechanize 那只有一個 button 可以選擇,那麼就加上 page = page.form_with(method: "POST").submit 就可通過這個頁面。

看到這裡,大家應該發現了一個基本的模式:用無痕查看頁面內容 → 使用 Mechanize 填入該頁面資料 → 送出表單,只要兩邊不斷對照就能一步步往前進。

接著我們就登入到個人動態時報頁面了,我們的目標是進入訊息收件夾中並找到要傳送訊息的朋友:

page.links.find { |link| link.text.start_with?("Message") }.click
page.links.find { |link| link.text.start_with?(<FriendName>) }.click

這裡我們要用到另一個技能:查看 links,並點擊。對於 Mechanize::Page 物件我們可以使用 .links 方法查看它所有的連結,再結合 .find 找到我們要點擊的連結關鍵字,好比說要先找到 “Message “,再找到朋友的名字進行點擊,而 Mechanize 也提供 .click 很直觀的方法做點擊的動作,因此 page = page.links.find { |link| link.text.start_with?("Message") }.click 就是完整的做了尋找特定連結、點擊它的動作,並且把結果存回 page

進入到聊天室頁面,那就是要傳送訊息了,把送出訊息這個動作當成在填表單就很好理解,幾乎跟送出帳號密碼的表單一樣:

page.form_with(method: "POST") do |form|
form.body = "早安"
end.submit

特別要注意的這個頁面較多,要找到對的表單

此為送出訊息的表單,可以看到 buttons 那有 Send, Like 等等

這樣就大功告成了,我們完成了一支可以送出「早安」訊息給朋友的程式

完整的程式碼為

# say_morning.rb
require "mechanize"

fb_agent = Mechanize.new
fb_agent.get("https://m.facebook.com") do |page|
# 輸入帳號密碼登入
page = page.form_with(method: "POST") do |form|
form.email = "example@gmail.com"
form.pass = "example_password"
end.submit
# 通過詢問是否儲存密碼
page = page.form_with(method: "POST").submit
# 點擊訊息欄按鈕
page = page.links.find { |link| link.text.start_with?("Messages") }.click
# 點擊朋友名字
page = page.links.find { |link| link.text.start_with?(<FriendName>) }.click
# 輸入訊息送出
page.form_with(method: "POST") do |form|
form.body = "早安"
end.submit
end

其實這是一個繁複的過程,需要有耐心的慢慢去看去比對,另外由於我們是直接去「爬」頁面上的內容,若是 Facebook 進行改版了,很有可能就會無法使用,必須重新修正程式碼。

除了送訊息以外,幾乎你能在網頁上做的事情都可以做到,好比說,自動邀請所有朋友到粉絲專頁按讚,原本必須一個個滑下去點擊,藉由程式去跑就能快速的節省許多時間!

--

--

David Lai
Practicode

Work in recommendation system and NLP algorithms. Microsofter in Redmond, Suzhou and Taiwan