1. 程式人生 > >python爬蟲-->表單互動

python爬蟲-->表單互動

前幾篇博文中,我們的程式下載的靜態網頁總是返回相同的內容。在本篇博文中,我們將與網頁進行互動,根據使用者輸入返回對應的內容
本篇博文將主要介紹以下兩種方式進行表單互動

  • 使用cookie登入網頁,更新網頁內容(較麻煩)
  • 使用Mechanize模組實現自動化表單處理(較簡單)

這裡寫圖片描述

這裡需要注意form標籤的action,enctype,method屬性以及兩個input域。action:表示表單資料提交的地址。本例為#,也就是和登入表單相同的URL。enctype:資料提交的編碼。method:提交方法,post表示通過請求體向伺服器提交表單。input標籤,重要的屬性是name,用於設定提交到伺服器端時某個域的名稱。

當我們登入時需要提交賬號密碼,然後點選登入按鈕才能把資料提交到伺服器。下面是嘗試自動化處理該流程程式碼。

LOGIN_EMAIL = '[email protected]'
LOGIN_PASSWORD = 'example'
LOGIN_URL = 'http://example.webscraping.com/places/default/user/login'


def login_basic():
    """fails because not using formkey
    """
    data = {'email': LOGIN_EMAIL, 'password': LOGIN_PASSWORD}
    encoded_data = urllib.urlencode(data)
    request = urllib2.Request(LOGIN_URL, encoded_data)
    response = urllib2.urlopen(request)
    print
response.geturl() login_basic()

因為登入表單十分嚴格。除了郵箱和密碼外,還需要提交另外幾個域。我們編寫一個函式,來提取表單中所有的input標籤詳情。

def parse_form(html):
    """extract all input properties from the form
    """
    tree = lxml.html.fromstring(html)
    data = {}
    for e in tree.cssselect('form input'):
        if e.get('name'):
            data[e.get('name'
)] = e.get('value') return data

看看獲取了哪些表單資料:

html = urllib2.urlopen(LOGIN_URL).read()
data = parse_form(html)
print data

列印結果:
{‘remember_me’: ‘on’, ‘_formname’: ‘login’, ‘_next’: ‘/places/default/index’, ‘_formkey’: ‘3049b530-61de-43dc-b1fa-57b105b37769’, ‘password’: ”, ‘email’: ”}

這裡需要注意_formkey屬性,伺服器端使用這個唯一的ID來避免表單多次提交。每次載入網頁時,都會產生不同的ID,然後伺服器端就可以通過這個給定的ID來判斷表單是否已經提交過。

再把獲取的所有表單資料全部進行提交,看能不能成功跳轉登入

def login_formkey():
    """fails because not using cookies to match formkey
    """
    html = urllib2.urlopen(LOGIN_URL).read()
    data = parse_form(html)
    data['email'] = LOGIN_EMAIL
    data['password'] = LOGIN_PASSWORD
    encoded_data = urllib.urlencode(data)
    request = urllib2.Request(LOGIN_URL, encoded_data)
    response = urllib2.urlopen(request)
    print response.geturl()
login_formkey()

我們缺失了一個重要的組成部分–cookie。當普通使用者載入登入介面時,_formkey的值會儲存在cookie中,然後該值會和提交的登入表單資料中的_formkey進行比較。下面是使用urllib2.HTTPCookieProcessor類的程式碼:

def login_cookies():
    """working login
    """
    cj = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    html = opener.open(LOGIN_URL).read()
    data = parse_form(html)
    data['email'] = LOGIN_EMAIL
    data['password'] = LOGIN_PASSWORD
    encoded_data = urllib.urlencode(data)
    request = urllib2.Request(LOGIN_URL, encoded_data)
    response = opener.open(request)
    print response.geturl()
    return opener

login_cookies()

支援內容更新的登入指令碼擴充套件
我們利用LOGIN_EMAIL = ‘[email protected]
LOGIN_PASSWORD = ‘example’ 登入http://example.webscraping.com/places/default/user/login,開啟任意一個國家的網頁,點選左下角的edit按鈕。這裡我們編寫一個函式,每次執行這個函式時都會使得該國家人口數量加1。

import urllib
import urllib2
import mechanize
import login

COUNTRY_URL = 'http://example.webscraping.com/places/default/edit/Afghanistan-1'

def edit_country():
    opener = login.login_cookies()
    country_html = opener.open(COUNTRY_URL).read()
    data = login.parse_form(country_html)
    import pprint; pprint.pprint(data)
    print 'Population before: ' + data['population']
    data['population'] = int(data['population']) + 1
    encoded_data = urllib.urlencode(data)
    request = urllib2.Request(COUNTRY_URL, encoded_data)
    response = opener.open(request)

    country_html = opener.open(COUNTRY_URL).read()
    data = login.parse_form(country_html)
    print 'Population after:', data['population']

使用Mechanize模組實現自動化表單處理
使用Mechanize模組不再需要管理cookie,而且訪問表單輸入框也更加容易。

def mechanize_edit():
    """Use mechanize to increment population
    """
    # login
    br = mechanize.Browser()
    br.open(login.LOGIN_URL)
    br.select_form(nr=0)
    print br.form
    br['email'] = login.LOGIN_EMAIL
    br['password'] = login.LOGIN_PASSWORD
    response = br.submit()
    print response.geturl()##列印輸出http://example.webscraping.com/places/default/index登入成功

    # edit country 更新內容
    br.open(COUNTRY_URL)
    br.select_form(nr=0)
    print 'Population before:', br['population']
    br['population'] = str(int(br['population']) + 1)##注意要字串型別的值,否則會丟擲TypeError
    br.submit()

    # check population increased
    br.open(COUNTRY_URL)
    br.select_form(nr=0)
    print 'Population after:', br['population']