音乐数据包分析
-
正常流程抓包分析数据, 找到音频链接
-
找到歌曲对应的数据包位置
-
找到
sign
加密位置
看到这里 MD5 又是 32位的 感觉是是正常的md5加密, 其实他不是的, 可以找一个MD5在线加密的测试一下, 发现好像不是, 那就只能去扣代码了, 这个代码还挺好扣的
JS代码
var commonjsGlobal = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : void 0 !== global ? global : "undefined" != typeof self ? self : {};
function createCommonjsModule(e, t) {
return e(t = {
exports: {}
}, t.exports),
t.exports
}
var md5 \= createCommonjsModule((function (module) {
!function () {
var ERROR \= "input is invalid type"
, WINDOW \= "object" == typeof window
, root \= WINDOW ? window : {};
root.JS\_MD5\_NO\_WINDOW && (WINDOW = !1);
var WEB\_WORKER \= !WINDOW && "object" == typeof self
,
NODE\_JS \= !root.JS\_MD5\_NO\_NODE\_JS && "object" == typeof process && process.versions && process.versions.node;
NODE\_JS ? root \= commonjsGlobal : WEB\_WORKER && (root = self);
var COMMON\_JS \= !root.JS\_MD5\_NO\_COMMON\_JS && module.exports,
ARRAY\_BUFFER \= !root.JS\_MD5\_NO\_ARRAY\_BUFFER && "undefined" != typeof ArrayBuffer,
HEX\_CHARS \= "0123456789abcdef".split(""), EXTRA = \[128, 32768, 8388608, -2147483648\],
SHIFT \= \[0, 8, 16, 24\], OUTPUT\_TYPES = \["hex", "array", "digest", "buffer", "arrayBuffer", "base64"\],
BASE64\_ENCODE\_CHAR \= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),
blocks \= \[\], buffer8, buffer, buffer8, blocks;
ARRAY\_BUFFER && (buffer = new ArrayBuffer(68),
buffer8 \= new Uint8Array(buffer),
blocks \= new Uint32Array(buffer)),
!root.JS\_MD5\_NO\_NODE\_JS && Array.isArray || (Array.isArray = function (e) {
return "\[object Array\]" === Object.prototype.toString.call(e)
}
),
!ARRAY\_BUFFER || !root.JS\_MD5\_NO\_ARRAY\_BUFFER\_IS\_VIEW && ArrayBuffer.isView || (ArrayBuffer.isView = function (e) {
return "object" == typeof e && e.buffer && e.buffer.constructor === ArrayBuffer
}
);
var createOutputMethod \= function (e) {
return function (t) {
return new Md5(!0).update(t)\[e\]()
}
}
, createMethod \= function () {
var e \= createOutputMethod("hex");
NODE\_JS && (e = nodeWrap(e)),
e.create \= function () {
return new Md5
}
,
e.update \= function (t) {
return e.create().update(t)
}
;
for (var t = 0; t < OUTPUT\_TYPES.length; ++t) {
var n \= OUTPUT\_TYPES\[t\];
e\[n\] \= createOutputMethod(n)
}
return e
}
, nodeWrap \= function (method) {
var crypto \= eval("require('crypto')")
, Buffer \= eval("require('buffer').Buffer")
, nodeMethod \= function (e) {
if ("string" == typeof e)
return crypto.createHash("md5").update(e, "utf8").digest("hex");
if (null == e)
throw ERROR;
return e.constructor === ArrayBuffer && (e = new Uint8Array(e)),
Array.isArray(e) || ArrayBuffer.isView(e) || e.constructor === Buffer ? crypto.createHash("md5").update(new Buffer(e)).digest("hex") : method(e)
};
return nodeMethod
};
function Md5(e) {
var t;
e ? (blocks\[0\] \= blocks\[16\] = blocks\[1\] = blocks\[2\] = blocks\[3\] = blocks\[4\] = blocks\[5\] = blocks\[6\] = blocks\[7\] = blocks\[8\] = blocks\[9\] = blocks\[10\] = blocks\[11\] = blocks\[12\] = blocks\[13\] = blocks\[14\] = blocks\[15\] = 0,
this.blocks \= blocks,
this.buffer8 \= buffer8) : ARRAY\_BUFFER ? (t = new ArrayBuffer(68),
this.buffer8 \= new Uint8Array(t),
this.blocks \= new Uint32Array(t)) : this.blocks = \[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\],
this.h0 \= this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0,
this.finalized \= this.hashed = !1,
this.first \= !0
}
Md5.prototype.update \= function (e) {
if (!this.finalized) {
var t, n \= typeof e;
if ("string" != n) {
if ("object" != n)
throw ERROR;
if (null === e)
throw ERROR;
if (ARRAY\_BUFFER && e.constructor === ArrayBuffer)
e \= new Uint8Array(e);
else if (!(Array.isArray(e) || ARRAY\_BUFFER && ArrayBuffer.isView(e)))
throw ERROR;
t \= !0
}
for (var r, i, s = 0, o = e.length, a = this.blocks, u = this.buffer8; s < o;) {
if (this.hashed && (this.hashed = !1,
a\[0\] \= a\[16\],
a\[16\] = a\[1\] = a\[2\] = a\[3\] = a\[4\] = a\[5\] = a\[6\] = a\[7\] = a\[8\] = a\[9\] = a\[10\] = a\[11\] = a\[12\] = a\[13\] = a\[14\] = a\[15\] = 0),
t)
if (ARRAY\_BUFFER)
for (i = this.start; s < o && i < 64; ++s)
u\[i++\] = e\[s\];
else
for (i = this.start; s < o && i < 64; ++s)
a\[i \>> 2\] |= e\[s\] << SHIFT\[3 & i++\];
else if (ARRAY\_BUFFER)
for (i = this.start; s < o && i < 64; ++s)
(r \= e.charCodeAt(s)) < 128 ? u\[i++\] = r : (r < 2048 ? u\[i++\] = 192 | r >> 6 : (r < 55296 || 57344 <= r ? u\[i++\] = 224 | r >> 12 : (r = 65536 + ((1023 & r) << 10 | 1023 & e.charCodeAt(++s)),
u\[i++\] = 240 | r >> 18,
u\[i++\] = 128 | r >> 12 & 63),
u\[i++\] = 128 | r >> 6 & 63),
u\[i++\] = 128 | 63 & r);
else
for (i = this.start; s < o && i < 64; ++s)
(r \= e.charCodeAt(s)) < 128 ? a\[i >> 2\] |= r << SHIFT\[3 & i++\] : (r < 2048 ? a\[i >> 2\] |= (192 | r >> 6) << SHIFT\[3 & i++\] : (r < 55296 || 57344 <= r ? a\[i >> 2\] |= (224 | r >> 12) << SHIFT\[3 & i++\] : (r = 65536 + ((1023 & r) << 10 | 1023 & e.charCodeAt(++s)),
a\[i \>> 2\] |= (240 | r >> 18) << SHIFT\[3 & i++\],
a\[i \>> 2\] |= (128 | r >> 12 & 63) << SHIFT\[3 & i++\]),
a\[i \>> 2\] |= (128 | r >> 6 & 63) << SHIFT\[3 & i++\]),
a\[i \>> 2\] |= (128 | 63 & r) << SHIFT\[3 & i++\]);
this.lastByteIndex \= i,
this.bytes += i - this.start,
64 <= i ? (this.start = i - 64,
this.hash(),
this.hashed \= !0) : this.start = i
}
return 4294967295 < this.bytes && (this.hBytes += this.bytes / 4294967296 << 0,
this.bytes \= this.bytes % 4294967296),
this
}
}
,
Md5.prototype.finalize \= function () {
var e, t;
this.finalized || (this.finalized = !0,
(e \= this.blocks)\[(t = this.lastByteIndex) >> 2\] |= EXTRA\[3 & t\],
56 <= t && (this.hashed || this.hash(),
e\[0\] \= e\[16\],
e\[16\] = e\[1\] = e\[2\] = e\[3\] = e\[4\] = e\[5\] = e\[6\] = e\[7\] = e\[8\] = e\[9\] = e\[10\] = e\[11\] = e\[12\] = e\[13\] = e\[14\] = e\[15\] = 0),
e\[14\] = this.bytes << 3,
e\[15\] = this.hBytes << 3 | this.bytes >>> 29,
this.hash())
}
,
Md5.prototype.hash \= function () {
var e, t, n, r, i, s \= this.blocks,
o \= this.first ? ((o = ((e = ((e = s\[0\] - 680876937) << 7 | e >>> 25) - 271733879 << 0) ^ (t = ((t = (-271733879 ^ (n = ((n = (-1732584194 ^ 2004318071 & e) + s\[1\] - 117830708) << 12 | n >>> 20) + e << 0) & (-271733879 ^ e)) + s\[2\] - 1126478375) << 17 | t >>> 15) + n << 0) & (n ^ e)) + s\[3\] - 1316259209) << 22 | o >>> 10) + t << 0 : (e = this.h0,
o \= this.h1,
t \= this.h2,
((o += ((e = ((e += ((n = this.h3) ^ o & (t ^ n)) + s\[0\] - 680876936) << 7 | e >>> 25) + o << 0) ^ (t = ((t += (o ^ (n = ((n += (t ^ e & (o ^ t)) + s\[1\] - 389564586) << 12 | n >>> 20) + e << 0) & (e ^ o)) + s\[2\] + 606105819) << 17 | t >>> 15) + n << 0) & (n ^ e)) + s\[3\] - 1044525330) << 22 | o >>> 10) + t << 0);
o \= ((o += ((e = ((e += (n ^ o & (t ^ n)) + s\[4\] - 176418897) << 7 | e >>> 25) + o << 0) ^ (t = ((t += (o ^ (n = ((n += (t ^ e & (o ^ t)) + s\[5\] + 1200080426) << 12 | n >>> 20) + e << 0) & (e ^ o)) + s\[6\] - 1473231341) << 17 | t >>> 15) + n << 0) & (n ^ e)) + s\[7\] - 45705983) << 22 | o >>> 10) + t << 0,
o \= ((o += ((e = ((e += (n ^ o & (t ^ n)) + s\[8\] + 1770035416) << 7 | e >>> 25) + o << 0) ^ (t = ((t += (o ^ (n = ((n += (t ^ e & (o ^ t)) + s\[9\] - 1958414417) << 12 | n >>> 20) + e << 0) & (e ^ o)) + s\[10\] - 42063) << 17 | t >>> 15) + n << 0) & (n ^ e)) + s\[11\] - 1990404162) << 22 | o >>> 10) + t << 0,
o \= ((o += ((e = ((e += (n ^ o & (t ^ n)) + s\[12\] + 1804603682) << 7 | e >>> 25) + o << 0) ^ (t = ((t += (o ^ (n = ((n += (t ^ e & (o ^ t)) + s\[13\] - 40341101) << 12 | n >>> 20) + e << 0) & (e ^ o)) + s\[14\] - 1502002290) << 17 | t >>> 15) + n << 0) & (n ^ e)) + s\[15\] + 1236535329) << 22 | o >>> 10) + t << 0,
o \= ((o += ((n = ((n += (o ^ t & ((e = ((e += (t ^ n & (o ^ t)) + s\[1\] - 165796510) << 5 | e >>> 27) + o << 0) ^ o)) + s\[6\] - 1069501632) << 9 | n >>> 23) + e << 0) ^ e & ((t = ((t += (e ^ o & (n ^ e)) + s\[11\] + 643717713) << 14 | t >>> 18) + n << 0) ^ n)) + s\[0\] - 373897302) << 20 | o >>> 12) + t << 0,
o \= ((o += ((n = ((n += (o ^ t & ((e = ((e += (t ^ n & (o ^ t)) + s\[5\] - 701558691) << 5 | e >>> 27) + o << 0) ^ o)) + s\[10\] + 38016083) << 9 | n >>> 23) + e << 0) ^ e & ((t = ((t += (e ^ o & (n ^ e)) + s\[15\] - 660478335) << 14 | t >>> 18) + n << 0) ^ n)) + s\[4\] - 405537848) << 20 | o >>> 12) + t << 0,
o \= ((o += ((n = ((n += (o ^ t & ((e = ((e += (t ^ n & (o ^ t)) + s\[9\] + 568446438) << 5 | e >>> 27) + o << 0) ^ o)) + s\[14\] - 1019803690) << 9 | n >>> 23) + e << 0) ^ e & ((t = ((t += (e ^ o & (n ^ e)) + s\[3\] - 187363961) << 14 | t >>> 18) + n << 0) ^ n)) + s\[8\] + 1163531501) << 20 | o >>> 12) + t << 0,
o \= ((o += ((n = ((n += (o ^ t & ((e = ((e += (t ^ n & (o ^ t)) + s\[13\] - 1444681467) << 5 | e >>> 27) + o << 0) ^ o)) + s\[2\] - 51403784) << 9 | n >>> 23) + e << 0) ^ e & ((t = ((t += (e ^ o & (n ^ e)) + s\[7\] + 1735328473) << 14 | t >>> 18) + n << 0) ^ n)) + s\[12\] - 1926607734) << 20 | o >>> 12) + t << 0,
o \= ((o += ((i = (n = ((n += ((r = o ^ t) ^ (e = ((e += (r ^ n) + s\[5\] - 378558) << 4 | e >>> 28) + o << 0)) + s\[8\] - 2022574463) << 11 | n >>> 21) + e << 0) ^ e) ^ (t = ((t += (i ^ o) + s\[11\] + 1839030562) << 16 | t >>> 16) + n << 0)) + s\[14\] - 35309556) << 23 | o >>> 9) + t << 0,
o \= ((o += ((i = (n = ((n += ((r = o ^ t) ^ (e = ((e += (r ^ n) + s\[1\] - 1530992060) << 4 | e >>> 28) + o << 0)) + s\[4\] + 1272893353) << 11 | n >>> 21) + e << 0) ^ e) ^ (t = ((t += (i ^ o) + s\[7\] - 155497632) << 16 | t >>> 16) + n << 0)) + s\[10\] - 1094730640) << 23 | o >>> 9) + t << 0,
o \= ((o += ((i = (n = ((n += ((r = o ^ t) ^ (e = ((e += (r ^ n) + s\[13\] + 681279174) << 4 | e >>> 28) + o << 0)) + s\[0\] - 358537222) << 11 | n >>> 21) + e << 0) ^ e) ^ (t = ((t += (i ^ o) + s\[3\] - 722521979) << 16 | t >>> 16) + n << 0)) + s\[6\] + 76029189) << 23 | o >>> 9) + t << 0,
o \= ((o += ((i = (n = ((n += ((r = o ^ t) ^ (e = ((e += (r ^ n) + s\[9\] - 640364487) << 4 | e >>> 28) + o << 0)) + s\[12\] - 421815835) << 11 | n >>> 21) + e << 0) ^ e) ^ (t = ((t += (i ^ o) + s\[15\] + 530742520) << 16 | t >>> 16) + n << 0)) + s\[2\] - 995338651) << 23 | o >>> 9) + t << 0,
o \= ((o += ((n = ((n += (o ^ ((e = ((e += (t ^ (o | ~n)) + s\[0\] - 198630844) << 6 | e >>> 26) + o << 0) | ~t)) + s\[7\] + 1126891415) << 10 | n >>> 22) + e << 0) ^ ((t = ((t += (e ^ (n | ~o)) + s\[14\] - 1416354905) << 15 | t >>> 17) + n << 0) | ~e)) + s\[5\] - 57434055) << 21 | o >>> 11) + t << 0,
o \= ((o += ((n = ((n += (o ^ ((e = ((e += (t ^ (o | ~n)) + s\[12\] + 1700485571) << 6 | e >>> 26) + o << 0) | ~t)) + s\[3\] - 1894986606) << 10 | n >>> 22) + e << 0) ^ ((t = ((t += (e ^ (n | ~o)) + s\[10\] - 1051523) << 15 | t >>> 17) + n << 0) | ~e)) + s\[1\] - 2054922799) << 21 | o >>> 11) + t << 0,
o \= ((o += ((n = ((n += (o ^ ((e = ((e += (t ^ (o | ~n)) + s\[8\] + 1873313359) << 6 | e >>> 26) + o << 0) | ~t)) + s\[15\] - 30611744) << 10 | n >>> 22) + e << 0) ^ ((t = ((t += (e ^ (n | ~o)) + s\[6\] - 1560198380) << 15 | t >>> 17) + n << 0) | ~e)) + s\[13\] + 1309151649) << 21 | o >>> 11) + t << 0,
o \= ((o += ((n = ((n += (o ^ ((e = ((e += (t ^ (o | ~n)) + s\[4\] - 145523070) << 6 | e >>> 26) + o << 0) | ~t)) + s\[11\] - 1120210379) << 10 | n >>> 22) + e << 0) ^ ((t = ((t += (e ^ (n | ~o)) + s\[2\] + 718787259) << 15 | t >>> 17) + n << 0) | ~e)) + s\[9\] - 343485551) << 21 | o >>> 11) + t << 0,
this.first ? (this.h0 \= e + 1732584193 << 0,
this.h1 \= o - 271733879 << 0,
this.h2 \= t - 1732584194 << 0,
this.h3 \= n + 271733878 << 0,
this.first \= !1) : (this.h0 = this.h0 + e << 0,
this.h1 \= this.h1 + o << 0,
this.h2 \= this.h2 + t << 0,
this.h3 \= this.h3 + n << 0)
}
,
Md5.prototype.hex \= function () {
this.finalize();
var e \= this.h0
, t \= this.h1
, n \= this.h2
, r \= this.h3;
return HEX\_CHARS\[e >> 4 & 15\] + HEX\_CHARS\[15 & e\] + HEX\_CHARS\[e >> 12 & 15\] + HEX\_CHARS\[e >> 8 & 15\] + HEX\_CHARS\[e >> 20 & 15\] + HEX\_CHARS\[e >> 16 & 15\] + HEX\_CHARS\[e >> 28 & 15\] + HEX\_CHARS\[e >> 24 & 15\] + HEX\_CHARS\[t >> 4 & 15\] + HEX\_CHARS\[15 & t\] + HEX\_CHARS\[t >> 12 & 15\] + HEX\_CHARS\[t >> 8 & 15\] + HEX\_CHARS\[t >> 20 & 15\] + HEX\_CHARS\[t >> 16 & 15\] + HEX\_CHARS\[t >> 28 & 15\] + HEX\_CHARS\[t >> 24 & 15\] + HEX\_CHARS\[n >> 4 & 15\] + HEX\_CHARS\[15 & n\] + HEX\_CHARS\[n >> 12 & 15\] + HEX\_CHARS\[n >> 8 & 15\] + HEX\_CHARS\[n >> 20 & 15\] + HEX\_CHARS\[n >> 16 & 15\] + HEX\_CHARS\[n >> 28 & 15\] + HEX\_CHARS\[n >> 24 & 15\] + HEX\_CHARS\[r >> 4 & 15\] + HEX\_CHARS\[15 & r\] + HEX\_CHARS\[r >> 12 & 15\] + HEX\_CHARS\[r >> 8 & 15\] + HEX\_CHARS\[r >> 20 & 15\] + HEX\_CHARS\[r >> 16 & 15\] + HEX\_CHARS\[r >> 28 & 15\] + HEX\_CHARS\[r >> 24 & 15\]
}
,
Md5.prototype.toString \= Md5.prototype.hex,
Md5.prototype.digest \= function () {
this.finalize();
var e \= this.h0
, t \= this.h1
, n \= this.h2
, r \= this.h3;
return \[255 & e, e >> 8 & 255, e >> 16 & 255, e >> 24 & 255, 255 & t, t >> 8 & 255, t >> 16 & 255, t >> 24 & 255, 255 & n, n >> 8 & 255, n >> 16 & 255, n >> 24 & 255, 255 & r, r >> 8 & 255, r >> 16 & 255, r >> 24 & 255\]
}
,
Md5.prototype.array \= Md5.prototype.digest,
Md5.prototype.arrayBuffer \= function () {
this.finalize();
var e \= new ArrayBuffer(16)
, t \= new Uint32Array(e);
return t\[0\] = this.h0,
t\[1\] = this.h1,
t\[2\] = this.h2,
t\[3\] = this.h3,
e
}
,
Md5.prototype.buffer \= Md5.prototype.arrayBuffer,
Md5.prototype.base64 \= function () {
for (var e, t, n, r = "", i = this.array(), s = 0; s < 15;)
e \= i\[s++\],
t \= i\[s++\],
n \= i\[s++\],
r += BASE64\_ENCODE\_CHAR\[e >>> 2\] + BASE64\_ENCODE\_CHAR\[63 & (e << 4 | t >>> 4)\] + BASE64\_ENCODE\_CHAR\[63 & (t << 2 | n >>> 6)\] + BASE64\_ENCODE\_CHAR\[63 & n\];
return e = i\[s\],
r \+ (BASE64\_ENCODE\_CHAR\[e >>> 2\] + BASE64\_ENCODE\_CHAR\[e << 4 & 63\] + "\==")
}
;
var exports \= createMethod();
COMMON\_JS ? module.exports \= exports : root.md5 = exports
}()
}
))
, secret \= "0b50b02fd0d73a9c4c8c3a781c30845f";
function createSign(e) {
if ("\[object Object\]" !== Object.prototype.toString.call(e))
throw new Error("The parameter of query must be a Object.");
var t \= Math.floor(Date.now() / 1e3);
Object.assign(e, {
timestamp: t
});
var n \= Object.keys(e);
n.sort();
for (var r = "", i = 0; i < n.length; i++) {
var s \= n\[i\];
r += (0 == i ? "" : "&") + s + "\=" + e\[s\]
}
return {
sign: md5(r += secret),
timestamp: t,
md5: md5
}
}
e \= {
"TSID": "T10049736422",
"appid": 16073360
}
console.log(createSign(e))
Python实现代码💥
import pprint
import requests
import execjs
import time
date\_time \= int(time.time())
f \= open('demo.js', encoding='utf-8').read()
js\_code \= execjs.compile(f)
link\_e \= {
"word": "许嵩",
"type": 1,
"pageNo": 3,
"pageSize": 20,
"appid": 16073360,
"timestamp": date\_time
}
link\_sign \= js\_code.call('createSign', link\_e)
print(link\_sign)
link \= f'https://music.91q.com/v1/search?sign={link\_sign\["sign"\]}&word=%E8%AE%B8%E5%B5%A9&type=1&pageNo=3&pageSize=20&appid=16073360×tamp={date\_time}'
headers \= {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
}
response \= requests.get(url=link, headers=headers)
for index in response.json()\['data'\]\['typeTrack'\]:
TSID \= index\['TSID'\]
e \= {
"TSID": TSID,
"appid": 16073360,
"timestamp": date\_time
}
sign \= js\_code.call('createSign', e)\['sign'\]
url \= f'https://music.91q.com/v1/song/tracklink?sign={sign}&appid=16073360&TSID={TSID}×tamp={date\_time}'
json\_data \= requests.get(url=url, headers=headers).json()
pprint.pprint(json\_data)
break
歌曲列表获取音乐ID同样也有一个sign加密参数, 加密方式是一样的, 改一下传入的参数就可以了
最后这里免费分享给大家一份Python全台学习资料,包含视频、源码。课件,希望能帮到那些不满现状,想提升自己却又没有方向的朋友,也可以和我一起来学习交流呀。
编程资料、学习路线图、源代码、软件安装包【点击这里】领取!
① Python所有方向的学习路线图
,清楚各个方向要学什么东西
② 100多节Python课程视频
,涵盖必备基础、爬虫和数据分析
③ 100多个Python实战案例
,学习不再是只会理论
④ 华为出品独家Python漫画教程
,手机也能学习
⑤ 历年互联网企业Python面试真题
,复习时非常方便
一、Python所有方向的学习路线
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、学习软件
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。
三、全套PDF电子书
书籍的好处就在于权威和体系健全,刚开始学习的时候你可以只看视频或者听某个人讲课,但等你学完之后,你觉得你掌握了,这时候建议还是得去看一下书籍,看权威技术书籍也是每个程序员必经之路。
四、入门学习视频
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
四、实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。