python爬虫学习:模拟登陆

                                                                                                                                  前面学习了如何在?get?的时候想服务器发送多变的请求数据,从而达到搜索的效果,而实际上?搜索是简单的登陆?!所以本文将要介绍如何向百度服务器发送?post?数据,从而达到模拟登陆百度的效果。

                                                                                                                                  1. 首先打开?firefox?浏览器,清除网页所有的历史纪录,这是为了防止以前的?Cookie?影响服务器返回的数据。
                                                                                                                                  2. F12?打开?firebug?,进入百度首页,点击?网络?->?清除?,这是为了删掉打开百度首页而弹出来的?html,方便后面的查找?html数据。
                                                                                                                                  3. 点击登陆按钮,依次填写账号、密码、验证码,点击?登陆?,在?firebug?中点击?保持?,这是为了防止登陆成功后,登陆表单的?html?被清除。

                                                                                                                                  在?firebug?中,找到如下一行?POST?login?:

                                                                                                                                  点击前面的?+?号 ->?post?,可以看到提交的表单,这个就是点击登陆后,网页向百度服务器后端发送的?登陆请求表单,表单中包含了?账号密码其他?等信息:

                                                                                                                                  如果百度后台认为此?登陆表单请求?是正确的后,会在?头信息?->?响应头信息?中返回一个?Set-Cookie?。当我们登陆成功后,关闭浏览器,下次再打开浏览器的时候发现百度还是处于一种登陆的状态,这就是和?Cookie?有关。在百度登陆成功后会返回一个?Cookie?储存到浏览器中,下次再打开百度的时候,浏览器中的?头信息?->?请求头信息?中会携带一个?Cookie?,这个?Cookie?就是百度服务器判断你以前是否登陆过百度。而这个?Cooike?就是?Set-Cookie?加工而来的!那么重点来了,如果要用代码模拟登陆百度,应该要具备以下几个步骤:

                                                                                                                                  1. 构造请求表单
                                                                                                                                  2. 请求成功后获取?Cookie?(这个?Cookie?并非?Set-Cookie)
                                                                                                                                  3. 在请求头部?header?中携带这个?Cookie?,就可以以登陆过后的身份访问百度

                                                                                                                                  原理讲清楚了,那么下面开始实践!

                                                                                                                                  '''
                                                                                                                                  遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料
                                                                                                                                  '''

                                                                                                                                  构造请求表单

                                                                                                                                  在上面的?POST?login?中发现百度的请求表单还是挺多的,那么如何表单中判断哪些是变化的那些事不变的?再一次清空?firefox?的全部历史纪录,清除?firebug?的?html?,重新在百度首页点击?登陆?,填写?账号错误的密码验证码,复制?POST?login?中的?post?信息下来,然后重复前面的步骤,就可以得到很多?post?信息,拿出来对比就可以知道哪些信息是变化的了。这里要解释一下为什么要填写?错误的密码,因为密码错误啦,登陆框就会一直都在啊,免去了清除?全部历史纪录?和清除?html?的步骤。最后对比的情况如下:

                                                                                                                                  可以发现,请求的表单有

                                                                                                                                  staticpage
                                                                                                                                  charset
                                                                                                                                  token
                                                                                                                                  tpl
                                                                                                                                  subpro
                                                                                                                                  apiver
                                                                                                                                  tt
                                                                                                                                  codestring
                                                                                                                                  safeflg
                                                                                                                                  u
                                                                                                                                  isPhone
                                                                                                                                  detect
                                                                                                                                  gid
                                                                                                                                  quick_user
                                                                                                                                  logintype
                                                                                                                                  logLoginType
                                                                                                                                  idc
                                                                                                                                  loginmerge
                                                                                                                                  splogin
                                                                                                                                  username
                                                                                                                                  password
                                                                                                                                  verifycode
                                                                                                                                  mem_pass
                                                                                                                                  rsakey
                                                                                                                                  crypttype
                                                                                                                                  ppui_logintime
                                                                                                                                  countrycode
                                                                                                                                  fp_uid
                                                                                                                                  dv
                                                                                                                                  callback
                                                                                                                                  

                                                                                                                                  其中,被红框框起来的表单是多次请求变化的:

                                                                                                                                  callback
                                                                                                                                  tt
                                                                                                                                  token
                                                                                                                                  ppui_logintime
                                                                                                                                  rsakey
                                                                                                                                  verifycode
                                                                                                                                  

                                                                                                                                  其中很明显可以看出来的是:

                                                                                                                                  1. tt?时间戳
                                                                                                                                  2. verifycode?验证码

                                                                                                                                  那么就剩下几个请求表单还暂时无法得知,首先查找?callback

                                                                                                                                  >1. 在 `firebug` 中勾选脚本,点击 `{}`
                                                                                                                                  >2. 搜索中勾选 **多个文件**
                                                                                                                                  >3. 在搜索框中搜索 `callback`
                                                                                                                                  

                                                                                                                                  一直搜索,最后发现?callback?和?getUniqueId?的生成有关:

                                                                                                                                  那么换着?getUniqueId?来搜索,得到如下?javascrip?代码:

                                                                                                                                  e.getUniqueId = function (e) {
                                                                                                                                  return e + Math.floor(2147483648 * Math.random()).toString(36)
                                                                                                                                  },
                                                                                                                                  

                                                                                                                                  和上面的?JavaScrip?整理起来,那么?callback?的生成代码为:

                                                                                                                                  function callback(){
                                                                                                                                          return 'bd__cbs__'+Math.floor(2147483648 * Math.random()).toString(36)
                                                                                                                                      }
                                                                                                                                  

                                                                                                                                  找到?callback?后,接下来要去寻找?token?。在?firebug?中寻找?token?第一次在哪里出现。最后发现首次出现在网址:

                                                                                                                                  https://passport.baidu.com/v2/api/?getapi&tpl=mn&apiver=v3&tt=1495185331704&class=login&gid=63F95D8-F402-4128-A98B-C7D3C19B8F89&logintype=dialogLogin&callback=bd__cbs__3cagws
                                                                                                                                  

                                                                                                                                  这个网址返回的是一个?Json?:

                                                                                                                                  bd__cbs__3cagws({"errInfo": {"no": "0"},
                                                                                                                                                   "data": {"rememberedUserName": "", "codeString": "", "token": "6245a75e6ba48d39033a8c31dfcb37c7",
                                                                                                                                                            "cookie": "1", "usernametype": "", "spLogin": "rate", "disable": "",
                                                                                                                                                            "loginrecord": {'email': [], 'phone': []}}})
                                                                                                                                  

                                                                                                                                  第一次?token?出现的地方找到了,那么分析一下请求出?token?的网址,网址中涉及到的变量有:

                                                                                                                                  tpl
                                                                                                                                  apiver
                                                                                                                                  tt
                                                                                                                                  class
                                                                                                                                  gid
                                                                                                                                  logintype
                                                                                                                                  callback
                                                                                                                                  

                                                                                                                                  为了查看哪些变量是变化的,就再次进行多次登陆。最后发现,变化的是:

                                                                                                                                  tt
                                                                                                                                  gid
                                                                                                                                  callback
                                                                                                                                  

                                                                                                                                  其中?tt?为长时间戳,?callback?在前面已经找到并且能生成了,那么只剩下?gid?这个变量。老规矩,按照下面的步骤再去找出?gid?:

                                                                                                                                  1. 在?firebug?中勾选脚本,点击?{}
                                                                                                                                  2. 搜索中勾选?多个文件
                                                                                                                                  3. 在搜索框中搜索?gid

                                                                                                                                  搜索发现?gid?是由?gid: e.guideRandom?这个函数生成的:

                                                                                                                                  那么接着搜搜这个函数?guideRandom?,找到如下?JavaScrip?代码:

                                                                                                                                  this.guideRandom = function () {
                                                                                                                                  return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (e) {
                                                                                                                                  var t = 16 * Math.random() | 0,
                                                                                                                                  n = 'x' == e ? t : 3 & t | 8;
                                                                                                                                  return n.toString(16)
                                                                                                                                  }).toUpperCase()
                                                                                                                                  }(),
                                                                                                                                  

                                                                                                                                  整理一下让其可以在?python?中运行:

                                                                                                                                  function gid(){
                                                                                                                                          return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (e) {
                                                                                                                                          var t = 16 * Math.random() | 0,
                                                                                                                                          n = 'x' == e ? t : 3 & t | 8;
                                                                                                                                          return n.toString(16)
                                                                                                                                          }).toUpperCase()
                                                                                                                                      }
                                                                                                                                  

                                                                                                                                  这样,根据?gid?和?callback?就能得到?token?了!

                                                                                                                                  下面继续寻找?ppui_logintime?,按照规矩来:

                                                                                                                                  1. 在?firebug?中勾选脚本,点击?{}
                                                                                                                                  2. 搜索中勾选?多个文件
                                                                                                                                  3. 在搜索框中搜索?ppui_logintime?,找到了?timeSpan: 'ppui_logintime'

                                                                                                                                  接着搜索?timeSpan?,得到如下信息:

                                                                                                                                  s.timeSpan = (new Date).getTime() - e.initTime
                                                                                                                                  

                                                                                                                                  现在还是看不出什么东西,那么就继续搜索?initTime?,得到如下代码:

                                                                                                                                  _initApi: function (e) {
                                                                                                                                  var t = this;
                                                                                                                                  t.initialized = !0,
                                                                                                                                  t.initTime = (new Date).getTime(),
                                                                                                                                  passport.data.getApiInfo({
                                                                                                                                  apiType: 'login',
                                                                                                                                  gid: t.guideRandom || '',
                                                                                                                                  loginType: t.config && t.config.diaPassLogin ? 'dialogLogin' : 'basicLogin'
                                                                                                                                  }).success(function (n) {
                                                                                                                                  var i = t.fireEvent('getApiInfo', {
                                                                                                                                  rsp: n
                                                                                                                                  });
                                                                                                                                  

                                                                                                                                  继续查找?initApi?,找到位置:

                                                                                                                                  原来是在登陆的时候发生点击,内容改变,按键按下等事件的时候,会调用?_initApi,再看源码和?ppui_logintime?的数值可以发现,?ppui_logintime?代表的是从输入信息开始到点击登陆后结束的时间差!那么在后面?post?的时候直接可以自己构造这个数据了。

                                                                                                                                  那么最后还剩下一个变量?rsakey?,在查找网页的时候发现第一次出现?rsakey?的地方是:

                                                                                                                                  https://passport.baidu.com/v2/getpublickey?token=fcd1f6684072372c6812a44c1d94bf51&tpl=mn&apiver=v3&tt=1461222170065&gid=C539A37-9B0C-4538-9920-E150AC6AE0D5&callback=bd__cbs__tpdrlq
                                                                                                                                  

                                                                                                                                  也就是这里面的?key?,虽然名字不一样,但是值是一样的:

                                                                                                                                  bd__cbs__tpdrlq({"errno": '0', "msg": '',
                                                                                                                                                   "pubkey": '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDiA2HxDW2iVnunx7faCBG3YGBy\nvvF+ysFAIXIVjFTseU7x\/f+Gpr1VTWe2Kxc2dlzBkn5NuRHVxbyXCawu0QlMUfb8\nI2ukM1cIlL0e+B1nBnIp03oXjFvQNhIu58SI6vCoihWX6Qwhb6ZOvJdA249zCNBU\nlTyd7RVwgwaAthI6gQIDAQAB\n-----END PUBLIC KEY-----\n',
                                                                                                                                                   "key": 'wS27H0665CWXK64i2VP02AYtjQUTujkb'})
                                                                                                                                  

                                                                                                                                  分析一下这个网址,出现的变量有:

                                                                                                                                  token
                                                                                                                                  tpl
                                                                                                                                  apiver
                                                                                                                                  tt
                                                                                                                                  gid
                                                                                                                                  callback
                                                                                                                                  

                                                                                                                                  这些变量在前面都能找到,所以也就是说,根据?gid?、?callback?、?token?就能得到?rsakey?了!

                                                                                                                                  下面开始讲解怎么用?python?来实现模拟百度登陆!首先需要构造一个持续登陆的链接,持续登陆的链接能够一直自我保存打开网页或者登陆后留下的?cookie?,当然这是存放在内存中的,例如:

                                                                                                                                  headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0"}
                                                                                                                                  
                                                                                                                                  session = requests.session()
                                                                                                                                  session.get("https://passport.baidu.com/v2/?login", headers=headers)
                                                                                                                                  

                                                                                                                                  session?就是一个持续的链接,一般用?python?访问网页是这样子调用的?requests.get?,这样子的访问不会保存历史访问留下的?cookie?,而用?session.get?能持续保存?cookie?,只要后面访问都用?session.get?或者?session.post?即可。

                                                                                                                                  获取callback

                                                                                                                                  callback?是由?JavaScrip?生成的:

                                                                                                                                  function callback(){
                                                                                                                                              return 'bd__cbs__'+Math.floor(2147483648 * Math.random()).toString(36)
                                                                                                                                          }
                                                                                                                                  

                                                                                                                                  所以直接在?python?中运行这个?JavaScrip?就行了。?python?运行?JavaScrip?需要安装库?pyexecjs,在命令指示符下直接输入?pip3 install pyexecjs?即可。调用方式为:

                                                                                                                                  import execjs
                                                                                                                                  js = '''function callback(){
                                                                                                                                              return 'bd__cbs__'+Math.floor(2147483648 * Math.random()).toString(36)
                                                                                                                                          }
                                                                                                                                  '''
                                                                                                                                  ctx = execjs.compile(js)
                                                                                                                                  callback = ctx.call("callback")
                                                                                                                                  

                                                                                                                                  获取gid

                                                                                                                                  gid?同样是可以用?JavaScrip?生成的,直接调用即可:

                                                                                                                                  import execjs
                                                                                                                                  js = '''function gid(){
                                                                                                                                              return 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (e) {
                                                                                                                                              var t = 16 * Math.random() | 0,
                                                                                                                                              n = 'x' == e ? t : 3 & t | 8;
                                                                                                                                              return n.toString(16)
                                                                                                                                              }).toUpperCase()
                                                                                                                                          }'''
                                                                                                                                  ctx = execjs.compile(js)
                                                                                                                                  gid = ctx.call("gid")
                                                                                                                                  

                                                                                                                                  获取时间tt

                                                                                                                                  时间?tt?是一个毫秒级别的长时间,而?python?生成的时间戳是短时间,所以要在短时间戳后面加上毫秒的长度即可,这里处理的方法是:在短时间戳的后面加上?3?位数的随机数,从而构造出长时间戳。

                                                                                                                                  import time
                                                                                                                                  import random
                                                                                                                                  
                                                                                                                                  timerandom = random.randint(100, 999)
                                                                                                                                  nowtime = int(time.time())
                                                                                                                                  tt = str(nowtime) + str(timerandom)
                                                                                                                                  

                                                                                                                                  获取token

                                                                                                                                  有了?callback?、?gid?、?tt?后,可以获取到?token?,构造请求参数:

                                                                                                                                  tokendata = {
                                                                                                                                          "tpl": "pp",
                                                                                                                                          "subpro": "",
                                                                                                                                          "apiver": "v3",
                                                                                                                                          "tt": tt,
                                                                                                                                          "class": "login",
                                                                                                                                          "gid": gid,
                                                                                                                                          "logintype": "basicLogin",
                                                                                                                                          "callback": callback
                                                                                                                                      }
                                                                                                                                  

                                                                                                                                  更新头部,携带头部访问:

                                                                                                                                  headers.update(dict(Referer="http://passport.baidu.com/", Accept="*/*", Connection="keep-alive", Host="passport.baidu.com"))
                                                                                                                                  resp = session.get(url="https://passport.baidu.com/v2/api/?getapi", params=tokendata, headers=headers)
                                                                                                                                  

                                                                                                                                  顺利抓到包含?token?的返回值,将其放到字典里面即可:

                                                                                                                                  data = json.loads(re.search(r".*?\((.*)\)", resp.text).group(1).replace(""", """))
                                                                                                                                  token = data.get('data').get('token')
                                                                                                                                  

                                                                                                                                  顺利返回?token?!

                                                                                                                                  获取rsakey

                                                                                                                                  获取?rsakey?只需要构造如下请求参数即可:

                                                                                                                                  tt = get_tt()
                                                                                                                                  get_data = {
                                                                                                                                  	"token": token,
                                                                                                                                  	"tpl": "pp",
                                                                                                                                  	"subpro": "",
                                                                                                                                  	"apiver": "v3",
                                                                                                                                  	"tt": tt,
                                                                                                                                  	"gid": gid,
                                                                                                                                  	"callback": callback,
                                                                                                                                  }
                                                                                                                                  

                                                                                                                                  在得到的返回值中提取出?rsakey?和?pubkey?既可:

                                                                                                                                  tt = get_tt()
                                                                                                                                  rsakeydata = {
                                                                                                                                  	"token": token,
                                                                                                                                  	"tpl": "pp",
                                                                                                                                  	"subpro": "",
                                                                                                                                  	"apiver": "v3",
                                                                                                                                  	"tt": tt,
                                                                                                                                  	"gid": gid,
                                                                                                                                  	"callback": callback,
                                                                                                                                  }
                                                                                                                                  resp = session.get(url="https://passport.baidu.com/v2/getpublickey", headers=headers, params=rsakeydata)
                                                                                                                                  data = json.loads(re.search(r".*?\((.*)\)", resp.text).group(1).replace("'", '"'))
                                                                                                                                  
                                                                                                                                  dicts = {}
                                                                                                                                  dicts["rsakey"] = data.get("key")
                                                                                                                                  dicts["pubkey"] = data.get("pubkey")
                                                                                                                                  

                                                                                                                                  加密密码

                                                                                                                                  百度登陆的密码加密方式是很简单的?RSA?加密,这个只是用?JavaScrip?就能实现,翻译成?python?的代码为:

                                                                                                                                  def base64_password(password, pubkey):
                                                                                                                                      pub = rsa.PublicKey.load_pkcs1_openssl_pem(pubkey.encode("utf-8"))
                                                                                                                                      encript_passwd = rsa.encrypt(password.encode("utf-8"), pub)
                                                                                                                                      return base64.b64encode(encript_passwd).decode("utf-8")
                                                                                                                                  

                                                                                                                                  需要安装库?rsa?,只需要在命令指示符下输入:

                                                                                                                                  pip3 install rsa
                                                                                                                                  

                                                                                                                                  登陆

                                                                                                                                  构造一个登陆的?postdata?:

                                                                                                                                  post_data = {
                                                                                                                                          "staticpage": "https://passport.baidu.com/static/passpc-account/html/v3Jump.html",
                                                                                                                                          "charset": "utf-8",
                                                                                                                                          "token": token,
                                                                                                                                          "tpl": "pp",
                                                                                                                                          "subpro": "",
                                                                                                                                          "apiver": "v3",
                                                                                                                                          "tt": tt,
                                                                                                                                          "codestring": "",
                                                                                                                                          "safeflg": 0,
                                                                                                                                          "u": "http://passport.baidu.com/disk/home",
                                                                                                                                          "isPhone": "",
                                                                                                                                          "detect": 1,
                                                                                                                                          "gid": gid,
                                                                                                                                          "quick_user": 0,
                                                                                                                                          "logintype": "basicLogin",
                                                                                                                                          "logLoginType": "pc_loginBasic",
                                                                                                                                          "idc": "",
                                                                                                                                          "loginmerge": "true",
                                                                                                                                          "foreignusername": "",
                                                                                                                                          "username": username,
                                                                                                                                          "password": newpassword,
                                                                                                                                          "mem_pass": "on",
                                                                                                                                          # 返回的key
                                                                                                                                          "rsakey": rsakey,
                                                                                                                                          "crypttype": 12,
                                                                                                                                          "ppui_logintime": 33554,
                                                                                                                                          "countrycode": "",
                                                                                                                                          "dv": dv,
                                                                                                                                          "callback": "parent." + callback
                                                                                                                                      }
                                                                                                                                  

                                                                                                                                  直接登陆:

                                                                                                                                  resp = session.post(url="https://passport.baidu.com/v2/api/?login", data=post_data, headers=headers)
                                                                                                                                  

                                                                                                                                  如果检测到自己在账号包含在返回的?html?里面,则说明登陆成功:

                                                                                                                                  if username in resp.content.decode("utf-8", "ignore"):
                                                                                                                                          print("登录成功")
                                                                                                                                      else:
                                                                                                                                          print("登录失败")
                                                                                                                                  

                                                                                                                                  登陆成功后有两种方式在登陆状态下访问网页:

                                                                                                                                  1. 持续使用?session
                                                                                                                                  2. 获取登录后的?cookie

                                                                                                                                  第一种方法在本次程序跑完后就会自动将后台保存下来的?cookie?丢弃掉,如果下次需要访问则需要重新登陆;第二种方法只要在头部增加这个?cookie?值,就能一直使用?cookie?保证是登陆状态,获取登录后的?cookie?的方法为:

                                                                                                                                  cookies = requests.utils.dict_from_cookiejar(session.cookies)
                                                                                                                                  print(cookies)
                                                                                                                                  

                                                                                                                                  等到的?cookies?为一个字典,将这个字典保存在本地的?json?中:

                                                                                                                                  import json
                                                                                                                                  file = open("cookie.json","w")
                                                                                                                                  file.write(json.dumps(cookies))
                                                                                                                                  file.close()
                                                                                                                                  

                                                                                                                                  下次访问携带这个?cookies?即可:

                                                                                                                                  json_file = open("cookie.json")
                                                                                                                                  cookies = json.load(json_file)
                                                                                                                                  json_file.close()
                                                                                                                                  
                                                                                                                                  header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0',
                                                                                                                                                'Host': 'passport.baidu.com',
                                                                                                                                                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                                                                                                                                                'Accept-Encoding': 'gzip, deflate',
                                                                                                                                                'Accept-Language': 'zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3',
                                                                                                                                                'Connection': 'keep-alive'}
                                                                                                                                  
                                                                                                                                  home_page = requests.get("https://passport.baidu.com/center", headers=headers,cookies=cookies).content.decode("utf-8", "ignore")
                                                                                                                                  print(home_page)
                                                                                                                                  

                                                                                                                                  这里需要提醒的是?cookie?会过期,一般是?7?天,如果发现使用?cookie?登陆失败,那么就需要重新使用账号密码登陆获取?cookie

                                                                                                                                  展开阅读全文

                                                                                                                                  没有更多推荐了,返回首页