适合新手看的资源下载小程序

golang_291 · · 2905 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

大部分新手(比如我)看完golang圣经之后不知道该做些什么好,那不如就来做一个资源下载小程序吧。这里用一个视频网站作为示范,来编写一个小小的资源下载小程序 因为是个小程序,所以所有函数和方法都放在一个 main 包里。 首先导入我有用到的标准库。 ``` package main import ( "bufio" "fmt" "io" "io/ioutil" "net/http" "os" "path" "regexp" "strings" "sync" "time" ) ``` 一些全局变量 ``` var index_url string = "https://www.example.com"//这里就用example来替换掉它的域名,免得被人找麻烦 /* ptnIndexItem是一个个播放视频网页的链接 ptnVideoItem是为了匹配视频播放网页里的视频链接 dir 是你要下载的路径 */ var ptnIndexItem = regexp.MustCompile(`<a[^<>]+href *\= *[\"']?(\/[\d]+)\"[^<>]*title\=\"([^\"]*)\".*name.*>`) var dir string = "./example_video" var ptnVideoItem = regexp.MustCompile(`<a[^<>]+href *\= *[\"']?(https\:\/\/[^\"]+)\"[^<>]*download[^<>]*>`) //增加一个等待组 var wg sync.WaitGroup ``` 写一个结构体,为了存放每一个视频的下载进度,为什么用int64 是因为后面用到的文件大小和ContentLength都是int64 ``` /* 用于记录下载进度的结构体 */ type DownList struct { Data map[string][]int64 Lock sync.Mutex } ``` 懒得对每一个error进行判断,写一个检查用的函数 ``` /* 检查点 */ func check(e error) { if e != nil { panic(e) } } ``` 获取网页的方法 ``` /* url: 需要获取的网页 return: content 抓取到的网页源码 statusCode 返回的状态码 */ func Get(url string) (content string, statusCode int) { resp, err1 := http.Get(url) if err1 != nil { statusCode = -100 return } defer resp.Body.Close() data, err2 := ioutil.ReadAll(resp.Body) if err2 != nil { statusCode = -200 return } statusCode = resp.StatusCode content = string(data) return } ``` 用文件来记录这个视频的链接,每次下载之前先和这个文件里面的链接比较,有就不下载了,免得重复下载。 得先创建一个你钟意的文件 ``` /* param: filename: 文件名 text 需要比较的字符串 return: ture 没 false 有 */ func readOnLine(filename string, text string) bool { fi, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND, os.ModeAppend) check(err) defer fi.Close() text = text + "\n" br := bufio.NewReader(fi) for { a, c := br.ReadString('\n') if c == io.EOF { fmt.Println(text, "不存在,现在写入") fi.WriteString(text) return true } if string(a) == text { fmt.Println("存在", text) break } } return false } ``` lock 为了防止后面goroutines 并发读写map时候报错 \033[2J 是用来清空屏幕的,做到类似top的效果,虽然远不如top,但是能看到下载进度还是很爽的 ``` /* 输出下载进度的方法 */ func (downList *DownList) process() { for { downList.Lock.Lock() for key, arr := range downList.Data { fmt.Printf("%s progress: [%-50s] %d%% Done\n", key, strings.Repeat("#", int(arr[0]*50/arr[1])), arr[0]*100/arr[1]) } //fmt.Println(downList) downList.Lock.Unlock() time.Sleep(time.Second * 3) fmt.Printf("\033[2J") } } ``` 下载视频的函数, ``` /* url: 视频链接 filename: 本地文件名 downList: 用来记录下载进度的一个结构体的指针 */ func Down(url string, filename string, downList *DownList) bool { b := make([]byte, 1024) f, err := os.Create(filename) if err != nil { fmt.Println("创建文件失败") return false } defer f.Close() repo, err := http.Get(url) if err != nil { fmt.Println("获取资源失败") return false } defer repo.Body.Close() bufRead := bufio.NewReader(repo.Body) for { n, err := bufRead.Read(b) if err == io.EOF { break } f.Write(b[:n]) fileInfo, err := os.Stat(filename) fileSize := fileInfo.Size() //fmt.Println(fileSize, "--", repo.ContentLength) downList.Lock.Lock() downList.Data[filename] = []int64{fileSize, repo.ContentLength} downList.Lock.Unlock() } wg.Done() return true } ``` 下面就是main 函数了 ``` func main() { //初始化downList 与 map var downListF DownList downListF.Data = make(map[string][]int64) downList := &downListF //首先获取index网页的内容 context, statusCode := Get(index_url) if statusCode != 200 { fmt.Println("error") return } /* 提取并复制到二维数组 html_result [][]string html_result[n] []string 匹配到的链接 html_result[n][0] string 全匹配数据 html_result[n][1] url string html_result[n][2] title string */ html_result := ptnIndexItem.FindAllStringSubmatch(context, -1) length := len(html_result) go downList.process() for i := 0; i < length; i++ { v := html_result[i] video_html, video_status := Get(index_url + v[1]) if video_status != 200 { fmt.Println("error") continue } video_result := ptnVideoItem.FindAllStringSubmatch(video_html, -1) ok := readOnLine("test", v[1]) if len(video_result) > 0 && len(video_result[0]) > 0 && ok { //fmt.Println(video_result[0][1]) wg.Add(1) dirFile := path.Join(dir, html_result[i][2]) go Down(video_result[0][1], dirFile, downList) } //fmt.Println(i, video_html) } wg.Wait() return } ``` 这样,不到200行的资源下载小程序就完成啦

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2905 次点击  
加入收藏 微博
5 回复  |  直到 2017-11-23 15:30:25
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传