Skip to content

Commit 036fd69

Browse files
authored
Merge pull request #32 from laorange/master
新增:url自动解析(支持BV号)
2 parents 7996f28 + 603cf8b commit 036fd69

File tree

13 files changed

+327
-197
lines changed

13 files changed

+327
-197
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ gen
2525
# Dependency directories (remove the comment below to include it)
2626
# vendor/
2727

28+
/cmd/myapp.syso

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ENV GOPROXY="https://goproxy.io" GO111MODULE=on
88
RUN go mod download
99
COPY . .
1010
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o /go/bin/simple-golang-crawler cmd/start-concurrent-engine.go
11+
12+
1113
FROM alpine:3.7
1214
RUN apk update \
1315
&& apk upgrade \

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ build: ## Build the container
66
build-nc: ## Build the container without caching
77
docker build --no-cache -t $(APP_NAME) .
88
run:
9-
docker run -it -v ${CURDIR}/download:/download $(APP_NAME)
9+
## docker run -it -v ${CURDIR}/download:/download $(APP_NAME)
10+
docker run -it -v ${CURDIR}/output:/output $(APP_NAME)
1011

README.md

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
# FastestBibiliDownloader
22

3-
#### 项目地址:**[ FastestBilibiliDownloader](https://github.com/sodaling/FastestBilibiliDownloader)**
3+
## 原项目地址:**[ FastestBilibiliDownloader](https://github.com/sodaling/FastestBilibiliDownloader)**
4+
5+
> 项目仅用于学习交流,请勿用于任何商业用途!
6+
7+
## ⭐新增
8+
9+
自动解析 **想要下载的视频网址 / UP主个人主页网址**,支持:
10+
11+
- [x] [https://www.bilibili.com/video/**旧版的av号**/](#),av号是以`av`开头的**一串数字**
12+
- [x] [https://www.bilibili.com/video/**新版的BV号**/](#),BV号是以`BV`开头的**一串字符**
13+
- [x] [https://space.bilibili.com/**UP主的ID**/](#),UP主的ID是**一串数字**
14+
15+
![demo.png](demo.png)
16+
17+
## ⚠较原项目的删减
18+
19+
+ 由于FFmeg拼接、转化耗时太长,故移除了 `video merge`中的功能 。下载后的视频为`.flv`格式。
20+
21+
-----
22+
23+
## 👍原项目说明
424

525
**东半球第二快的Bilibili.com(B站)视频下载器!**
626

727
如果你想下载b站某个up主的所有视频,而且要飞快的那种,那么你可以试试这个项目-.-
828

9-
目前提供两个视频下载方案:
29+
目前提供两个(三个)视频下载方案:
1030

1131
1. 通过视频的aid,下载单个视频.
1232
2. 通过up主的upid(b站叫mid),下载这个up主所投稿的所有视频.
13-
33+
3. 通过视频的BVid,下载单个视频. **(new)**
1434

1535

1636
> 特性:
@@ -26,18 +46,18 @@
2646
> * 当单个aid视频分了若干个part时候,或者当你选了下载up主下所有视频时候.多个视频将会同时并行下载,跑满你的网速绝对不是问题.
2747
> * 下载与合并视频并行处理.如果视频分了多个part,下载完成的同时就会立即合并.该视频合并处理和其他与其他下载和合并同时进行且互不影响.
2848
29-
## 运行
30-
31-
下载的视频会存放在运行路径下的**download**文件夹下,每个视频(aid)一个文件夹。
49+
### 运行
3250

51+
下载的临时视频会存放在运行路径下的**download**文件夹下,每个视频(aid)一个文件夹,以**aid_视频标题**为文件夹名称。
52+
最终的视频会存放在运行路径下的**output**文件夹下,每个aid一个文件夹,以**视频标题**为文件夹名称。
3353
```shell
34-
Please enter your id type(`aid` or `upid`) #aid是视频id,upid是up主id,可以在up主主页地址栏获得。
35-
Please enter your id #输入id
54+
go run cmd/start-concurrent-engine.go -h # 获得参数
3655
```
3756

3857

3958

40-
### 使用Golang编译环境
59+
#### 使用Golang编译环境
60+
4161
1. 安装Golang编译环境
4262
* Ubuntu
4363
```shell
@@ -53,18 +73,18 @@ go env -w GOPROXY=https://goproxy.io #使用官方代理
5373
2. 一次性运行FastestBibiliDownloader
5474
程序入口在**cmd/start-concurrent-engine.go**,只需要
5575
```shell
56-
go run cmd/start-concurrent-engine.go
76+
go run cmd/start-concurrent-engine.go -t (aid/bvid/upid) -v (id)
5777
```
5878
首次运行会花时间下一大堆东西,然后按提示操作即可。
5979
注意,合并视频需要FFmpeg的支持。不然只会下载并不会自动合并。FFmpeg的安装教程请咨询搜索引擎。
6080

6181
3. 编译FastestBibiliDownloader
6282
```shell
63-
go build cmd/start-concurrent-engine.go
83+
go build cmd/start-concurrent-engine.go -t (aid/bvid/upid) -v (id)
6484
```
6585
之后直接运行./start-concurrent-engine即可。
6686

67-
### 如果你没有Golang编译环境,或者没有FFmeg环境。那么推荐用docker方式运行。已经写好了dockefile和makefile。你只需要:
87+
#### 如果你没有Golang编译环境,或者没有FFmeg环境。那么推荐用docker方式运行。已经写好了dockefile和makefile。你只需要:
6888

6989
```shell
7090
$ cd FastestBilibiliDownloader
@@ -74,9 +94,9 @@ go build cmd/start-concurrent-engine.go
7494

7595

7696

77-
### 后续有空会打包bin文件到release的。
97+
#### 后续有空会打包bin文件到release的。
7898

79-
## 感谢
99+
### 感谢
80100

81101
1. engine部分的框架参考**ccmouse**的思路,后面自己调整了整体架构部分,非常感谢。
82102
2. [bilibili-downloader](https://github.com/stevenjoezhang/bilibili-downloader):b站请求视频的API等等都是从这位的代码获得,本身的py代码注释也非常清晰,非常感谢。

cmd/start-concurrent-engine.go

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"fmt"
55
"log"
66
"os"
7+
"regexp"
78
"simple-golang-crawler/engine"
89
"simple-golang-crawler/parser"
910
"simple-golang-crawler/persist"
1011
"simple-golang-crawler/scheduler"
12+
"strconv"
1113
"sync"
1214
)
1315

@@ -21,27 +23,70 @@ func main() {
2123
panic(err)
2224
}
2325

24-
var idType string
25-
var id int64
26+
var urlInput string
27+
28+
var idType = "else"
29+
var aid int64
30+
var upid int64
31+
var bvid string
32+
33+
var params []string
34+
2635
var req *engine.Request
27-
fmt.Println("Please enter your id type(`aid` or `upid`)")
28-
fmt.Scan(&idType)
29-
fmt.Println("Please enter your id")
30-
fmt.Scan(&id)
36+
37+
fmt.Println("欢迎使用B站视频下载器 v1.0.1")
38+
fmt.Println("项目地址: https://github.com/laorange/FastestBilibiliDownloader")
39+
fmt.Println("原项目地址:https://github.com/sodaling/FastestBilibiliDownloader")
40+
fmt.Println("\n\n支持以下几种格式的输入:")
41+
fmt.Println("· https://www.bilibili.com/video/旧版的av号/ | av号 是以`av`开头的一串数字")
42+
fmt.Println("· https://www.bilibili.com/video/新版的BV号/ | BV号 是以`BV`开头的一串字符")
43+
fmt.Println("· https://space.bilibili.com/UP主的ID/ | UP主的ID 是一串数字")
44+
fmt.Print("\n\n请输入想要下载的视频网址/up主个人主页网址: ")
45+
fmt.Scan(&urlInput)
46+
47+
// bvid
48+
bvidRegexp := regexp.MustCompile(`/?(BV\w+)[/?]?`)
49+
params = bvidRegexp.FindStringSubmatch(urlInput)
50+
if params != nil {
51+
idType = "bvid"
52+
bvid = params[1]
53+
}
54+
55+
// aid
56+
aidRegexp := regexp.MustCompile(`/?(av\d+)/?`)
57+
params = aidRegexp.FindStringSubmatch(urlInput)
58+
if params != nil {
59+
idType = "aid"
60+
aid, _ = strconv.ParseInt(params[1], 10, 64)
61+
}
62+
63+
// upid
64+
upidRegexp := regexp.MustCompile(`space.bilibili.com/(\d+)/?`)
65+
params = upidRegexp.FindStringSubmatch(urlInput)
66+
if params != nil {
67+
idType = "upid"
68+
upid, _ = strconv.ParseInt(params[1], 10, 64)
69+
}
3170

3271
if idType == "aid" {
33-
req = parser.GetRequestByAid(id)
72+
req = parser.GetRequestByAid(aid)
73+
} else if idType == "bvid" {
74+
aid = parser.Bv2av(bvid)
75+
req = parser.GetRequestByAid(aid)
3476
} else if idType == "upid" {
35-
req = parser.GetRequestByUpId(id)
77+
req = parser.GetRequestByUpId(upid)
3678
} else {
37-
log.Fatalln("Wrong type you enter")
79+
req = nil
80+
log.Fatalln("您输入的网址无法解析,请查证后重试")
3881
os.Exit(1)
3982
}
4083

4184
queueScheduler := scheduler.NewConcurrentScheduler()
4285
conEngine := engine.NewConcurrentEngine(30, queueScheduler, itemChan)
43-
log.Println("Start working.")
86+
log.Println("开始下载...")
4487
conEngine.Run(req)
4588
wg.Wait()
46-
log.Println("All work has done")
89+
log.Print("所有视频均已下载完成。按 Ctrl+C 来退出程序。")
90+
var eof string
91+
fmt.Scan(&eof)
4792
}

demo.png

64.7 KB
Loading

fetcher/downloader.go

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"path/filepath"
1010
"simple-golang-crawler/model"
1111
"simple-golang-crawler/tool"
12+
"time"
1213
)
1314

1415
var _startUrlTem = "https://api.bilibili.com/x/web-interface/view?aid=%d"
@@ -39,13 +40,13 @@ func GenVideoFetcher(video *model.Video) FetchFun {
3940

4041
resp, err := client.Do(request)
4142
if err != nil {
42-
log.Fatalf("Fail to download the video %d,err is %s", video.ParCid.Cid, err)
43+
log.Fatalf("下载 %d 时出错, 错误信息:%s", video.ParCid.Cid, err)
4344
return nil, err
4445
}
4546

4647
if resp.StatusCode != http.StatusPartialContent {
47-
log.Fatalf("Fail to download the video %d,status code is %d", video.ParCid.Cid, resp.StatusCode)
48-
return nil, fmt.Errorf("wrong status code: %d", resp.StatusCode)
48+
log.Fatalf("下载 %d 时出错, 错误码:%d", video.ParCid.Cid, resp.StatusCode)
49+
return nil, fmt.Errorf("错误码: %d", resp.StatusCode)
4950
}
5051
defer resp.Body.Close()
5152

@@ -58,13 +59,18 @@ func GenVideoFetcher(video *model.Video) FetchFun {
5859
}
5960
defer file.Close()
6061

61-
log.Println(video.ParCid.ParAid.Title + ":" + filename + " is downloading.")
62+
log.Println("正在下载:" + video.ParCid.ParAid.Title + "\\" + filename)
6263
_, err = io.Copy(file, resp.Body)
6364
if err != nil {
64-
log.Printf("Failed to download video %d", video.ParCid.Cid)
65+
log.Printf("下载失败 aid: %d, cid: %d, title: %s, part: %s",
66+
video.ParCid.ParAid.Aid, video.ParCid.Cid, video.ParCid.ParAid.Title, video.ParCid.Part)
67+
log.Println("错误信息:", err)
68+
69+
// request again
70+
go requestLater(file, resp, video)
6571
return nil, err
6672
}
67-
log.Println(video.ParCid.ParAid.Title + ":" + filename + " has finished.")
73+
log.Println("下载完成:" + video.ParCid.ParAid.Title + "\\" + filename)
6874

6975
return nil, nil
7076
}
@@ -76,3 +82,16 @@ func genCheckRedirectfun(referer string) func(req *http.Request, via []*http.Req
7682
return nil
7783
}
7884
}
85+
86+
func requestLater(file *os.File, resp *http.Response, video *model.Video) error {
87+
88+
log.Println("连接失败,30秒后重试 (Unable to open the file due to the remote host, request in 30 seconds)")
89+
time.Sleep(time.Second * 30)
90+
91+
_, err := io.Copy(file, resp.Body)
92+
if err != nil {
93+
log.Printf("下载失败 aid: %d, cid: %d, title: %s, part: %s again",
94+
video.ParCid.ParAid.Aid, video.ParCid.Cid, video.ParCid.ParAid.Title, video.ParCid.Part)
95+
}
96+
return err
97+
}

