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']