爬蟲學習(5):豆瓣、Steam賬號PC端登錄(登錄篇一)

登錄後才能獲取數據也是現在反爬的一個重點,所以計劃接下來的幾期內容都是關於登錄各大站點。由於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在這裏插入圖片描述
點擊登錄,查看網絡請求發現有兩個,getrsakeydologin
在這裏插入圖片描述
首先看一下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裏,這個函數有兩個參數:passwordpubkey,pubkey是通過RSA.getPublickey函數得到的,這個函數也有兩個參數:result.publickey_modresult.publickey_exp,看着似曾相識,其實倒回去到https://store.steampowered.com/login/getrsakey/鏈接返回的地方可以看到有兩個同名的參數返回,這裏再貼一次,但是現在還不能確定是不是,還需要再調試:
在這裏插入圖片描述

調試

387處打一個斷點開始調試:
在這裏插入圖片描述
這裏可以看到result.publickey_exp010001與前面返回的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()

運行

在這裏插入圖片描述
successtrue即代表登錄成功,接下來你可以進行你的操作了。

結束語

到這裏就結束了,爲了節省篇幅,完整代碼發佈在個人博客網站從今天開始種樹,有興趣的可以直接複製運行。
更多內容請移步從今天開始種樹,關注知識圖譜與大數據公衆號,獲取更多內容,當然不關注也無所謂。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章