網頁

2015年1月5日 星期一

Selenium 瀏覽器操作自動化

Selenium 是一個自動化操作瀏覽器的函式庫一般常用來做網站自動化測試,由於越來越多的網站應用 JavaScript 設計,因此直接透過 HTTP method 實做 crawler 在開發上較為複雜,而 Selenium 則成了撰寫 crawler 的一個替代方案。Selenium 的原理是呼叫瀏覽器,透過瀏覽器開啟網頁,接著對網頁中特定元件觸發事件,原則上只要是網頁中可顯示的元件都可以嘗試觸發事件,觸發的事件可為點擊滑鼠或是鍵盤輸入。基本上可以想像它就是用來模擬真實使用者操作瀏覽器的行為。

今天要示範的是在 Linux 下使用 python selenium 呼叫 firefox 31 (firefox v31為目前測試過可正常工作的版本,若瀏覽器發生問題可參考瀏覽器支援列表選擇相容的瀏覽器) 開啟 Google 首頁,並且觸發鍵盤輸入以及滑鼠點擊事件,模擬使用者的操作行為。

首先設定 python virtualenv 測試環境:
1. 設定 virtualenv 路徑(假設以 ~/.python/virt_env/selenium/ 為 virtualenv 路徑)
mkdir ~/.python/virt_env/ 
virtualenv  ~/.python/virt_env/selenium/
2. 切換至測試環境
source ~/.python/virt_env/selenium/bin/activate
3. 安裝 yolk 
easy_install yolk
4. 安裝 selenium, 安裝後可用 yolk 檢查套件
easy_install selenium
yolk -l 

接著必須先了解哪些 Google 首頁哪些元件可以操作,selenium 操作可以透過 ID 或是 XPath 來指定網頁元件,可以透過 chrome 瀏覽器頁面點右鍵 -> 檢視元素,會跳出 Developer Tools 視窗,可檢視 html 語法

可以自語法中檢視 id 或是 class 等,例如文字輸入框 input 的 id="lst-ib"

此外也在物件上點右鍵檢視 XPath

同樣的也可以用相同方法找到 "Google 搜尋" 按鈕的資訊。

前情提要完畢進入正題,selenium client webdrive 提供的主要 API 以及一般操作流程如下:
1. 初始化 selenium webdrive: browser=webdrive.Firefox()
2. 取得網頁: browser.get ("URL")
3. 指定特定元件: browser.find_element_by_id() / browser.find_element_by_class() ...etc
4. 對物件觸發事件: send_keys() / click()
5. 取得新的頁面結果:  browser.page_source

可參考 Selenium python API 取得完整 API 文件


2010年3月6日 星期六

人工智慧的機器人核心

目前大多數的網路機器人核心由固定的程序組成重複執行特定工作,然而我希望我的網路機器人能夠學習環境的變化,依據所取得的訊息做出決策,並產生不同的行為。因此網路機器人的核心必須是個具備學習能力的有限狀態機,實際上網路上已經存在一些機器學習相關的函式庫例如: PyBrain,它提供了 Reinforcement Learning, Supervised Learning 以及 Unsupervised Learning 的功能,此外 gensim 則是將語言轉換成向量空間的計畫,表示或許能夠依據這些基礎嘗試改寫出能夠處理 Free Text 的機器人核心。

2010年2月16日 星期二

以 POST Method 發布訊息

機器人收進訊息之後我們期望它能做出一些回應,其中 Post Method 是網路上常見的發表訊息的方法可將網頁上的表格回傳至伺服器端。

以下提供一個簡單的網頁做示範 post.php

<html>
 <head>
 </head>
 <body>
  <?php if ($_POST) {echo "<p>Hi ".$_POST[Name]." !</p>";} ?>
  <form id="Post_DEMO" name="Form_DEMO" method="post" action="post.php">
   <h1>Nice to meet you. I am POST_DEMO!</h1>
   <h2>What is your name?</h2>
   <input type="text" name="Name" id="Name" />
   <input type="submit" name="button" id="button" value="Confirmed" />
  </form>
 </body>
</html>

基本上這是一個打招呼的網頁,網頁中有一個表單讓使用者填入自己的名字,送出表單後網頁會重新載入並顯示 Hi "使用者名字" 的訊息,當然機器人應該也能跟網頁打招呼,於是借助 curl 的 POST 工能,在 setopt 時增加 POST 以及 POSTFIELD 的設定讓機器人也能送出表單。

假設 post.php 路徑為 http://www.mypage.tw/post.php ,Post 機器人如下:

#! /usr/bin/env python
import pycurl

class GetPage:
    def __init__ (self, url):
        self.contents = ''
        self.url = url

    def read_page (self, buf):
        self.contents = self.contents + buf

    def show_page (self):
        print self.contents

class GetPageByFakeBrowser(GetPage):
    def __init__ (self, url, ua):
        self.contents = ''
        self.url = url
        self.ua = ua

mypage = GetPageByFakeBrowser( \
"http://www.mypage.tw/post.php", \
"Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00")
testcurl = pycurl.Curl()
testcurl.setopt(testcurl.URL, mypage.url)
testcurl.setopt(testcurl.USERAGENT, mypage.ua)
testcurl.setopt(testcurl.WRITEFUNCTION, mypage.read_page)
testcurl.perform()
testcurl.setopt(testcurl.POST, 1)
testcurl.setopt(testcurl.POSTFIELDS, "Name=Robot&button=confirmed")
testcurl.perform()
testcurl.close()
mypage.show_page()

