爬虫学习(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即代表登录成功,接下来你可以进行你的操作了。

结束语

到这里就结束了,为了节省篇幅,完整代码发布在个人博客网站从今天开始种树,有兴趣的可以直接复制运行。
更多内容请移步从今天开始种树,关注知识图谱与大数据公众号,获取更多内容,当然不关注也无所谓。
在这里插入图片描述


   转载规则


《爬虫学习(5):Steam游戏账号PC端登录(登录篇一)》 罗华 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
python面试48题(附答案):语言特性与数据类型篇 python面试48题(附答案):语言特性与数据类型篇
本文内容均收集自互联网,并对内容不恰当地方进行适当修改。更多请访问从今天开始种树 正文1. 谈谈对 Python 和其他语言的区别答:Python 是一门语法简洁优美,功能强大无比,应用领域非常广泛,具有强大完备的第三方库,它是一门强类
2020-07-16
下一篇 
爬虫学习(4):自动保存百度云盘资源 爬虫学习(4):自动保存百度云盘资源
完整代码移步从今天开始种树 开始在上一文中,我们保存了百度云盘的地址和提取码,但是这种分享链接很容易被屏蔽,最好的做法就是保存资源到自己的网盘,不过采集的链接有上万个,人肉保存并不现实,所以本文尝试了批量保存资源,如您还没看过上文,这里
  目录