登錄後才能獲取數據也是現在反爬的一個重點,所以計劃接下來的幾期內容都是關於登錄各大站點。由於js代碼過長,所以只截取了部分,完整代碼移步從今天開始種樹,直接拷貝運行即可。
囉嗦兩句
各大網站登錄提交數據的方式不太 一樣,有些只需要POST
明文的賬號密碼、有些網站POST
的密碼是經過加密的(RSA、BASE64、AES、SHA1
等等),還有些網站增加了一些其它的參數,而這些網站的統一特點就是均可以從JS文件
中提取加密代碼復現出來,獲得這些代碼則完全依靠數據提取師父們個人的調試能力了,下面就嘗試去登錄兩個不同的網站-豆瓣與Steam
。
明文提交
明文提交最典型的就是豆瓣,輸入錯誤密碼按F12
然後點擊登錄發現,提交的url爲https://accounts.douban.com/j/mobile/login/basic
,提交的數據只有username和password
,還有其它兩個空的參數:
這種登錄方式屬於Baby級的,甚至於都不需要寫一個登錄類或者函數,不過有點需要注意的是你需要帶着cookie去post賬號密碼,如果你代碼中直接POST
到這個url是不會成功的,除非你在請求頭中添加了cookies
,還有一種辦法就是先用requests.Session
, session方式請求豆瓣的網址,下一次請求就會自動帶上cookie,代碼如下:
import requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36",
}
url = "https://www.douban.com/"
session = requests.Session()
session.get(url,headers=headers, verify=False)
login_url = "https://accounts.douban.com/j/mobile/login/basic"
login_data = {
"ck": "",
"name": "nide",
"password": "nide",
"remember": "false",
"ticket": "",
}
resp = session.post(login_url, data=login_data, headers=headers, verify=False)
print(resp.text)
登錄成功:
加密登錄
前言
Steam作爲全球最大的一個男性購物平臺,每天都有可能撿漏到一些折扣較低的遊戲。假設你有代碼在監控平臺上的遊戲折扣,在購買時當然需要先登錄,要實現自動化的過程,首先就得讓你的賬號自動登錄,接下來就看看如何實現Steam登錄(如需監控遊戲商城需自行發揮,本文不涉及相關內容)。
分析
關於如何打開Steam登錄網頁之類的就不廢話了,直入正題,Steam登錄url爲https://store.steampowered.com/login/?redir=&redir_ssl=1
,請求這個url就會到賬號密碼輸入環節,老方法按F12
,先看看輸入錯誤賬號密碼時候返回的內容,比如我輸入的是賬號爲test
,密碼爲123456
:
點擊登錄,查看網絡請求發現有兩個,getrsakey
和dologin
:
首先看一下getrsakey/
請求,通過上圖可以看到請求url爲https://store.steampowered.com/login/getrsakey/
,方法爲POST
,提交的參數如下:
donotcache
看着像是時間戳,username
就不用說了,點擊Preview
看看返回了什麼內容:
返回了一些參數,但是現在還不知道這些有何用處,接着查看dologin/
:
提交的路徑爲https://store.steampowered.com/login/dologin/
,POST
的參數爲:
基本可以斷定donotcache
是時間戳了,而password
則經過了各種加密,關於rsatimestamp
如果觀察仔細的話可以看到其值與getrsakey/
返回的timestamp
的值是一樣的,其它幾個參數基本可以忽略了:
Form Data參數 | 是否變化 |
---|---|
donotcache | 約等於固定 |
password | 經過加密 |
username | 約等於固定 |
twofactorcode | 可爲空 |
emailauth | 可爲空 |
loginfriendlyname | 可爲空 |
captchagid | 驗證碼標誌,約等於固定 |
captcha_text | 無驗證碼情況可爲空,約等於固定 |
emailsteamid | 固定(可爲空) |
rsatimestamp | 變化,getrsakey/ 返回中獲取 |
remember_login | 固定 |
經過分析,現在需要獲取的值就只有經過加密的password
,那就去Sources
裏搜索一下看看有沒有包含passwor
的js文件,查找結果如下:
有幾個js文件都包含了password
變量,但是結果觀察發現login.js
文件中包含了很多password
,甚至都能看到RSA.encrypt
的字樣,那基本就確定了我們想要的加密過程就在這個文件裏,那就打開這個文件看看吧:
上面紅框裏的內容已經很明瞭了,加密的函數在RSA.encrypt
裏,這個函數有兩個參數:password
和pubkey
,pubkey
是通過RSA.getPublickey
函數得到的,這個函數也有兩個參數:result.publickey_mod
和result.publickey_exp
,看着似曾相識,其實倒回去到https://store.steampowered.com/login/getrsakey/
鏈接返回的地方可以看到有兩個同名的參數返回,這裏再貼一次,但是現在還不能確定是不是,還需要再調試:
調試
在387
處打一個斷點開始調試:
這裏可以看到result.publickey_exp
爲010001
與前面返回的publickey_exp
一樣,result.publickey_mod
也一樣,下面是getPublickey
函數,這是已經跳轉到另外一個名爲rsa.js
的JS文件裏,:
RSA.encrypt
同樣也在這個文件裏,可以看出encrypt
裏還包含Base64
加密:
Bases64
:
繼續運行,直到加密完返回到login.js
中:
到這裏其實整個流程都理清楚了,主要就是獲取pubkey
,然後再通過pubkey
去加密password
,這裏我把rsa.js
文件複製了下來,再加上login.js
中對password
的一些處理操作,重新整合了一個js文件。有能力的童靴可以用python去復現一下,水平有限,我就直接用的execjs
庫去執行了,execjs
庫的安裝參考我上一篇。
部分JS代碼
篇幅有限,只展示部分js代碼了,完整代碼移步從今天開始種樹:
navigator = {};
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
....
省略
.....
var RSA = {
getPublicKey: function( $modulus_hex, $exponent_hex ) {
return new RSAPublicKey( $modulus_hex, $exponent_hex );
},
encrypt: function($data, $pubkey) {
if (!$pubkey) return false;
$data = this.pkcs1pad2($data,($pubkey.modulus.bitLength()+7)>>3);
if(!$data) return false;
$data = $data.modPowInt($pubkey.encryptionExponent, $pubkey.modulus);
if(!$data) return false;
$data = $data.toString(16);
if(($data.length & 1) == 1)
$data = "0" + $data;
return Base64.encode(Hex.decode($data));
},
pkcs1pad2: function($data, $keysize) {
if($keysize < $data.length + 11)
return null;
var $buffer = [];
var $i = $data.length - 1;
while($i >= 0 && $keysize > 0)
$buffer[--$keysize] = $data.charCodeAt($i--);
$buffer[--$keysize] = 0;
while($keysize > 2)
$buffer[--$keysize] = Math.floor(Math.random()*254) + 1;
$buffer[--$keysize] = 2;
$buffer[--$keysize] = 0;
return new BigInteger($buffer);
}
};
function pwd(password,publickey_mod,publickey_exp) {
password = password.replace(/[^\x00-\x7F]/g, '');
console.log(publickey_mod)
console.log(publickey_exp)
var pubKey = RSA.getPublicKey(publickey_mod,publickey_exp)
var encryptedPassword = RSA.encrypt(password, pubKey);
return encryptedPassword
}
python代碼
Steam
類繼承自自己寫的SpiderBase
類,有興趣完整複製的請移步SpiderBase類
import json
import time
from spider.spider_base import SpiderBase
import requests
requests.packages.urllib3.disable_warnings()
class Steam(SpiderBase):
def __init__(self):
super().__init__()
self.rsa_url = "https://store.steampowered.com/login/getrsakey/"
self.login_url = "https://store.steampowered.com/login/dologin/"
self.js_path = '..//js//steam.js'
self.username = "你的"
self.password = "你的"
self.ua = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36"
}
def login(self):
resp = self.get_rsa()
rsakey = json.loads(resp.text)
print(rsakey)
js = self.exec_js(self.js_path)
enc_password = js.call('pwd', self.password, rsakey["publickey_mod"], rsakey["publickey_exp"])
print(enc_password)
login_data = {
'donotcache': int(time.time() * 1000),
'password': enc_password,
'username': self.username,
'twofactorcode': '',
'emailauth': '',
'loginfriendlyname': '',
'captchagid': '-1',
'captcha_text': '',
'emailsteamid': '',
'rsatimestamp': rsakey['timestamp'],
'remember_login': 'false',
}
resp = self.session.post(self.login_url,data=login_data,headers = self.ua,verify=False)
print(resp.text)
def get_rsa(self):
rsakey_data = {
'donotcache': int(time.time()*1000),
'username': self.username
}
resp = self.session.post(self.rsa_url,data=rsakey_data,headers=self.ua,verify=False)
return resp
if __name__ == '__main__':
st = Steam()
st.login()
運行
success
爲true
即代表登錄成功,接下來你可以進行你的操作了。
結束語
到這裏就結束了,爲了節省篇幅,完整代碼發佈在個人博客網站從今天開始種樹,有興趣的可以直接複製運行。
更多內容請移步從今天開始種樹,關注知識圖譜與大數據公衆號,獲取更多內容,當然不關注也無所謂。