如此一來機器人就有讀取網頁以及送出表單的功能,接下來我們希望它能做一些較複雜的事情。

使用 python + curl 取得網頁內容(2)

當我們取得整個網頁的 html 文件後,接下來就是從 html 中取得我們有興趣的訊息,例如可以搜尋 a 標籤來取得網頁中的連結,以下範例從 http://chobill.twbbs.org/blog/ 網頁中取得所有連結,以 GetPageByFakeBrowser 取得網頁內容,接著使用 URLParser 將網頁連結過濾出來:

#! /usr/bin/env python
import pycurl

class GetPage:
    def __init__ (self, url):
        self.contents =''
        self.url = url

    def read_page (self, buf):
        self.contents = self.contents + buf

    def show_page (self):
        print self.contents

class GetPageByFakeBrowser(GetPage):
    def __init__ (self, url, ua):
        self.contents = ''
        self.url = url
        self.ua = ua

import HTMLParser
class URLParser(HTMLParser.HTMLParser):
    def __init__(self):
        HTMLParser.HTMLParser.__init__(self)
        self.urls = []

    def handle_starttag(self, tag, attributes):
        if tag != 'a': return
        for name, value in attributes:
            if name == 'href' and value not in self.urls:
                self.urls.append (value)

mypage = GetPageByFakeBrowser( \
"http://chobill.twbbs.org/blog/", \
"Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00")
testcurl = pycurl.Curl()
testcurl.setopt(testcurl.URL, mypage.url)
testcurl.setopt(testcurl.USERAGENT, mypage.ua)
testcurl.setopt(testcurl.WRITEFUNCTION, mypage.read_page)
testcurl.perform()
testcurl.close()
parser = URLParser()
parser.feed ( mypage.contents )
parser.close()
for url in parser.urls:
    print url 
然而這個程式在許多情況下並不管用,因為這世界上存在許多撰寫的相當"有藝術"或是加入一些直譯語言的網頁,它們會讓這支程式在處理 html 文件時出錯,我們之後會回過頭來修正這些問題。

2010年2月8日 星期一

使用 python + curl 取得網頁內容(1)

為了讓機器人能從網路上搜尋資訊第一步就是讓機器人程式取得網頁的內容,我們可以利用 curl 這個函式庫來幫我們處理取得網頁,事實上 curl 能做的事情很多它能做 HTTP、HTTPS 細部設定並且支援多種協定如 Telnet、FTP、SCP 以及 LDAP 等,是很方便的工具。

以下是一段 python 程式,它的功能是用來取得 http://robotexp.blogspot.com/ 也就是本站的內容

#! /usr/bin/env python
import pycurl

class GetPage:
    def __init__ (self, url):
        self.contents = ''
        self.url = url

    def read_page (self, buf):
        self.contents = self.contents + buf

    def show_page (self):
        print self.contents

mypage = GetPage("http://robotexp.blogspot.com/")
testcurl = pycurl.Curl()
testcurl.setopt(testcurl.URL, mypage.url)
testcurl.setopt(testcurl.WRITEFUNCTION, mypage.read_page)
testcurl.perform()
testcurl.close()
mypage.show_page()

執行這支程式時會將整個頁面 html 下載並顯示在終端機上,然而我們希望 curl 讀取網頁時它的行為能更像一般瀏覽器,首先我們希望它向網站發出 Request 時做一些偽裝。
以下為利用 wireshark 擷取 pycurl 預設的 GET Request:
GET / HTTP/1.1
User-Agent: PycURL/7.19.5
Host: robotexp.blogspot.com
Accept: */*

實際上網頁伺服器會紀錄這些 Request 訊息,伺服器管理者可以從這段訊息的 User-Agent 得知使用者以哪種瀏覽器瀏覽網頁,而 User-Agent: PycURL/7.19.5 很明顯暴露使用者不是以瀏覽器來瀏覽網頁,因此必須修改 curl 的 User-Agent 設定使它看起來像一般瀏覽器,以下我們就讓 curl 偽裝成 Opera 吧!
 
將先前的程式碼修改如下,即可將 User-Agent 訊息置換成 Opera:

#! /usr/bin/env python
import pycurl

class GetPage:
    def __init__ (self, url):
        self.contents = ''
        self.url = url

    def read_page (self, buf):
        self.contents = self.contents + buf

    def show_page (self):
        print self.contents

class GetPageByFakeBrowser(GetPage):
    def __init__ (self, url, ua):
        self.contents = ''
        self.url = url
        self.ua = ua

mypage = GetPageByFakeBrowser( \
"http://robotexp.blogspot.com/", \
"Opera/9.80 (Windows NT 5.1; U; cs) Presto/2.2.15 Version/10.00")
testcurl = pycurl.Curl()
testcurl.setopt(testcurl.URL, mypage.url)
testcurl.setopt(testcurl.USERAGENT, mypage.ua)
testcurl.setopt(testcurl.WRITEFUNCTION, mypage.read_page)
testcurl.perform()
testcurl.close()
mypage.show_page()