Python爬蟲實戰之(四)| 模擬登入京東商城

Python資料科學發表於2018-04-11

作者:xiaoyu

微信公眾號:Python資料科學

知乎:Python資料分析師


前兩篇和大家分享了爬蟲中http的一些概念和使用方法,基礎篇我們主要介紹了http的請求頭,高階篇我們主要介紹了cookiesession(具體可以點選上面連結進行回顧)。但其實在爬蟲中還有很多關於http的內容需要了解,例如 tokenoauth等。對於這些概念博主將在後續文章中逐一的詳細介紹,本篇主要針對前兩篇內容與大家分享一個模擬登入的實戰例子。

開始想以知乎為例,但是看到網上關於知乎模擬登入的教程太多了,所以就以“京東”為例。

大家都知道,京東是不需要登入就可以訪問主頁內容的,因此模擬登入的意義在於檢視個人資訊,比如可以獲取個人的交易資訊(購物車商品,購物歷史記錄,待收貨商品資訊等),或者賣家的商品銷售資訊和評論等等。

好了,瞭解背景過後,讓我們開始模擬登入吧。

準備工作

大家都知道,模擬登入其實就是通過http的post請求方式來提交使用者資訊的(使用者名稱和密碼)。對於瀏覽器而言,只輸入使用者名稱和密碼就可以登陸了(偶爾有驗證碼),那是因為瀏覽器在背後都幫你處理好了。而爬蟲的模擬登入過程需要我們自己解決,因此我們需要弄清楚瀏覽器的那些背後操作是如何進行的才能對症下藥。

難點分析:

  • 尋找提交表單所需欄位資訊
  • cookie資訊的獲取和使用
  • 驗證碼的處理

我們開啟瀏覽器,博主用的Chrome瀏覽器。首先使用Ctrl+Shift+N進入乾淨的無痕模式,防止之前的cookie資料造成干擾。

Python爬蟲實戰之(四)| 模擬登入京東商城

輸入了京東的登陸網址 passport.jd.com/new/login.a…,進入如下登入介面。

Python爬蟲實戰之(四)| 模擬登入京東商城

表單欄位資訊

現在我們通過開發者工具來看看瀏覽器背後都幹了什麼吧。有的朋友提問到,輸入使用者名稱和密碼後頁面直接跳轉到主頁面了,看不到我們要的資料了。其實這裡只需要故意將你的密碼輸錯不進入跳轉就可以解決了。

點選登入,然後我們看到有個FormData,這就是瀏覽器每次向伺服器提交的表單資訊。

Python爬蟲實戰之(四)| 模擬登入京東商城

第一眼看過去感覺快要無望了。但是彆著急,這些欄位資訊其實都是有處可尋的。我們Ctrl+U開啟京東登入頁面的原始碼裡,然後Ctrl+F 試著搜一搜這些欄位資訊。

先搜第一個uuid欄位,發現它就在原始碼中,緊著後面是一些其它的欄位資訊,那就齊活了。我們看到除了loginname,nloginpwd,authcode,其他的欄位全都是hidden的型別,也就是被隱藏了的欄位。

Python爬蟲實戰之(四)| 模擬登入京東商城

好了,那下一步就自然知道幹什麼了。我們可以直接請求登入頁面原始碼提取欄位資訊了。

Cookie的處理

Cookie可以通過使用http的Cookiejar定製opener進行獲取,也可以直接使用requests模組來實現。

requests模組實現起來比較方便,因為內部已經封裝好了自動處理Cookie的功能。第一次傳送請求可以通過伺服器獲取Cookie,後續的請求則會自動帶著已獲取的Cookie資訊進行傳送。當然,也可以手動新增Cookie,手動新增的Cookie優先順序高,將會覆蓋預設的資訊。

為了說明模擬登入的用法,本篇博主將使用簡便的requests模組來完成對Cookie的處理。

驗證碼的處理

驗證碼的處理方法也有多種,可以分為自動識別的和手動識別

  • 手動處理驗證碼就是通過驗證碼連結將驗證碼圖片下載到本地,然後手動敲入完成資訊錄入。
  • 自動識別是使用一些高階的演算法技術來完成的,可以使用OCR智慧圖文識別,機器學習進行識別訓練等。

本篇將選擇手動錄入驗證碼,旨在理解模擬登入的過程。

程式碼實現

初始資訊配置

class JD_crawl:
    def __init__(self, username, password):
        self.headers = {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36'
                                      ' (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36',
                        'Referer': 'https://www.jd.com/',
                        }
        self.login_url = "https://passport.jd.com/new/login.aspx"
        self.post_url = "https://passport.jd.com/uc/loginService"
        self.auth_url = "https://passport.jd.com/uc/showAuthCode"
        self.session = requests.session()
        self.username = username
        self.password = password
複製程式碼
  • 建立了一個JD_crawl的類,設定了例項的headerssession會話物件,以及三個後面請求需要用到的url。
  • 因為整個登入是一個完整的過程,所以後續若干請求需要共同使用同一個session會話物件

提取表單登入資訊

    def get_login_info(self):
        html = self.session.get(self.login_url, headers=self.headers).content
        soup = BeautifulSoup(html, 'lxml')

        uuid = soup.select('#uuid')[0].get('value')
        eid = soup.select('#eid')[0].get('value')
        fp = soup.select('input[name="fp"]')[0].get('value')  # session id
        _t = soup.select('input[name="_t"]')[0].get('value')  # token
        login_type = soup.select('input[name="loginType"]')[0].get('value')
        pub_key = soup.select('input[name="pubKey"]')[0].get('value')
        sa_token = soup.select('input[name="sa_token"]')[0].get('value')

        auth_page = self.session.post(self.auth_url,
                                      data={'loginName': self.username, 'nloginpwd': self.password}).text
        print(auth_page)
        if 'true' in auth_page:
            auth_code_url = soup.select('#JD_Verification1')[0].get('src2')
            auth_code = str(self.get_auth_img(auth_code_url))
        else:
            auth_code = ''

        data = {
            'uuid': uuid,
            'eid': eid,
            'fp': fp,
            '_t': _t,
            'loginType': login_type,
            'loginname': self.username,
            'nloginpwd': self.password,
            'chkRememberMe': True,
            'pubKey': pub_key,
            'sa_token': sa_token,
            'authcode': auth_code
            }
        return data
