requests請求狀態保持-登入github為例

wt321088發表於2018-06-20

模擬登入-狀態保持

​ ---- 以登入 github 為例

1 .頁面分析:

  • 登入頁 ( https://github.com/login ) 網頁原始碼能找到 form 表單的提交方式是 post 請求,登入賬號並且開啟 Chrome 瀏覽器的除錯工具 ( F12 - Network - all ) 檢視提交請求,能到以下資訊

    • 提交資料為:

      commit: Sign in
      utf8: ✓
      authenticity_token: UCGHpjglqtJ70hl/3ygpPUPVGLukUm0ACcYJ47BGb4pyyz6s+V5GHQSYQp419+EYcyUfJx3j/cGJziQonKIeAw==
      login: email
      password: passwd
      

    其中 login 和 password 的引數值為使用者的郵箱和密碼,每個人不一樣。

    分析 authenticity_token 這個引數值是沒有規律的,但是細心點會發現,在登入頁的網頁原始碼中的 form 標籤中有一個 type 為 "hidden" 並且 name 引數 為 "authenticity_token" 的 <input> 子標籤,其 value 引數值就是提交資料中的值。

2.流程說明

  • 使用 requests.Session() 方法建立一個 Session 物件,這樣可以做到模擬同一個會話而不用擔心 Cookies 的問題。
  • get 請求登入頁,xpath 解析出 "authenticity_token" 的值。為避免反爬,請加上請求頭 headers。
  • 構建 post 請求,提交資料資料為上述的那些,請自行驗證其正確性。為避免反爬,請加上請求頭 headers。
  • 登入成功後會自動重定向到首頁,此時已經做到了session 的狀態保持。
  • 請求個人主頁解析自己的 使用者名稱 和 郵箱。(這一步只是測試。)

3.程式碼示例

  • demo版(面向流程)只是為了跑通流程,之後會改為物件導向版本。

    import requests
    from lxml import etree
    
    # 登入頁get請求URL
    base_url = 'https://github.com/login'
    
    # 登入post提交URL
    login_url = 'https://github.com/session'
    
    # github個人主頁
    profile_url = 'https://github.com/settings/profile'
    
    # 登入頁headers
    headers = {
      'Referer':'https://github.com/',
      'Host':'github.com',
      'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
    }
    
    # post 提交資料
    post_data = {
      'commit':'Sign in',
      'utf8':'✓',
      'login':'email@mail.com',
      'password':'password',
    }
    
    # 保持會話式請求
    s = requests.Session()
    # 請求登入頁獲取 token
    res_login = s.get(url=base_url, headers=headers)
    
    if res_login.status_code == requests.codes.ok:
      index_html = res_login.text
      index_html_obj = etree.HTML(index_html)
      # 獲取 authenticity_token,在post提交時會作為一個鍵值對傳入
      token = index_html_obj.xpath('//div[@id="login"]/form/input[@name="authenticity_token"]/@value')[0]
      post_data['authenticity_token'] = token
      # 登入提交
      res_index = s.post(url=login_url, headers=headers, data=post_data)
      if res_index.status_code == requests.codes.ok:
          print("登入成功,開始請求個人主頁")
    
          # 進入個人中心
          res_profile = s.get(url=profile_url, headers=headers)
          if res_profile.status_code == requests.codes.ok:
              print("請求個人主頁成功,開始解析資料")
              profile_html = res_profile.text
              profile_obj = etree.HTML(profile_html)
              username = profile_obj.xpath('//div[@class="column two-thirds"]/dl[contains(@class,"form-group")]/dd/input[@id="user_profile_name"]/@value')[0]
              print("使用者名稱:", username)
              email = profile_obj.xpath('//div[@class="column two-thirds"]/dl[2]/dd/select/option[2]/text()')[0]
              print("郵箱:", email)
          else:
              print("請求個人主頁失敗")
      else:
          print("登入失敗")
    
    
  • 物件導向版本

    import requests
    from lxml import etree
    
    class GithubLogin(object):
      def __init__(self):
          # 登入頁get請求URL
          self.base_url = 'https://github.com/login'
          # 登入post提交URL
          self.login_url = 'https://github.com/session'
          # github個人主頁
          self.profile_url = 'https://github.com/settings/profile'
          # 登入頁headers
          self.headers = {
              'Referer': 'https://github.com/',
              'Host': 'github.com',
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
          }
          self.session = requests.Session()
      
        # 獲取登入頁的 authenticity_token 值
      def token(self):
          res = self.session.get(url=self.base_url, headers=self.headers)
          if res.status_code == requests.codes.ok:
              res_obj = etree.HTML(res.text)
              token_value = res_obj.xpath('//div[@id="login"]/form/input[@name="authenticity_token"]/@value')[0]
              return token_value
      
      def login(self, email, passwd):
          post_data = {
              'commit': 'Sign in',
              'utf8': '✓',
              'login': email,
              'password': '753159wt',
              'authenticity_token': self.token()
          }
            
            # 登入 post 提交表單
          res_index = self.session.post(url=self.login_url, headers=self.headers, data=post_data)
          if res_index.status_code == requests.codes.ok:
              self.repository(res_index.text)
          
            # 請求個人中心頁面
          res_profile = self.session.get(url=self.profile_url, headers=self.headers)
          if res_profile.status_code == requests.codes.ok:
              self.getProfile(res_profile.text)
      
      def repository(self, text):
          res_obj = etree.HTML(text)
          repo_list = res_obj.xpath('//div[@class="Box-body"]/ul/li//a/@href')
          for repo in repo_list:
              print(repo)
              
      def getProfile(self, text):
          res_obj = etree.HTML(text)
          username = res_obj.xpath('//div[@class="column two-thirds"]/dl[contains(@class,"form-group")]/dd/input[@id="user_profile_name"]/@value')[0]
          print("使用者名稱:", username)
          email = res_obj.xpath('//div[@class="column two-thirds"]/dl[2]/dd/select/option[2]/text()')[0]
          print("郵箱:", email)
          
          
    if __name__ == '__main__':
      alice_login = GithubLogin()
      alice_login.login('email@mail.com', 'password')
    
    

相關文章