適合新手看的資源下載小程式
來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。大部分新手(比如我)看完golang聖經之後不知道該做些什麼好,那不如就來做一個資源下載小程式吧。這裡用一個視頻網站作為示範,來編寫一個小小的資源下載小程式因為是個小程式,所以所有函數和方法都放在一個 main 包裡。首先匯入我有用到的標準庫。```package mainimport ("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][]int64Lock 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 = -100return}defer resp.Body.Close()data, err2 := ioutil.ReadAll(resp.Body)if err2 != nil {statusCode = -200return}statusCode = resp.StatusCodecontent = 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 與 mapvar downListF DownListdownListF.Data = make(map[string][]int64)downList := &downListF//首先擷取index網頁的內容context, statusCode := Get(index_url)if statusCode != 200 {fmt.Println("error")return}/*提取並複製到二維數組html_result [][]stringhtml_result[n] []string匹配到的連結html_result[n][0] string 全匹配資料html_result[n][1] urlstringhtml_result[n][2] titlestring*/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行的資源下載小程式就完成啦1265 次點擊