找到存储歌曲地址的url界面
首先我们要进入网易云的web页面在页面中我们随意选择一首歌曲,打开开发者工具查看响应的界面。
在这些页面中我们需要查找存储有音乐文件的url,这是我们可以打开全局搜索直接搜索与音频文件后缀相关的文件。(当然这这里可能会白忙活)
因为我们打开媒体文件,看见的后缀是.m4a,这也是类属于音频文件的一种只是划分的更细一点。(媒体文件一般包括音频和视频)
我们知道了返回的音频的格式,这时我们就知道我们需要找什么样的返回格式了,直接ctrl+f搜索.m4a,我们可以看见这样一个界面
如果想更直观的观看就点击预览:
这时我们就知道在这个页面中返回了一些什么参数,code:200成功请求,歌曲的id号也可以说是歌曲的省份证,类型url地址,还有一些不知道干什么的参数,我们都可以通过键名猜测它是干嘛的,(如果还不放心也可以直接复制url地址打开看一看)
确定了返回的界面我们的第一步就已经完成了。
对比寻找我们需要的传递解密的参数
第二步我们就需要尝试多请求几首不同的歌曲发现里面有什么不同的参数。
可以看到post传递的参数有两个params和ensSeckey,哪里面的内容是什么呢,我们如何才能够传递像浏览器一样的参数,那是我们下一个需要解决的问题。
如果我们把自己想象为网页的负责人,一定不想让爬虫顺利的爬取我们网页的内容把,这时我们就会设置措施,这就是反爬,我们又想要数据这就是对抗。这里不得不提醒下爬虫需要注意的三个准则。
1.经常的看爬取网站的君子协议(Robots.txt协议,里面也可以看见网页允许什么样的请求头,那样我们的爬虫体验将是一种新待遇)
2.在不影响服务器端正常运行的前提下进行爬虫
3.不要使用爬虫进行不当得利,这样会违背不当竞争,如果东窗事发一告一个准。
说了这么多的题外话,就是希望我们在每一次爬虫时都要构造请求头,这里需要注意到是一个浏览器自带的代理地址,还有一个是cookie,cookie是一个有时效性的身份认证,里面有时也会带有必要的参数,需要我们去破解。
而在这里我们可以使用插件工具进行在线测试,在页面中我没有对请求头传入任何数据,值传递了post参数也能够完成请求,所以我们在这里可以不用构造请求头,但是作为一个有专业素养的爬虫高手,习惯的写上去了。
插件的名字叫Talend API Tester - Free Edition
如有需要自行下载:密码:1kbc(需要解压点击crx文件压缩,edg开发者模式自行配置路径)
https://pan.baidu.com/s/1uFadnzIq10WDbiYNRqpRrg?pwd=1kbc
到了这里我们我们知道只需要向网页传递post的数据返回值里面就会有音乐的源文件地址,第二步我们也完成了。
分析post表单数据
输出模块
我们如何知道这数据是怎么来的呢?
第一种关键字查找,我们可以使用CTRL+shift+f进行全局搜索params:和encSecKey:的一个
搜索直接带我们返回到了源代码的这儿,而且params:和encSecKey:还是成对出现的,只能说很大概率是这里,要确定就需要我们进一步的验证,这就是断点调试看见了返回页面需要经过这里。
这时我们就需要对这里的js代码进行分析了,可以看出有一个函数传入四个参数然后赋值于bVe7X,之后我们传递的参数带入到bVe7X,返回我们的post参数。这里我们就需要去研究这个window.asrsea函数的内部构造。
点击跳转我们到了这样一个函数体中,我们把断点打在这里不断的调试,
这里一共有五个函数,a函数是生成16给字符的随机字符串 ,b函数传入两个值进行加密模块,这里是AES对称加密,下面的偏移和模块CBC都是常见的加密方式。我们可以经过不断的尝试把这三个函数一python代码表示出来,我们知道在界面的post数据是如何输出的,接下来我们就需要知道我们需要输入什么数据了。
输入数据
在不断的尝试中我知道了window.asrsea函数中传入的4个参数有三个是固定值,那还有一个是确定歌曲的唯一标识。
那还有一个是确定歌曲的唯一标识。
可以看出传递的参数可以设定歌曲的音质,ids号。。。
到这里我们的分析就结束了下一节是我的代码阶段,明天在续写。
第一次写python爬虫实战,写的坎坎坷坷的,有种心有余而力不足的感觉,见谅!!!
先把代码发出来js代码
let window = {};
var CryptoJS = CryptoJS || function(u, p) {
var d = {}
, l = d.lib = {}
, s = function() {}
, t = l.Base = {
extend: function(a) {
s.prototype = this;
var c = new s;
a && c.mixIn(a);
c.hasOwnProperty("init") || (c.init = function() {
c.$super.init.apply(this, arguments)
}
);
c.init.prototype = c;
c.$super = this;
return c
},
create: function() {
var a = this.extend();
a.init.apply(a, arguments);
return a
},
init: function() {},
mixIn: function(a) {
for (var c in a)
a.hasOwnProperty(c) && (this[c] = a[c]);
a.hasOwnProperty("toString") && (this.toString = a.toString)
},
clone: function() {
return this.init.prototype.extend(this)
}
}
, r = l.WordArray = t.extend({
init: function(a, c) {
a = this.words = a || [];
this.sigBytes = c != p ? c : 4 * a.length
},
toString: function(a) {
return (a || v).stringify(this)
},
concat: function(a) {
var c = this.words
, e = a.words
, j = this.sigBytes;
a = a.sigBytes;
this.clamp();
if (j % 4)
for (var k = 0; k < a; k++)
c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4);
else if (65535 < e.length)
for (k = 0; k < a; k += 4)
c[j + k >>> 2] = e[k >>> 2];
else
c.push.apply(c, e);
this.sigBytes += a;
return this
},
clamp: function() {
var a = this.words
, c = this.sigBytes;
a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);
a.length = u.ceil(c / 4)
},
clone: function() {
var a = t.clone.call(this);
a.words = this.words.slice(0);
return a
},
random: function(a) {
for (var c = [], e = 0; e < a; e += 4)
c.push(4294967296 * u.random() | 0);
return new r.init(c,a)
}
})
, w = d.enc = {}
, v = w.Hex = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++) {
var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255;
e.push((k >>> 4).toString(16));
e.push((k & 15).toString(16))
}
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j += 2)
e[j >>> 3] |= parseInt(a.substr(j, 2), 16) << 24 - 4 * (j % 8);
return new r.init(e,c / 2)
}
}
, b = w.Latin1 = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++)
e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255));
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j++)
e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4);
return new r.init(e,c)
}
}
, x = w.Utf8 = {
stringify: function(a) {
try {
return decodeURIComponent(escape(b.stringify(a)))
} catch (c) {
throw Error("Malformed UTF-8 data")
}
},
parse: function(a) {
return b.parse(unescape(encodeURIComponent(a)))
}
}
, q = l.BufferedBlockAlgorithm = t.extend({
reset: function() {
this.i4m = new r.init;
this.yL2x = 0
},
AE9v: function(a) {
"string" == typeof a && (a = x.parse(a));
this.i4m.concat(a);
this.yL2x += a.sigBytes
},
mD8v: function(a) {
var c = this.i4m
, e = c.words
, j = c.sigBytes
, k = this.blockSize
, b = j / (4 * k)
, b = a ? u.ceil(b) : u.max((b | 0) - this.Vr8j, 0);
a = b * k;
j = u.min(4 * a, j);
if (a) {
for (var q = 0; q < a; q += k)
this.tt1x(e, q);
q = e.splice(0, a);
c.sigBytes -= j
}
return new r.init(q,j)
},
clone: function() {
var a = t.clone.call(this);
a.i4m = this.i4m.clone();
return a
},
Vr8j: 0
});
l.Hasher = q.extend({
cfg: t.extend(),
init: function(a) {
this.cfg = this.cfg.extend(a);
this.reset()
},
reset: function() {
q.reset.call(this);
this.mj8b()
},
update: function(a) {
this.AE9v(a);
this.mD8v();
return this
},
finalize: function(a) {
a && this.AE9v(a);
return this.nZ9Q()
},
blockSize: 16,
mE8w: function(a) {
return function(b, e) {
return (new a.init(e)).finalize(b)
}
},
yK2x: function(a) {
return function(b, e) {
return (new n.HMAC.init(a,e)).finalize(b)
}
}
});
var n = d.algo = {};
return d
}(Math);
(function() {
var u = CryptoJS
, p = u.lib.WordArray;
u.enc.Base64 = {
stringify: function(d) {
var l = d.words
, p = d.sigBytes
, t = this.bD5I;
d.clamp();
d = [];
for (var r = 0; r < p; r += 3)
for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + .75 * v < p; v++)