model/bilibili.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type VideoCid struct {
4343
Cid int64
4444
ParAid *VideoAid
4545
Page int64
46+
Part string
4647
AllOrder int64
4748
}
4849

@@ -51,6 +52,6 @@ type Video struct {
5152
ParCid *VideoCid
5253
}
5354

54-
func NewVideoCidInfo(cid int64, parAid *VideoAid, page int64) *VideoCid {
55-
return &VideoCid{Cid: cid, ParAid: parAid, Page: page}
55+
func NewVideoCidInfo(cid int64, parAid *VideoAid, page int64, part string) *VideoCid {
56+
return &VideoCid{Cid: cid, ParAid: parAid, Page: page, Part: part}
5657
}

parser/aid.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@ import (
55
"simple-golang-crawler/engine"
66
"simple-golang-crawler/fetcher"
77
"simple-golang-crawler/model"
8-
8+
"simple-golang-crawler/tool"
9+
"math"
910
"github.com/tidwall/gjson"
1011
)
1112

1213
var _getAidUrlTemp = "https://api.bilibili.com/x/space/arc/search?mid=%d&ps=30&tid=0&pn=%d&keyword=&order=pubdate&jsonp=jsonp"
13-
var _getCidUrlTemp = "https://api.bilibili.com/x/player/pagelist?aid=%d"
14+
var _getCidUrlTemp = "https://api.bilibili.com/x/web-interface/view?aid=%d"
15+
//var _getCidUrlTemp = "https://api.bilibili.com/x/player/pagelist?aid=%d"
16+
17+
var table string = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"
18+
var s = [6]int{11, 10, 3, 8, 4, 6}
19+
var xor = 177451812
20+
var add = 8728348608
21+
var tr map[string]int
22+
1423

1524
func UpSpaceParseFun(contents []byte, url string) engine.ParseResult {
25+
1626
var retParseResult engine.ParseResult
1727
value := gjson.GetManyBytes(contents, "data.list.vlist", "data.page")
1828

@@ -25,22 +35,26 @@ func UpSpaceParseFun(contents []byte, url string) engine.ParseResult {
2535
}
2636

2737
func getAidDetailReqList(pageInfo gjson.Result) ([]*engine.Request, int64) {
38+
2839
var retRequests []*engine.Request
2940
var upid int64
3041
for _, i := range pageInfo.Array() {
3142
aid := i.Get("aid").Int()
3243
upid = i.Get("mid").Int()
3344
title := i.Get("title").String()
45+
title = tool.TitleEdit(title) // remove special characters
3446
reqUrl := fmt.Sprintf(_getCidUrlTemp, aid)
3547
videoAid := model.NewVideoAidInfo(aid, title)
36-
reqParseFunction := GenGetAidChildrenParseFun(videoAid)
48+
reqParseFunction := GenGetAidChildrenParseFun(videoAid) //子视频
3749
req := engine.NewRequest(reqUrl, reqParseFunction, fetcher.DefaultFetcher)
3850
retRequests = append(retRequests, req)
3951
}
4052
return retRequests, upid
4153
}
4254

55+
// 访问up主的时候 需要翻页
4356
func getNewBilibiliUpSpaceReqList(pageInfo gjson.Result, upid int64) []*engine.Request {
57+
4458
var retRequests []*engine.Request
4559

4660
count := pageInfo.Get("count").Int()
@@ -51,7 +65,7 @@ func getNewBilibiliUpSpaceReqList(pageInfo gjson.Result, upid int64) []*engine.R
5165
extraPage = 1
5266
}
5367
totalPage := count/ps + extraPage
54-
for i := int64(1); i < totalPage; i++ {
68+
for i := int64(1); i <= totalPage; i++ {
5569
if i == pn {
5670
continue
5771
}
@@ -63,6 +77,20 @@ func getNewBilibiliUpSpaceReqList(pageInfo gjson.Result, upid int64) []*engine.R
6377
}
6478

6579
func GetRequestByUpId(upid int64) *engine.Request {
80+
6681
reqUrl := fmt.Sprintf(_getAidUrlTemp, upid, 1)
6782
return engine.NewRequest(reqUrl, UpSpaceParseFun, fetcher.DefaultFetcher)
6883
}
84+
85+
// source code: https://blog.csdn.net/dotastar00/article/details/108805779
86+
func Bv2av(x string) int64 {
87+
tr = make(map[string]int)
88+
for i:=0; i<58; i++ {
89+
tr[string(table[i])] = i
90+
}
91+
r := 0
92+
for i:=0; i<6; i++ {
93+
r += tr[string(x[s[i]])] * int(math.Pow(float64(58), float64(i)))
94+
}
95+
return int64((r - add) ^ xor)
96+
}

0 commit comments

Comments
 (0)