1. 程式人生 > >python網路爬蟲——表單互動

python網路爬蟲——表單互動

將與網頁進行互動,根據使用者輸入返回對應的內容。有些網站需要在登入後才能訪問某個網頁,在登入之前不允許訪問。所以使用使用者表單互動傳遞引數登入。
表單方法
HTML定義了兩種向伺服器提交資料的方法,分別是GET和POST。使用GET時,會將類似?name1=value1&name2=value2的資料新增到URL中,這串資料被稱為“查詢字串”。由於瀏覽器存在URL長度限制,因此這種方法只適用於少量資料的場景。這種方法應當用於從伺服器獲取資料,而不是修改資料。在使用POST請求時,資料在請求體中傳送,與URL保持分離。敏感資料只應使用POST請求進行傳送,以免將資料暴露在URL中。POST資料在請求體中如何表示需要依賴所使用的編碼型別。伺服器還支援其他HTTP方法。
登入表單


登入這裡寫連結內容。需要用FirebugLite對這個表單進行分析。
這裡寫圖片描述
包括幾個重要組成部分,分別是form標籤action、enctype和method屬性,以及兩個input域。action屬性用於設定表單資料提交的地址,encytpe屬性用於設定資料提交的編碼。
當普通使用者通過瀏覽器開啟該網頁時,需要輸入郵箱和密碼,然後單擊按鈕將資料提交到服務端。如果登入成功,則會跳轉到主頁;否則會跳轉到登入頁。
cookie
cookie某些網站為了辨明使用者身份、進行session跟蹤而儲存在使用者本地終端上的資料當普通使用者載入登入表單時,_formkey的值將會儲存在cookie中然後該值會與提交的登入表單資料種的_formkey值進行對比。

# coding:utf-8
import cookielib
import urllib
import urllib2
import lxml.html

# 使用者郵箱


LOGTN_EMAIL='[email protected]'

#使用者密碼
LOGTN_PASSWORD='123456'

# 登入表單地址
LOGIN_URL='http://example.webscraping.com/places/default/user/login?_next=/places/default/index'


def login():

    # 來捕獲cookie並在後續連線請求時重新發送,建立cj處理器
    cj=cookielib.CookieJar()
    # 自定義opener,並將opener和CookieJar物件繫結
    # urllib2.build_opener不同於urllib2.urlopen的是
    # 它支援驗證、cookie或者其他HTTP高階功能
    opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    # 下載登入頁面的原始碼
    html=opener.open(LOGIN_URL).read()
    data=parse_form(html)
    data['email']=LOGTN_EMAIL
    data['password']=LOGTN_PASSWORD
    # 獲取資料的編碼格式
    encoded_data=urllib.urlencode(data)
    # 給url傳送請求和編碼格式
    request=urllib2.Request(LOGIN_URL,encoded_data)
    # 獲取響應
    response=opener.open(request)

    print response.geturl()
    return opener


def parse_form(html):
    """從表單中獲取輸入的資料"""

    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

if __name__=='__main__':
    login()

這裡寫圖片描述

從返回的URL地址可以看出伺服器接受了提交的表單,response的URL是主頁。
從瀏覽器載入cookie
先在瀏覽器中手工登入,然後使用之前的程式碼得到cookie,從而實現自動登入。以Firefox為例。Firefox在sqlite資料庫中儲存cookie,在JSON檔案中儲存session,都可以會獲取。
不同的作業系統,Firefox儲存session檔案的位置不同。

  • Linux:~/.mozilla/firefox/*.default/sessionstore.js
  • OSX:~/Library/Application Support/Firefox/Profiles/*.default/sessionstore.js
  • Window:%APPDATA%/Roaming/Mozilla/Firefox/Profiles/*.default/sessionstore.js
def login_firefox():
    """從Firefox中載入cookie"""

    # 獲取session快取的檔案路徑
    session_filename=find_ff_sessions()
    #獲取一個cookie處理器,包含登入使用者的資訊
    cj=load_ff_sessions(session_filename)
    opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

    # 爬取主頁資訊,如果登入成功則會返回主頁資訊
    html=opener.open(COUNTRY_URL).read()

    # 檢測是否登入成功
    tree=lxml.html.fromstring(html)
    print tree.cssselect('ul#navbar li a')[0].text_content()
    return opener


def load_ff_sessions(session_filename):
    cj=cookielib.CookieJar()
    if os.path.exists(session_filename):
        try:
            json_data=json.loads(open(session_filename,'rb').read())
        except ValueError as e:
            print e
        else:
            for window in json_data.get('windows',[]):
                for cookie in window.get('cookies',[]):
                    pprint.pprint(cookie)
                    c=cookielib.Cookie(0,cookie.get('name',''),
                                       cookie.get('value',''),
                                        None,False,
                                        cookie.get('host',''),
                                       cookie.get('host','').startswith('.'),
                                       cookie.get('host','').startswith('.'),
                                       cookie.get('path',''),False,False,
                                       str(int(time.time())+3600*24*7),
                                       False,None,None,{})
                    cj.set_cookie(c)
    else:
        print 'Session filename does not exist:',session_filename

    return cj

def find_ff_sessions():
    # 不同的作業系統Firefox中的session儲存路徑
    paths=[
        '~/.mozilla/firefox/*.default/sessionstore-backups',
        '~/Library/Application Support/Firefox/Profiles/*.default',
        '%APPDATA%/Roaming/Mozilla/Firefox/Profiles/*.default'
    ]

    for path in paths:
        filename=os.path.join(path,'*.js')

        # 返回指定路徑中所有匹配的檔案
        matches=glob.glob(os.path.expanduser(filename))
        if matches:
            return matches[0]

if __name__=='__main__':
    login_firefox()

檢測是否登入成功,在登入成功的主頁面

這裡寫圖片描述
檢測結果:
這裡寫圖片描述
支援內容更新的登入指令碼擴充套件
自動化登入成功執行後,還可以繼續擴充套件該指令碼,時其與網站進行互動,更新國家資料。

這裡寫圖片描述
編寫一個指令碼,每次執行時,都會使該國家的人口數量加1.

 Edit_URL='http://example.webscraping.com/places/default/edit/Afghanistan-1'
    opener=login()
    country_html=opener.open(Edit_URL).read()
    data=parse_form(country_html)
    pprint.pprint(data)

這裡寫圖片描述
更新資料

 data['population']=int(data['population'])+1
    encoded_data=urllib.urlencode(data)
    request=urllib2.Request(Edit_URL,encoded_data)
    response=opener.open(request)

這裡寫圖片描述
可以對任意欄位隨意進行四ugai和測試。這裡適用的表單技術可以應用於抓取時的複雜表單互動當中。
使用Mechanize模組實現自動錶單處理
該模組提供了表單互動的高階介面。

import mechanize


    edit_url='http://example.webscraping.com/places/default/edit/Afghanistan-1'
    # 開啟瀏覽器
    br=mechanize.Browser()
    br.open(LOGIN_URL)
    # 選擇表單
    br.select_form(nr=0)
    br['email']=LOGTN_EMAIL
    br['password']=LOGTN_PASSWORD
    # 提交
    response=br.submit()
    br.open(edit_url)
    br.select_form(nr=0)
    br['population']=str(int(br['population'])+1)
    br.submit()

不再需要管理cookie,而且訪問表單輸入框也更加容易處理。
首先建立一個Mechanize瀏覽器物件,然後定位到登入URL,選擇登入表單。直接向瀏覽器物件傳遞名稱和值,來設定選定表單的輸入框內容。然後定位到國家編輯頁面,使用表單增加人數。
這裡寫圖片描述