洞察金融行业需要转型,如何利用鸿蒙app开发提升运营效率
936
2022-10-02
爬虫之抓js教程
在初学的爬虫过程中,很多人还不知道有些字段是如何生成的,怎样模拟生成这些字段来拼接头部。为了再次纪念【宏彦获水】成语初次面世,特地用【百度登陆】写下一篇登陆百度的教程,以供大家参考。
前面学习了如何在 get 的时候想服务器发送多变的请求数据,从而达到搜索的效果,而实际上 搜索是简单的登陆 !所以本文将要介绍如何向百度服务器发送 post 数据,从而达到模拟登陆百度的效果。
在 firebug 中,找到如下一行 POST?login :
如果百度后台认为此 登陆表单请求 是正确的后,会在 头信息 -> 响应头信息 中返回一个 Set-Cookie 。当我们登陆成功后,关闭浏览器,下次再打开浏览器的时候发现百度还是处于一种登陆的状态,这就是和 Cookie 有关。在百度登陆成功后会返回一个 Cookie 储存到浏览器中,下次再打开百度的时候,浏览器中的 头信息 -> 请求头信息 中会携带一个 Cookie ,这个 Cookie 就是百度服务器判断你以前是否登陆过百度。而这个 Cooike 就是 Set-Cookie 加工而来的!那么重点来了,如果要用代码模拟登陆百度,应该要具备以下几个步骤:
构造请求表单请求成功后获取Cookie (这个Cookie 并非Set-Cookie)在请求头部header 中携带这个Cookie ,就可以以登陆过后的身份访问百度
原理讲清楚了,那么下面开始实践!
构造请求表单
可以发现,请求的表单有
staticpagecharsettokentplsubproapiverttcodestringsafeflguisPhonedetectgidquick_userlogintypelogLoginTypeidcloginmergesploginusernamepasswordverifycodemem_passrsakeycrypttypeppui_logintimecountrycodefp_uidfp_infologinversiondstkdvtraceidcallback
其中,被红框框起来的表单是多次请求变化的:
callbacktttokenppui_logintimersakeyverifycode
而 ds、tk 在第一次请求网页直接生成了:
json,所以得到 ds、tk:
jsonpCallbackb5819({"code":0,"data":{"tk":"1966\/wzQ9fSm481E1Dd6MPpdaM08fX2AB5MkNq1aMZHDBoekhU51\/8+yOdlYGlLXJVKpduaYRnOVNhfERmTiBXB1Vw==","as":"a82478bc","ds":"oLZa10fYIvKavmDHTaWvTF5D9f3NBzweejdgFGUJB9yI6TFVGHZ8EtWhXcLshwfDL0sU7ymlQe3uVByWIXCym03HZTZxZmGaXl8Jw+unuO5D3SN29KiMO0oj1fSH58BU"}})
其中很明显可以看出来的是:
tt 时间戳verifycode 验证码
那么就剩下几个请求表单还暂时无法得知,首先查找 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 第一次在哪里出现。最后发现首次出现在网址:
Json :
bd__cbs__3cagws({"errInfo": {"no": "0"}, "data": {"rememberedUserName": "", "codeString": "", "token": "6245a75e6ba48d39033a8c31dfcb37c7", "cookie": "1", "usernametype": "", "spLogin": "rate", "disable": "", "loginrecord": {'email': [], 'phone': []}}})
第一次 token 出现的地方找到了,那么分析一下请求出 token 的网址,网址中涉及到的变量有:
tplapiverttclassgidlogintypecallback
为了查看哪些变量是变化的,就再次进行多次登陆。最后发现,变化的是:
ttgidcallback
其中 tt 为长时间戳, callback 在前面已经找到并且能生成了,那么只剩下 gid 这个变量。老规矩,按照下面的步骤再去找出 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 ,按照规矩来:
接着搜索 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 ,找到位置:
那么最后还剩下一个变量 rsakey ,在查找网页的时候发现第一次出现 rsakey 的地方是:
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'})
分析一下这个网址,出现的变量有:
tokentplapiverttgidcallback
这些变量在前面都能找到,所以也就是说,根据 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("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 execjsjs = '''function callback(){ return 'bd__cbs__'+Math.floor(2147483648 * Math.random()).toString(36) }'''ctx = execjs.compile(js)callback = ctx.call("callback")
获取traceid
traceid 同样是可以用 JavaScrip 生成的,直接调用即可:
import execjsjs = '''function traceid(){ var e = {a: 1, b: 1, c: 1} e.traceID = { headID: e.traceID && e.traceID.headID || "", flowID: e.traceID && e.traceID.flowID || "", cases: e.traceID && e.traceID.cases || "", initTraceID: function(e) { var t = this; e && e.length > 0 ? (t.headID = e.slice(0, 6), t.flowID = e.slice(6, 8)) : t.destory() }, createTraceID: function() { var e = this; return e.headID + e.flowID + e.cases }, startFlow: function(e) { var t = this , n = t.getFlowID(e); 0 === t.flowID.length || t.flowID === n ? (t.createHeadID(), t.flowID = n) : t.finishFlow(n) }, finishFlow: function() { var e = this; e.destory() }, getRandom: function() { return parseInt(90 * Math.random() + 10, 10) }, createHeadID: function() { var e = this , t = (new Date).getTime() + e.getRandom().toString() , n = Number(t).toString(16) , i = n.length , s = n.slice(i - 6, i).toUpperCase(); e.headID = s }, getTraceID: function(e) { var t = this , n = e && e.traceid || ""; t.initTraceID(n) }, getFlowID: function(e) { var t = { login: "01", reg: "02" }; return t[e] }, setData: function(e) { var t = this; return e.data ? e.data.traceid = t.createTraceID() : e.url = e.url + (e.url.indexOf("?") > -1 ? "&" : "?") + "traceid=" + t.createTraceID(), e }, destory: function() { var e = this; e.headID = "", e.flowID = "" } }; e.traceID.initTraceID() e.traceID.createHeadID() return e.traceID.createTraceID() + "01" }'''ctx = execjs.compile(js)traceid = ctx.call("traceid")
获取gid
gid 同样是可以用 JavaScrip 生成的,直接调用即可:
import execjsjs = '''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 timeimport randomtimerandom = 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="Accept="*/*", Connection="keep-alive", Host="passport.baidu.com"))resp = session.get(url="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="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": " "charset": "utf-8", "token": token, "tpl": "pp", "subpro": "", "apiver": "v3", "tt": tt, "codestring": "", "safeflg": 0, "u": " "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="data=post_data, headers=headers)
如果检测到自己在账号包含在返回的 html 里面,则说明登陆成功:
if username in resp.content.decode("utf-8", "ignore"): print("登录成功") else: print("登录失败")
登陆成功后有两种方式在登陆状态下访问网页:
持续使用session获取登录后的cookie
第一种方法在本次程序跑完后就会自动将后台保存下来的 cookie 丢弃掉,如果下次需要访问则需要重新登陆;第二种方法只要在头部增加这个 cookie 值,就能一直使用 cookie 保证是登陆状态,获取登录后的 cookie 的方法为:
cookies = requests.utils.dict_from_cookiejar(session.cookies)print(cookies)
等到的 cookies 为一个字典,将这个字典保存在本地的 json 中:
import jsonfile = 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("headers=headers,cookies=cookies).content.decode("utf-8", "ignore")print(home_page)
这里需要提醒的是 cookie 会过期,一般是 7 天,如果发现使用 cookie 登陆失败,那么就需要重新使用账号密码登陆获取 cookie。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~