複製程式碼
  • 首先對登入的login_url發起請求,獲取登陸頁面原始碼後通過BeautifulSoup解析工具ccs選擇器來提取隱藏欄位資訊。
  • loginname,nloginpwd,authcode三個非隱藏欄位需要使用者手動錄入。
  • 對於是否需要錄入驗證碼的問題,可以通過請求https://passport.jd.com/uc/showAuthCode(程式碼中的auth_url)來判斷。

請求結果是一個如下格式的字串。

請求結果: ({"verifycode":xxx})
xxx:true 或者 false
複製程式碼

因此可以簡單的檢視結果中是否有true來判斷是否需要驗證碼。

  • 如果為true就需要呼叫驗證碼函式方法,將驗證碼圖片下載,輸入圖片上的驗證碼,並賦給authcode欄位進行表單提交完成登入。
  • 如果為false則不需要驗證碼,authcode欄位為空字串。

一般當我們多次輸入了錯誤的賬號或密碼時,構成安全危險,就會提示輸入驗證碼。

獲取驗證碼

    def get_auth_img(self, url):
        auth_code_url = 'http:{}&yys={}'.format(url, str(int(time.time()*1000)))
        auth_img = self.session.get(auth_code_url, headers=self.headers)
        with open('authcode.jpg', 'wb') as f:
            f.write(auth_img.content)
        code_typein = input('請根據下載圖片輸入驗證碼:')
        return code_typein
複製程式碼
  • 從原始碼獲取的驗證碼連結是一個相對連結src2
     src2="//authcode.jd.com/verify/image?a=1&acid=dcb4370b-2763-44e6-83ff-4b89bc01193d&uid=dcb4370b-2763-44e6-83ff-4b89bc01193d"
複製程式碼

因此,我們需要將它補全,在src2前面拼接字串 http: 。但是當我們嘗試這個url的時候會發現圖片並沒有下載成功,為什麼呢?

因為還需要在結尾加上時間戳,接著看後邊的 onclick,它的字串中有和src2完全一樣的連結,但在結尾處多了 &yys= ''。通過觀察內容發現有 datetime 字樣,於是可以判斷這可能是一個時間戳字串

onclick="this.src= document.location.protocol +'//authcode.jd.com/verify/image?a=1&acid=dcb4370b-2763-44e6-83ff-4b89bc01193d&uid=dcb4370b-2763-44e6-83ff-4b89bc01193d&yys='+new Date().getTime();$('#authcode').val('');"
複製程式碼

小提示:

時間戳(引自百度百科):

時間戳是指格林威治時間1970年01月01日00時00分00秒(北京時間1970年01月01日08時00分00秒)起至現在的總秒數。

時間戳在Python中可以用time模組來完成:

time.time()*1000
複製程式碼
  • 將驗證碼圖片存為jpg格式,儲存在專案檔案目錄下。

Python爬蟲實戰之(四)| 模擬登入京東商城

可以看到圖片就在目錄下,我們雙擊開啟然後按照圖片輸入驗證碼。

Python爬蟲實戰之(四)| 模擬登入京東商城

模擬登入

    def login(self):
        data = self.get_login_info()
        headers = {
                    'Referer': self.post_url,
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36'
                                  ' (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36',
                    'X-Requested-With': 'XMLHttpRequest'
                  }
        try:
            login_page = self.session.post(self.post_url, data=data, headers=headers)
            print(login_page.text)
        except Exception as e:
            print(e)
複製程式碼
  • 根據獲取的表單登入資訊進行提交登入。
  • 請求的url是 https://passport.jd.com/uc/loginService
  • 注意這裡的 Service 中的 "S" 是大寫。

如果登入成功,則會顯示以下字串。

({"success":"http://www.jd.com"})
複製程式碼

登入驗證及結果

為了驗證登入真的成功了,現將博主的購物車拿出來做實驗,如果在獲取原始碼中找到了指定商品名稱,那麼就說明成功了。

Python爬蟲實戰之(四)| 模擬登入京東商城

好吧,都是之前隨便放的,就以牙線為測試目標吧。

    def shopping(self):
        carshop = self.session.post('https://cart.jd.com/cart.action', headers=self.headers)
        print(carshop.text)
複製程式碼

簡單的請求了一下購物車url。在下載的原始碼中搜尋“牙”,然後找了目標。

Python爬蟲實戰之(四)| 模擬登入京東商城

程式碼連結:github.com/xiaoyusmd/j…

總結

本篇主要介紹了京東商城的模擬登入方法,當然還有一些網站的登入機制比較複雜,比如weibo登入需要呼叫api,需要我們詳細閱讀api說明。

後續將會分享更多模擬登入的內容,歡迎大家指正。

參考連結: https://github.com/xchaoinfo/fuck-login http://blog.csdn.net/weixin_38206454/article/details/78655209?locationNum=2&fps=1


關注微信公眾號Python資料科學,獲取 120G 人工智慧 學習資料。

Python爬蟲實戰之(四)| 模擬登入京東商城

Python爬蟲實戰之(四)| 模擬登入京東商城

相關文章