Weibo Crawler in Action
1、要写一个微博爬虫,得分开几个模块来做:
(1)模拟登录
(2)模拟浏览
(3)针对短时间内大量访问而引起怀疑的禁止登陆解决方案
(4)其他
(1)模拟登陆模块
前提:要模拟登录,得首先知道在登录微博的时候,每一次的HTTP请求中都包含了什么信息,于是,可以利用fiddler结合浏览器(chrome除外)来观察每个请求包信息
过程:
(一)在浏览器输入:weibo.com,截获如下操作:
可以看到这个过程中,有一次尤为重要的HTTP请求:GET /sso/prelogin.php
也就是登陆前的预处理,名字也显而易见--pre,这一步操作主要是拿到了几个重要的参数,用作下一步POST表单的参数。
可以在浏览器中访问这一请求的地址:http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.11)&_=1390381358228
查看返回的如下(一共有7个参数):
sinaSSOController.preloginCallBack ({
1 "retcode":0,
2 "servertime":1390393012,
3 "pcid":"xd-7b601057c7fdb6c0cd2308ab66f824cf655d",
4 "nonce":"YNJDUH",
5 "pubkey":"EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443",
6 "rsakv":"1330428213",
7 "exectime":1
})
其中,
1.
2. servertime:服务时间戳,
3.
4. nonce:是一个6位随机码,
5. pubkey:是用于rsa2密码加密的公钥,
6. rsakv:显然也是加密用的。
7.
2,4,5,6 四个值都是需要在下一步用到的。
在旧版本的新浪微博登陆机制当中,使用的是sha的加密方式,没有pubkey和rsakv参数,
因此,在网上看到的2012年的爬虫代码模拟登陆的代码都已经不适合用,但总体逻辑是没多大变化的,只需要稍作修改还是能用的,我就是这么走过来的,碰壁无数=.=
给出参考文章:
原文参考:http://www.th7.cn/web/js/201308/12198.shtml
For Python: http://www.douban.com/note/201767245/
For PHP: http://www.2cto.com/kf/201210/159591.html
(二)在登录页面输入登录信息(用户名和密码后)截获如下操作:
通过查看其中第一个HTTP请求:POST /sso/login.php?client=ssologin.js(v1.4.11)
看到了,有几个重要的参数包括:
1. su:加密后的用户名
2. servertime
3. nonce
4. pwencode:pwd加密方式
5. nonce
6. rsakv
7. sp:加密后的密码
现在问题就是用户名(su)和密码(sp)是怎么加密的了,
很容易地我们找到了pwencode,知道密码使用rsa2加密的
而通过查看微博登录页面 http://login.sina.com.cn/signup/signin.php:
的目光聚焦到js代码上面
我们发现了里面有用于密码加密的js代码位于 http://login.sina.com.cn/js/sso/ssologin.js,
里面的代码是加密过的,找个工具解密一下(http://www.cnlabs.net/tools/Js_Decoder/),
由上文知道,加密的时候使用了 servertime和nonce,那么我们可以找到:
其中发现与su,sp和servertime,nonce有关的 js代码:
var makeRequest = function (username, password, savestate)
{
var request =
{
entry : me.getEntry(), gateway : 1, from : me.from, savestate : savestate, useticket : me.useTicket ? 1 : 0
};
if (me.failRedirect) {
me.loginExtraQuery.frd = 1
}
request = objMerge(request, {
pagerefer : document.referrer || ""
});
request = objMerge(request, me.loginExtraFlag);
request = objMerge(request, me.loginExtraQuery);
request.su = sinaSSOEncoder.base64.encode(urlencode(username));
if (me.service) {
request.service = me.service
}
if ((me.loginType & rsa) && me.servertime && sinaSSOEncoder && sinaSSOEncoder.RSAKey)
{
request.servertime = me.servertime;
request.nonce = me.nonce;
request.pwencode = "rsa2";
request.rsakv = me.rsakv;
var RSAKey = new sinaSSOEncoder.RSAKey();
RSAKey.setPublic(me.rsaPubkey, "10001");
password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password)
}
else
{
if ((me.loginType & wsse) && me.servertime && sinaSSOEncoder && sinaSSOEncoder.hex_sha1)
{
request.servertime = me.servertime;
request.nonce = me.nonce;
request.pwencode = "wsse";
password = sinaSSOEncoder.hex_sha1("" + sinaSSOEncoder.hex_sha1(sinaSSOEncoder.hex_sha1(password)) + me.servertime + me.nonce);
}
}
request.sp = password;
try {
request.sr = window.screen.width + "*" + window.screen.height
}
catch (e) {}
return request;
};
可以看到su使用的是base64加密方式;
通过寻找sinaSSOEncoder.base64.encode函数,可以看到:
this.base64 =
{
encode : function (l)
{
l = "" + l;
if (l == "") {
return ""
}
var j = "";
var s, q, o = "";
var r, p, n, m = "";
var k = 0;
do
{
s = l.charCodeAt(k++);
q = l.charCodeAt(k++);
o = l.charCodeAt(k++);
r = s >> 2;
p = ((s & 3) << 4) | (q >> 4);
n = ((q & 15) << 2) | (o >> 6);
m = o & 63;
if (isNaN(q)) {
n = m = 64
}
else {
if (isNaN(o)) {
m = 64;
}
}
j = j + this._keys.charAt(r) + this._keys.charAt(p) + this._keys.charAt(n) + this._keys.charAt(m);
s = q = o = "";
r = p = n = m = ""
}
while (k < l.length);
return j;
},
decode : function (s, p, l)
{
...
}
_keys : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
_keys_urlsafe : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
};
而sp就稍微复杂一点了,
if中的是新浪当前版本的密码加密方式rsa2的代码,而else中的是就版本sha加密的代码,
我们只需要关心if中的内容, 加密过程很简单:
var RSAKey = new sinaSSOEncoder.RSAKey();
RSAKey.setPublic(me.rsaPubkey, "10001");
password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password)
首先利用sinaSSOEncoder生成一个RSAKey,好,那么sinaSSOEncoder.RSAKey是什么呢?通过查看,发现在sinaSSOEncoder中有代码:
var sinaSSOEncoder = sinaSSOEncoder || {};
(function ()
{
var av;
var ah = 244837814094590;
var Y = ((ah & 16777215) == 15715070);
function aq(z, t, az)
{
if (z != null)
{
if ("number" == typeof z) {
this.fromNumber(z, t, az)
}
else {
if (t == null && "string" != typeof z) {
this.fromString(z, 256)
}
else {
this.fromString(z, t)
}
}
}
}
function h()
{
return new aq(null)
}
function b(aB, t, z, aA, aD, aC)
{
while (--aC >= 0) {
var az = t * this [aB++] + z[aA] + aD;
aD = Math.floor(az / 67108864);
z[aA++] = az & 67108863
}
return aD
}
function ax(aB, aG, aH, aA, aE, t)
{
var aD = aG & 32767, aF = aG >> 15;
while (--t >= 0)
{
var az = this [aB] & 32767;
var aC = this [aB++] >> 15;
var z = aF * az + aC * aD;
az = aD * az + ((z & 32767) << 15) + aH[aA] + (aE & 1073741823);
aE = (az >>> 30) + (z >>> 15) + aF * aC + (aE >>> 30);
aH[aA++] = az & 1073741823
}
return aE
}
function aw(aB, aG, aH, aA, aE, t)
{
var aD = aG & 16383, aF = aG >> 14;
while (--t >= 0)
{
var az = this [aB] & 16383;
var aC = this [aB++] >> 14;
var z = aF * az + aC * aD;
az = aD * az + ((z & 16383) << 14) + aH[aA] + aE;
aE = (az >> 28) + (z >> 14) + aF * aC;
aH[aA++] = az & 268435455
}
return aE
}
if (Y && (navigator.appName == "Microsoft Internet Explorer")) {
aq.prototype.am = ax;
av = 30
}
else
{
if (Y && (navigator.appName != "Netscape")) {
aq.prototype.am = b;
av = 26
}
else {
aq.prototype.am = aw;
av = 28;
}
}
aq.prototype.DB = av;
aq.prototype.DM = ((1 << av) - 1);
aq.prototype.DV = (1 << av);
var Z = 52;
aq.prototype.FV = Math.pow(2, Z);
aq.prototype.F1 = Z - av;
aq.prototype.F2 = 2 * av - Z;
var ad = "0123456789abcdefghijklmnopqrstuvwxyz";
var af = new Array();
var ao, v;
ao = "0".charCodeAt(0);
for (v = 0; v <= 9; ++v) {
af[ao++] = v
}
ao = "a".charCodeAt(0);
for (v = 10; v < 36; ++v) {
af[ao++] = v
}
ao = "A".charCodeAt(0);
for (v = 10; v < 36; ++v) {
af[ao++] = v
}
function ay(t)
{
return ad.charAt(t)
}
function A(z, t)
{
var az = af[z.charCodeAt(t)];
return (az == null) ?- 1 : az
}
function X(z)
{
for (var t = this.t - 1; t >= 0; --t) {
z[t] = this [t]
}
z.t = this.t;
z.s = this.s
}
function n(t)
{
this.t = 1;
this.s = (t < 0) ?- 1 : 0;
if (t > 0) {
this [0] = t
}
else {
if (t <- 1) {
this [0] = t + DV
}
else {
this.t = 0;
}
}
}
function c(t)
{
var z = h();
z.fromInt(t);
return z
}
function w(aD, z)
{
var aA;
if (z == 16) {
aA = 4
}
else
{
if (z == 8) {
aA = 3
}
else
{
if (z == 256) {
aA = 8
}
else
{
if (z == 2) {
aA = 1
}
else {
if (z == 32) {
aA = 5
}
else {
if (z == 4) {
aA = 2
}
else {
this.fromRadix(aD, z);
return
}
}
}
}
}
}
this.t = 0;
this.s = 0;
var aC = aD.length, az = false, aB = 0;
while (--aC >= 0)
{
var t = (aA == 8) ? aD[aC] & 255 : A(aD, aC);
if (t < 0) {
if (aD.charAt(aC) == "-") {
az = true
}
continue
}
az = false;
if (aB == 0) {
this [this.t++] = t
}
else
{
if (aB + aA > this.DB) {
this [this.t - 1] |= (t & ((1 << (this.DB - aB)) - 1)) << aB;
this [this.t++] = (t >> (this.DB - aB))
}
else {
this [this.t - 1] |= t << aB
}
}
aB += aA;
if (aB >= this.DB) {
aB -= this.DB
}
}
if (aA == 8 && (aD[0] & 128) != 0) {
this.s =- 1;
if (aB > 0) {
this [this.t - 1] |= ((1 << (this.DB - aB)) - 1) << aB
}
}
this.clamp();
if (az) {
aq.ZERO.subTo(this, this)
}
}
function O()
{
var t = this.s & this.DM;
while (this.t > 0 && this [this.t - 1] == t) {
--this.t
}
}
function q(z)
{
if (this.s < 0) {
return "-" + this.negate().toString(z)
}
var az;
if (z == 16) {
az = 4
}
else
{
if (z == 8) {
az = 3
}
else
{
if (z == 2) {
az = 1
}
else {
if (z == 32) {
az = 5
}
else {
if (z == 4) {
az = 2
}
else {
return this.toRadix(z);
}
}
}
}
}
var aB = (1 << az) - 1, aE, t = false, aC = "", aA = this.t;
var aD = this.DB - (aA * this.DB) % az;
if (aA--> 0)
{
if (aD < this.DB && (aE = this [aA] >> aD) > 0) {
t = true;
aC = ay(aE)
}
while (aA >= 0)
{
if (aD < az) {
aE = (this [aA] & ((1 << aD) - 1)) << (az - aD);
aE |= this [--aA] >> (aD += this.DB - az)
}
else {
aE = (this [aA] >> (aD -= az)) & aB;
if (aD <= 0) {
aD += this.DB;
--aA
}
}
if (aE > 0) {
t = true
}
if (t) {
aC += ay(aE)
}
}
}
return t ? aC : "0"
}
function R()
{
var t = h();
aq.ZERO.subTo(this, t);
return t
}
function ak()
{
return (this.s < 0) ? this.negate() : this
}
function G(t)
{
var az = this.s - t.s;
if (az != 0) {
return az
}
var z = this.t;
az = z - t.t;
if (az != 0) {
return az
}
while (--z >= 0) {
if ((az = this [z] - t[z]) != 0) {
return az;
}
}
return 0
}
function j(z)
{
var aA = 1, az;
if ((az = z >>> 16) != 0) {
z = az;
aA += 16
}
if ((az = z >> 8) != 0) {
z = az;
aA += 8
}
if ((az = z >> 4) != 0) {
z = az;
aA += 4
}
if ((az = z >> 2) != 0) {
z = az;
aA += 2
}
if ((az = z >> 1) != 0) {
z = az;
aA += 1
}
return aA
}
function u()
{
if (this.t <= 0) {
return 0
}
return this.DB * (this.t - 1) + j(this [this.t - 1]^(this.s & this.DM))
}
function ap(az, z)
{
var t;
for (t = this.t - 1; t >= 0; --t) {
z[t + az] = this [t]
}
for (t = az - 1; t >= 0; --t) {
z[t] = 0
}
z.t = this.t + az;
z.s = this.s
}
function W(az, z)
{
for (var t = az; t < this.t; ++t) {
z[t - az] = this [t]
}
z.t = Math.max(this.t - az, 0);
z.s = this.s
}
function s(aE, aA)
{
var z = aE % this.DB;
var t = this.DB - z;
var aC = (1 << t) - 1;
var aB = Math.floor(aE / this.DB), aD = (this.s << z) & this.DM, az;
for (az = this.t - 1; az >= 0; --az) {
aA[az + aB + 1] = (this [az] >> t) | aD;
aD = (this [az] & aC) << z
}
for (az = aB - 1; az >= 0; --az) {
aA[az] = 0
}
aA[aB] = aD;
aA.t = this.t + aB + 1;
aA.s = this.s;
aA.clamp()
}
function l(aD, aA)
{
aA.s = this.s;
var aB = Math.floor(aD / this.DB);
if (aB >= this.t) {
aA.t = 0;
return
}
var z = aD % this.DB;
var t = this.DB - z;
var aC = (1 << z) - 1;
aA[0] = this [aB] >> z;
for (var az = aB + 1; az < this.t; ++az) {
aA[az - aB - 1] |= (this [az] & aC) << t;
aA[az - aB] = this [az] >> z
}
if (z > 0) {
aA[this.t - aB - 1] |= (this.s & aC) << t
}
aA.t = this.t - aB;
aA.clamp()
}
function aa(z, aA)
{
var az = 0, aB = 0, t = Math.min(z.t, this.t);
while (az < t) {
aB += this [az] - z[az];
aA[az++] = aB & this.DM;
aB >>= this.DB
}
if (z.t < this.t) {
aB -= z.s;
while (az < this.t) {
aB += this [az];
aA[az++] = aB & this.DM;
aB >>= this.DB
}
aB += this.s
}
else {
aB += this.s;
while (az < z.t) {
aB -= z[az];
aA[az++] = aB & this.DM;
aB >>= this.DB
}
aB -= z.s
}
aA.s = (aB < 0) ?- 1 : 0;
if (aB <- 1) {
aA[az++] = this.DV + aB
}
else {
if (aB > 0) {
aA[az++] = aB;
}
}
aA.t = az;
aA.clamp()
}
function D(z, aA)
{
var t = this.abs(), aB = z.abs();
var az = t.t;
aA.t = az + aB.t;
while (--az >= 0) {
aA[az] = 0
}
for (az = 0; az < aB.t; ++az) {
aA[az + t.t] = t.am(0, aB[az], aA, az, 0, t.t)
}
aA.s = 0;
aA.clamp();
if (this.s != z.s) {
aq.ZERO.subTo(aA, aA)
}
}
function Q(az)
{
var t = this.abs();
var z = az.t = 2 * t.t;
while (--z >= 0) {
az[z] = 0
}
for (z = 0; z < t.t - 1; ++z)
{
var aA = t.am(z, t[z], az, 2 * z, 0, 1);
if ((az[z + t.t] += t.am(z + 1, 2 * t[z], az, 2 * z + 1, aA, t.t - z - 1)) >= t.DV) {
az[z + t.t] -= t.DV;
az[z + t.t + 1] = 1;
}
}
if (az.t > 0) {
az[az.t - 1] += t.am(z, t[z], az, 2 * z, 0, 1)
}
az.s = 0;
az.clamp()
}
function E(aH, aE, aD)
{
var aN = aH.abs();
if (aN.t <= 0) {
return
}
var aF = this.abs();
if (aF.t < aN.t) {
if (aE != null) {
aE.fromInt(0)
}
if (aD != null) {
this.copyTo(aD)
}
return
}
if (aD == null) {
aD = h()
}
var aB = h(), z = this.s, aG = aH.s;
var aM = this.DB - j(aN[aN.t - 1]);
if (aM > 0) {
aN.lShiftTo(aM, aB);
aF.lShiftTo(aM, aD)
}
else {
aN.copyTo(aB);
aF.copyTo(aD)
}
var aJ = aB.t;
var az = aB[aJ - 1];
if (az == 0) {
return
}
var aI = az * (1 << this.F1) + ((aJ > 1) ? aB[aJ - 2] >> this.F2 : 0);
var aQ = this.FV / aI, aP = (1 << this.F1) / aI, aO = 1 << this.F2;
var aL = aD.t, aK = aL - aJ, aC = (aE == null) ? h() : aE;
aB.dlShiftTo(aK, aC);
if (aD.compareTo(aC) >= 0) {
aD[aD.t++] = 1;
aD.subTo(aC, aD)
}
aq.ONE.dlShiftTo(aJ, aC);
aC.subTo(aB, aB);
while (aB.t < aJ) {
aB[aB.t++] = 0
}
while (--aK >= 0)
{
var aA = (aD[--aL] == az) ? this.DM : Math.floor(aD[aL] * aQ + (aD[aL - 1] + aO) * aP);
if ((aD[aL] += aB.am(0, aA, aD, aK, 0, aJ)) < aA) {
aB.dlShiftTo(aK, aC);
aD.subTo(aC, aD);
while (aD[aL] <--aA) {
aD.subTo(aC, aD)
}
}
}
if (aE != null) {
aD.drShiftTo(aJ, aE);
if (z != aG) {
aq.ZERO.subTo(aE, aE)
}
}
aD.t = aJ;
aD.clamp();
if (aM > 0) {
aD.rShiftTo(aM, aD)
}
if (z < 0) {
aq.ZERO.subTo(aD, aD)
}
}
function N(t)
{
var z = h();
this.abs().divRemTo(t, null, z);
if (this.s < 0 && z.compareTo(aq.ZERO) > 0) {
t.subTo(z, z)
}
return z
}
function K(t)
{
this.m = t
}
function U(t)
{
if (t.s < 0 || t.compareTo(this.m) >= 0) {
return t.mod(this.m)
}
else {
return t;
}
}
function aj(t)
{
return t
}
function J(t)
{
t.divRemTo(this.m, null, t)
}
function H(t, az, z)
{
t.multiplyTo(az, z);
this.reduce(z)
}
function at(t, z)
{
t.squareTo(z);
this.reduce(z)
}
K.prototype.convert = U;
K.prototype.revert = aj;
K.prototype.reduce = J;
K.prototype.mulTo = H;
K.prototype.sqrTo = at;
function B()
{
if (this.t < 1) {
return 0
}
var t = this [0];
if ((t & 1) == 0) {
return 0
}
var z = t & 3;
z = (z * (2 - (t & 15) * z)) & 15;
z = (z * (2 - (t & 255) * z)) & 255;
z = (z * (2 - (((t & 65535) * z) & 65535))) & 65535;
z = (z * (2 - t * z % this.DV)) % this.DV;
return (z > 0) ? this.DV - z :- z
}
function f(t)
{
this.m = t;
this.mp = t.invDigit();
this.mpl = this.mp & 32767;
this.mph = this.mp >> 15;
this.um = (1 << (t.DB - 15)) - 1;
this.mt2 = 2 * t.t
}
function ai(t)
{
var z = h();
t.abs().dlShiftTo(this.m.t, z);
z.divRemTo(this.m, null, z);
if (t.s < 0 && z.compareTo(aq.ZERO) > 0) {
this.m.subTo(z, z)
}
return z
}
function ar(t)
{
var z = h();
t.copyTo(z);
this.reduce(z);
return z
}
function P(t)
{
while (t.t <= this.mt2) {
t[t.t++] = 0
}
for (var az = 0; az < this.m.t; ++az)
{
var z = t[az] & 32767;
var aA = (z * this.mpl + (((z * this.mph + (t[az] >> 15) * this.mpl) & this.um) << 15)) & t.DM;
z = az + this.m.t;
t[z] += this.m.am(0, aA, t, az, 0, this.m.t);
while (t[z] >= t.DV) {
t[z] -= t.DV;
t[++z]++
}
}
t.clamp();
t.drShiftTo(this.m.t, t);
if (t.compareTo(this.m) >= 0) {
t.subTo(this.m, t)
}
}
function al(t, z)
{
t.squareTo(z);
this.reduce(z)
}
function y(t, az, z)
{
t.multiplyTo(az, z);
this.reduce(z)
}
f.prototype.convert = ai;
f.prototype.revert = ar;
f.prototype.reduce = P;
f.prototype.mulTo = y;
f.prototype.sqrTo = al;
function i()
{
return ((this.t > 0) ? (this [0] & 1) : this.s) == 0
}
function x(aE, aF)
{
if (aE > 4294967295 || aE < 1) {
return aq.ONE
}
var aD = h(), az = h(), aC = aF.convert(this), aB = j(aE) - 1;
aC.copyTo(aD);
while (--aB >= 0) {
aF.sqrTo(aD, az);
if ((aE & (1 << aB)) > 0) {
aF.mulTo(az, aC, aD)
}
else {
var aA = aD;
aD = az;
az = aA;
}
}
return aF.revert(aD)
}
function am(az, t)
{
var aA;
if (az < 256 || t.isEven()) {
aA = new K(t)
}
else {
aA = new f(t)
}
return this.exp(az, aA)
}
aq.prototype.copyTo = X;
aq.prototype.fromInt = n;
aq.prototype.fromString = w;
aq.prototype.clamp = O;
aq.prototype.dlShiftTo = ap;
aq.prototype.drShiftTo = W;
aq.prototype.lShiftTo = s;
aq.prototype.rShiftTo = l;
aq.prototype.subTo = aa;
aq.prototype.multiplyTo = D;
aq.prototype.squareTo = Q;
aq.prototype.divRemTo = E;
aq.prototype.invDigit = B;
aq.prototype.isEven = i;
aq.prototype.exp = x;
aq.prototype.toString = q;
aq.prototype.negate = R;
aq.prototype.abs = ak;
aq.prototype.compareTo = G;
aq.prototype.bitLength = u;
aq.prototype.mod = N;
aq.prototype.modPowInt = am;
aq.ZERO = c(0);
aq.ONE = c(1);
function k()
{
this.i = 0;
this.j = 0;
this.S = new Array()
}
function e(aB)
{
var aA, z, az;
for (aA = 0; aA < 256; ++aA) {
this.S[aA] = aA
}
z = 0;
for (aA = 0; aA < 256; ++aA)
{
z = (z + this.S[aA] + aB[aA % aB.length]) & 255;
az = this.S[aA];
this.S[aA] = this.S[z];
this.S[z] = az
}
this.i = 0;
this.j = 0
}
function a()
{
var z;
this.i = (this.i + 1) & 255;
this.j = (this.j + this.S[this.i]) & 255;
z = this.S[this.i];
this.S[this.i] = this.S[this.j];
this.S[this.j] = z;
return this.S[(z + this.S[this.i]) & 255]
}
k.prototype.init = e;
k.prototype.next = a;
function an()
{
return new k()
}
var M = 256;
var m;
var T;
var ab;
function d(t)
{
T[ab++]^ = t & 255;
T[ab++]^ = (t >> 8) & 255;
T[ab++]^ = (t >> 16) & 255;
T[ab++]^ = (t >> 24) & 255;
if (ab >= M) {
ab -= M
}
}
function S()
{
d(new Date().getTime())
}
if (T == null)
{
T = new Array();
ab = 0;
var I;
if (navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto && typeof (window.crypto.random) === "function")
{
var F = window.crypto.random(32);
for (I = 0; I < F.length; ++I) {
T[ab++] = F.charCodeAt(I) & 255;
}
}
while (ab < M) {
I = Math.floor(65536 * Math.random());
T[ab++] = I >>> 8;
T[ab++] = I & 255
}
ab = 0;
S()
}
function C()
{
if (m == null) {
S();
m = an();
m.init(T);
for (ab = 0; ab < T.length; ++ab) {
T[ab] = 0
}
ab = 0
}
return m.next()
}
function au(z)
{
var t;
for (t = 0; t < z.length; ++t) {
z[t] = C();
}
}
function ac() {}
ac.prototype.nextBytes = au;
function g(z, t)
{
return new aq(z, t)
}
function ag(az, aA)
{
var t = "";
var z = 0;
while (z + aA < az.length) {
t += az.substring(z, z + aA) + "\n";
z += aA
}
return t + az.substring(z, az.length)
}
function r(t)
{
if (t < 16) {
return "0" + t.toString(16)
}
else {
return t.toString(16);
}
}
function ae(aA, aD)
{
if (aD < aA.length + 11) {
alert("Message too long for RSA");
return null
}
var aC = new Array();
var az = aA.length - 1;
while (az >= 0 && aD > 0)
{
var aB = aA.charCodeAt(az--);
if (aB < 128) {
aC[--aD] = aB
}
else
{
if ((aB > 127) && (aB < 2048)) {
aC[--aD] = (aB & 63) | 128;
aC[--aD] = (aB >> 6) | 192
}
else {
aC[--aD] = (aB & 63) | 128;
aC[--aD] = ((aB >> 6) & 63) | 128;
aC[--aD] = (aB >> 12) | 224
}
}
}
aC[--aD] = 0;
var z = new ac();
var t = new Array();
while (aD > 2) {
t[0] = 0;
while (t[0] == 0) {
z.nextBytes(t)
}
aC[--aD] = t[0]
}
aC[--aD] = 2;
aC[--aD] = 0;
return new aq(aC)
}
function L()
{
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null
}
function o(z, t)
{
if (z != null && t != null && z.length > 0 && t.length > 0) {
this.n = g(z, 16);
this.e = parseInt(t, 16)
}
else {
alert("Invalid RSA public key")
}
}
function V(t)
{
return t.modPowInt(this.e, this.n)
}
function p(az)
{
var t = ae(az, (this.n.bitLength() + 7) >> 3);
if (t == null) {
return null
}
var aA = this.doPublic(t);
if (aA == null) {
return null
}
var z = aA.toString(16);
if ((z.length & 1) == 0) {
return z
}
else {
return "0" + z;
}
}
L.prototype.doPublic = V;
L.prototype.setPublic = o;
L.prototype.encrypt = p;
this.RSAKey = L;
}).call(sinaSSOEncoder);
那么,通过这样一步一步往回退,就解决了密码加密的问题了。
只要给出有效的参数,就可以得到su和sp了。
(三)模拟登录请求:
1)首先是发送 GET请求:http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su=&rsakt=mod&client=ssologin.js(v1.4.11)&_=1390463770958 <-- 时间戳
并得到返回:
sinaSSOController.preloginCallBack({"retcode":0,"servertime":1390466275,"pcid":"xd-5cdeb6364e29309bdea78e342a27bb3d2389","nonce":"SBUEXR","pubkey":"EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443","rsakv":"1330428213","exectime":0})
提取其中的servertime、nonce、pubkey和rsakv作为下一步的必要输入。
2)然后发送 POST请求:http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.11)
并填写好下面的这些参数,利用setEntity来设置他们的值
其中的servertime,rsakv,nonce是从上一步得到的,su利用得到的js代码或者用base64加密方式生成,sp利用js代码生成(pubkey,servertime,nonce,password作为参数)
特别注意的是,su和sp有可能会随着微博版本而有所改变,需要注意定期更新或跟踪观察!!
这一步可以得到下述的内容,其中获取location.replace("...")中的内容作为下一步请求的url的:
<html>
<head>
<title>����ͨ��֤</title>
<meta http-equiv="refresh" content="0; url='http://weibo.com/sso/login.php?ssosavestate=1393172391&url=http%3A%2F%2Fweibo.com%2Fajaxlogin.php%3Fframelogin%3D1%26callback%3Dparent.sinaSSOController.feedBackUrlCallBack%26sudaref%3Dweibo.com&ticket=ST-MTc2MDM3OTk2NA==-1390580391-gz-312B05A73C71C256F9C7E79E92F78E72&retcode=0'"/>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
</head>
<body bgcolor="#ffffff" text="#000000" link="#0000cc" vlink="#551a8b" alink="#ff0000">
<script type="text/javascript" language="javascript">
location.replace("http://weibo.com/sso/login.php?ssosavestate=1393172391&url=http%3A%2F%2Fweibo.com%2Fajaxlogin.php%3Fframelogin%3D1%26callback%3Dparent.sinaSSOController.feedBackUrlCallBack%26sudaref%3Dweibo.com&ticket=ST-MTc2MDM3OTk2NA==-1390580391-gz-312B05A73C71C256F9C7E79E92F78E72&retcode=0");
</script>
</body>
</html>
这里特别需要注意的是只有当retcode=0的时候,才说明之前的所有步骤操作正确,否则请检查之前的操作哪一步中出错了!(是请求的参数包括"_"等等没有填写好还是url没有填写好)
3)接着发送 POST请求:(刚刚得到的locationreplace中的url)
获取POST请求执行结果中的Header内容,将其中所有的“Set-Cache”值取出来作为下一步正式登陆时的header中的cache值添加以登陆
4)最后以个人主页作为url,发送GET请求(除了cache,还要设置其他的header,具体哪些可以通过使用一次fiddler捕获登陆过程中的header来填写)
若成功,会返回个人主页的页面信息,否则,会得到邀请注册的页面(就是HI谁谁谁,快加入微博关注我之类的话的内容
(1)模拟浏览模块
(一)解析页面获得微博内容并实现翻页
说明:网页版微博有几个更新或者叫翻页的功能,分别是:
1)
利用了:GET http://weibo.com/aj/mblog/fsearch?_wv=5_k=1392707288692116&_t=0&since_id=3679341378537185&__rnd=1392708157261 HTTP/1.1
其中,如_wv=5(貌似与个人账户有关),_k1392707299692116,_t=0(暂时不明)均不是必须的,
since_id:(与end_id相反)上一次,最后一条刷新出来的微博信息的mid,向下到哪条微博为止
__rnd:现在的系统时间
2)
利用了:GET http://weibo.com/aj/mblog/fsearch?_wv=5&page=1&count=15&max_id=3679341991665814&pre_page=1&end_id=3679341378537185&pagebar=0&_k=139270728869244&_t=0&__rnd=1392707310629 HTTP/1.1
3)
利用了:GET http://weibo.com/aj/mblog/fsearch?_wv=5&page=2&count=15&pre_page=1&_k=139270851073081&_t=0&end_id=3679348915284679&end_msign=-1&__rnd=1392708820178 HTTP/1.1
2)和3)都十分相似,实际上他们几乎都是一样的,其中
(二)模拟请求获得微博评论
说明:每次点击评论按钮就是发送了一个Get请求,如下图所示:
请求如下所示:
http://weibo.com/aj/comment/small?_wv=5&act=list&mid=3677461138997998&uid=1760379964&isMain=true&ouid=3926428816&special_feed=2&location=home&_t=0&__rnd=1392295975327%20HTTP/1.1
通过多次比较,发现其中的意义:
_wv:
mid:微博id
uid:登陆用户的id
ouid:微博所有用户的id
special_feed:
__rnd:系统时间
mid、uid和ouid都可以从登陆页面的个人信息和weibo处得到:
通过发送上面的Get请求就可以得到评论的内容了!(!!!注意,返回的内容是Json格式的数据,需要将html的内容提取出来,并转义成,评论内容在<dd></dd>之中)
(三)模拟获得好友列表访问并获得好友主页内容
Weibo Crawler in Action的更多相关文章
- 动态网页爬取例子(WebCollector+selenium+phantomjs)
目标:动态网页爬取 说明:这里的动态网页指几种可能:1)需要用户交互,如常见的登录操作:2)网页通过JS / AJAX动态生成,如一个html里有<div id="test" ...
- Linux一些常用操作
1.linux swap分区 可采用文件的方式 dd if=/dev/zero of=/var/swap bs=1024 count=2048000 mkswap /var/swap swapon / ...
- 【网络爬虫】【java】微博爬虫(二):如何抓取HTML页面及HttpClient使用
一.写在前面 上篇文章以网易微博爬虫为例,给出了一个很简单的微博爬虫的爬取过程,大概说明了网络爬虫其实也就这么回事,或许初次看到这个例子觉得有些复杂,不过没有关系,上篇文章给的例子只是让大家对爬虫过程 ...
- 动态网页爬取样例(WebCollector+selenium+phantomjs)
目标:动态网页爬取 说明:这里的动态网页指几种可能:1)须要用户交互,如常见的登录操作:2)网页通过JS / AJAX动态生成.如一个html里有<div id="test" ...
- Ubuntu下一个python的BeautifulSoup和rsa安装方法---信息检索project2部分:微博爬行要求python包裹
后果<信息检索>第二project,微博具有抓取数据,再处理.师兄给了代码.让慢慢爬.可是在ubuntu下.少了非常多python软件包.须要安装. 1.首先执行时.说少了python.B ...
- Python从入门到实践 学习笔记(二)元祖686gffs
列表是可以修改的,而不可变的列表被称为元组 . 定义 * 用圆括号来标识.定义元组后,使用索引来访问其元素,就像访问列表元素一样 修改变量 * 不能修改元组的元素,但可以给存储元组的变量赋值 修改元素 ...
- A web crawler design for data mining
Abstract The content of the web has increasingly become a focus for academic research. Computer prog ...
- Weibo SSO认证 和初次请求数据
在进行SSO请求之前 我们要先去新浪微博的开放平台http://open.weibo.com/进行创建应用.以便得到appKey 和AppSecret. 点击创建应用 .进行资料填写 在这里 App ...
- 使用预定义的action值启动系统应用
1.启动浏览器 Intent intent = new Intent(); intent.setAction(Intent.ACTION_WEB_SEARCH); //可以传一个搜索关键字,会直接显示 ...
随机推荐
- javascript callback函数的理解与使用
最近做的一个项目中用到了callback函数,于是就研究了下总结下我对javascript callback的理解 首先从callback的字面翻译“回调” 可以理解这是一个函数被调用的机制 当我们遇 ...
- leetcode:Delete Node in a Linked List
Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...
- Android - View绘图原理总结
Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构 View定义了绘图的 ...
- Android 用户界面---拖放(Drag and Drop)(二)
拖拽事件监听器和回调方法 View对象既可以用实现View.OnDragListener接口的拖放事件监听器,也可以用View对象的onDragEvent(DragEvent)回调方法来接收拖拽事 ...
- Navicat数据存放位置和备份数据库路径设置
navicat的数据库存放位置在什么地方?带着这样的疑问,我们去解决问题,navicat是默认安装,mysql也是默认安装,数据库存在默认用户所在的目录下面. 安装MySQL时,请选择“Custom” ...
- This project needs to migrate WTP metadata
in command-line: path> mvn eclipse:clean path> mvn -Dwtpversion=1.5 eclipse:eclipse path> m ...
- [ionic开源项目教程] - 第10讲 新闻详情页的用户体验优化
目录 [ionic开源项目教程] 第1讲 前言,技术储备,环境搭建,常用命令 [ionic开源项目教程] 第2讲 新建项目,架构页面,配置app.js和controllers.js [ionic开源项 ...
- 51nod1086 背包问题 V2
我都快不会写二进制优化多重背包了...卡了一下常数从rank100+到20+... #include<cstdio> #include<cstring> #include< ...
- HDU 3555 Bomb (数位DP-记忆化搜索模板)
题意 求区间[1,n]内含有相邻49的数. 思路 比较简单的按位DP思路.这是第一次学习记忆化搜索式的数位DP,确实比递推形式的更好理解呐,而且也更通用~可以一般化: [数位DP模板总结] int d ...
- (一) 从零开始搭建Spark Standalone集群环境搭建
本文主要讲解spark 环境的搭建 主机配置 4核8线程,主频3.4G,16G内存 虚拟环境: VMWare 虚拟环境系统:Ubuntu 14.10 虚拟机运行环境: jdk-1.7.0_79(64 ...