Selenium

Selenium

需要在 Python 上操作瀏覽器的話,我會選擇使用 Selenium;
以爬蟲來說,和 requests 不同的地方在於,
像是在讀取網頁時,使用 Selenium 開啟瀏覽器的話可以幫我們處理掉渲染的問題。

這篇主要會以 Selenium 的方式來取得痞客邦的 Access Token。

先搞懂痞客邦的流程

首先,我們先到痞客邦的開發者網頁

PIXNET

或是可以直接到 API Explorer 的畫面

API Explorer

接下來會需要進行登入的動作

Login

登入成功後,便是授權給 API Explorer 權限來取得 Access token

Granted

最後可以在 API Explorer 的畫面上看到 Access Token。

AccessToken

程式方面

我們會使用到 Selenium 的這些元件

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def get_token():
    url = 'https://devtool.pixnet.pro/#/'
    # 使用 Firefox 來開啟網頁
    driver = webdriver.Firefox()
    # 打開 API Explorer 的畫面
    driver.get(url)
    # 進行登入
    driver.get('https://panel.pixnet.cc/login/openid?done=https%3A%2F%2Fdevtool.pixnet.pro&openid=https%3A%2F%2Fmember.pixnet.cc%2Flogin&easy_login=1')
    # 選擇臉書登入
    driver.get('https://panel.pixnet.cc/login/facebooklogin?done=https%3A%2F%2Fdevtool.pixnet.pro&easy_login=1&register_url=%2F%2Fmember.pixnet.cc%2Fregister')
    # 在 Email 和 Password 欄位填上值,並按下登入按鈕
    email = driver.find_element_by_id('email_container')
    password = driver.find_element_by_id('pass')
    login_button = driver.find_element_by_id('loginbutton')
    email.send_keys('Your Facebook email')
    password.send_keys('Your Facebook password')
    login_button.click()
    # 跳轉至授權畫面並按下同意
    driver.get('https://emma.pixnet.cc/oauth2/authorize?redirect_uri=https://devtool.pixnet.pro/index/cb&client_id='Your client id'&response_type=code')
    driver.find_element_by_id('send-Allow').click()
    time.sleep(5)
    # 使用 WebDriverWait 來等候標題出現 EMMA API Explorer(這邊我只寫 EMMA)
    try:
        WebDriverWait(driver, 10).until(EC.title_contains('EMMA'))
    except TimeoutException:
        print('time out')
    finally:
        # 接著 Parse 出 Access token 並且 return。
        soup = BeautifulSoup(driver.page_source, 'html.parser')
        ps = soup.find_all('p', 'form-control-static ng-binding')
        token = ps[0].string.replace(' ', '').replace('n', '')
        driver.close()
        return token

可能遇到的問題

geckodriver

由於我是使用 Ubuntu + Firefox 來執行,而在 Firefox 後續的版本中,
並沒有內建 geckodriver,需要手動安裝到電腦之中。

Crontab

若你是使用 Crontab 來跑的話,由於 Crontab 本身並不會有 output 的輸出,
意思是指跑 Crontab 時,也不會自己跳出 Firefox 的畫面;
會導致 Selenium 發生錯誤。

解決的方式是使用 pyvirtualdisplay 來弄一個虛擬的畫面,
給 Firefox 作為使用。
上面的程式碼則補上:

...
from pyvirtualdisplay import Display

def get_token():
    # 先準備好虛擬的畫面
    display = Display(visible=0, size=(800, 600))
    display.start()
    ...
    # 最後記得關掉畫面
    display.stop()
    return token

大致上是這樣,就可以利用 Selenium 的方式取得痞客邦的 Access Token!

Crontab

Crontab

Crontab 在 Ubuntu 裏頭是預載的,其功能是可以根據時間參數來執行工作排程;
它的格式如下:

* * * * * command to be executed

依序分別是 分鐘[0-59]小時[0-23]日期[1-31]月份[1-12]星期[0-6]command

其中 星期 0 = 天的意思。

符號

「*」:不設限
「,」:分隔時段。例如:30 10,20 * * * command,代表早上十點半和下午八點半執行。
「-」:一段時間範圍。例如:15 9-12 * * * command,代表從九點到十二點的每個 15 分都執行一次。
「/n」:表示每個 n 單位間隔。例如:*/5 * * * * command,代表每隔 5 分鐘執行一次。

* * * * *:每隔一分鐘執行一次。

你也可以使用 @ 來取代五個參數:

@reboot:僅在開機的時候執行一次。
@yearly:一年執行一次,和0 0 1 1 * command效果一樣。
@annually:(和@yearly一樣)
@monthly:一個月執行一次,和0 0 1 * * command效果一樣。
@weekly:一個星期執行一次,和0 0 * * 0 command效果一樣。
@daily:每天執行,和0 0 * * * command效果一樣。
@midnight:(和@daily一樣)
@hourly :每小時執行,和0 * * * * command效果一樣。

輸出

另外,需要設立 command 輸出的地點或方式;
如使用 Postfix 或是直接在 command 後方加上 >> /file_path,

sudo apt-get install postfix

設定完後,可以在下列位置查看 output

sudo tail -f /var/mail/<user>

而如果需要清空 mail 內容的話,
則利用

> /var/mail/<user>

除錯

Ubuntu 的話,cron log 會和 syslog 寫在一塊,使用下列的 function 來區隔:

grep CRON /var/log/syslog

編輯

crontab -l
crontab -e
crontab -r

-l:列出所有的 cron
-e:編輯
-r:移除

並可以使用

/etc/init.d/cron restart

來重新啟用 crontab