返回提交历史
Modified
docs/misc/sign/wbi.md
+153
-1
XFEstudio/bilibili-API-collect
:sparkles: feat: add golang wbi demo (#696)
fc0c687
代码差异
1 个文件
+153
-1
@@ -93,7 +93,7 @@
93
93
94
94
## Wbi签名算法实现Demo
95
95
96
该 Demo 提供 [Python](#Python)、[JavaScript](#JavaScript) 语言
96
该 Demo 提供 [Python](#Python)、[JavaScript](#JavaScript)、[Golang](#Golang) 语言
97
97
98
98
### Python
99
99
@@ -248,3 +248,155 @@ console.log(query)
248
248
```
249
249
bar=514&baz=1919810&foo=114&wts=1684805578&w_rid=bb97e15f28edf445a0e4420d36f0157e
250
250
```
251
252
### Golang
253
254
需要 `github.com/tidwall/gjson` 作为依赖
255
256
```golang
257
package main
258
259
import (
260
"crypto/md5"
261
"encoding/hex"
262
"fmt"
263
"io/ioutil"
264
"net/http"
265
"net/url"
266
"sort"
267
"strconv"
268
"strings"
269
"sync"
270
"time"
271
272
"github.com/tidwall/gjson"
273
)
274
275
var mixinKeyEncTab = []int{
276
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
277
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
278
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
279
36, 20, 34, 44, 52,
280
}
281
282
func getMixinKey(orig string) string {
283
var str strings.Builder
284
for _, v := range mixinKeyEncTab {
285
if v < len(orig) {
286
str.WriteByte(orig[v])
287
}
288
}
289
return str.String()[:32]
290
}
291
func EncWbi(params map[string]string, imgKey string, subKey string) map[string]string {
292
mixinKey := getMixinKey(imgKey + subKey)
293
currTime := strconv.FormatInt(time.Now().Unix(), 10)
294
params["wts"] = currTime
295
// Sort keys
296
keys := make([]string, 0, len(params))
297
for k := range params {
298
keys = append(keys, k)
299
}
300
sort.Strings(keys)
301
// Remove unwanted characters
302
for k, v := range params {
303
v = strings.ReplaceAll(v, "!", "")
304
v = strings.ReplaceAll(v, "'", "")
305
v = strings.ReplaceAll(v, "(", "")
306
v = strings.ReplaceAll(v, ")", "")
307
v = strings.ReplaceAll(v, "*", "")
308
params[k] = v
309
}
310
// Build URL parameters
311
var str strings.Builder
312
for _, k := range keys {
313
str.WriteString(fmt.Sprintf("%s=%s&", k, params[k]))
314
}
315
query := strings.TrimSuffix(str.String(), "&")
316
// Calculate w_rid
317
hash := md5.Sum([]byte(query + mixinKey))
318
params["w_rid"] = hex.EncodeToString(hash[:])
319
return params
320
}
321
322
var cache sync.Map
323
var lastUpdateTime time.Time
324
325
func updateCache() {
326
if time.Now().Sub(lastUpdateTime).Minutes() < 10 {
327
return
328
}
329
imgKey, subKey := GetWbiKeys()
330
cache.Store("imgKey", imgKey)
331
cache.Store("subKey", subKey)
332
lastUpdateTime = time.Now()
333
}
334
335
func GetWbiKeysCached() (string, string) {
336
updateCache()
337
imgKeyI, _ := cache.Load("imgKey")
338
subKeyI, _ := cache.Load("subKey")
339
return imgKeyI.(string), subKeyI.(string)
340
}
341
342
func GetWbiKeys() (string, string) {
343
resp, err := http.Get("https://api.bilibili.com/x/web-interface/nav")
344
if err != nil {
345
fmt.Println("Error:", err)
346
return "", ""
347
}
348
defer resp.Body.Close()
349
body, err := ioutil.ReadAll(resp.Body)
350
if err != nil {
351
fmt.Println("Error:", err)
352
return "", ""
353
}
354
json := string(body)
355
imgURL := gjson.Get(json, "data.wbi_img.img_url").String()
356
subURL := gjson.Get(json, "data.wbi_img.sub_url").String()
357
imgKey := strings.Split(strings.Split(imgURL, "/")[len(strings.Split(imgURL, "/"))-1], ".")[0]
358
subKey := strings.Split(strings.Split(subURL, "/")[len(strings.Split(subURL, "/"))-1], ".")[0]
359
return imgKey, subKey
360
}
361
362
func SignURL(urlStr string) string {
363
urlObj, _ := url.Parse(urlStr)
364
imgKey, subKey := GetWbiKeysCached()
365
fmt.Println(imgKey, subKey)
366
query := urlObj.Query()
367
params := map[string]string{}
368
for k, v := range query {
369
params[k] = v[0]
370
}
371
newParams := EncWbi(params, imgKey, subKey)
372
for k, v := range newParams {
373
query.Set(k, v)
374
}
375
urlObj.RawQuery = query.Encode()
376
newUrlStr := urlObj.String()
377
return newUrlStr
378
}
379
func main() {
380
urlStr := "https://api.bilibili.com/x/space/wbi/acc/info?mid=1850091"
381
newUrlStr := utils.SignURL(urlStr) // 签名
382
req, err := http.NewRequest("GET", newUrlStr, nil)
383
if err != nil {
384
return nil, err
385
}
386
// 设置请求头,模拟浏览器
387
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
388
response, err := http.DefaultClient.Do(req)
389
fmt.Println(newUrlStr)
390
if err != nil {
391
fmt.Printf("请求发送失败:%s", err)
392
return nil, err
393
}
394
defer response.Body.Close()
395
body, err := ioutil.ReadAll(response.Body)
396
if err != nil {
397
fmt.Printf("读取响应失败:%s", err)
398
return nil, err
399
}
400
fmt.Println(string(body))
401
}
402
```