這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
package mainimport ( "encoding/json" "errors" "fmt" "net/http" "io" "os" "path" "io/ioutil" "sync")//翻唱FM的URL:http://www.qingting.fm/#/vchannels/136962/programs/5659080//翻唱FM的ajax地址:http://www.qingting.fm/s/vchannels/136962/programs/5745196/ajaxvar QingTingAjaxAddrFmt = "http://www.qingting.fm/s/vchannels/%d/programs/%d/ajax"//Go語言中json的解析,如果沒有固定的輸入格式,盡量使用通用格式 map [] interface{}//因為GO語言匯出結構體預設使用首字母大寫,而且需要和實際的格式進行對應,還不如使用map+interface+[]//type QingTingSongSubItem struct {// Name string "歌曲名稱"// ParentName string "專輯名稱"// QTSongs []string "歌曲URL"// ParentID int "父目錄ID"// Duration int "時間長度"// Type string "類型"// ID int "類型"// Thumb string "縮圖"//}////type QingTingSongItem struct {// PlayInfo QingTingSongSubItem//}////type QingTingPlayInfo QingTingSongItem////func (this *QingTingPlayInfo) Download(dirname string) (bool, error) {// return false, nil//}////func (this *QingTingPlayInfo) String() string{// return fmt.Sprintf("歌曲名稱:%#v 專輯名稱:%#v 歌曲URL地址:%#v 父目錄ID:%#v 時間長度:%#v 類型:%#v 縮圖:%#v",// this.PlayInfo.Name,// this.PlayInfo.ParentName,// this.PlayInfo.QTSongs,// this.PlayInfo.ParentID,// this.PlayInfo.Duration,// this.PlayInfo.Type,// this.PlayInfo.ID,// this.PlayInfo.Thumb)//}type QingTingPlayInfo map[string]interface{}func Download(this QingTingPlayInfo, dirname string, wg *sync.WaitGroup) (bool, error){ defer wg.Done() //pthis := (map[string]interface{})(this) name := this["name"].(string) url :="http://od.qingting.fm" + this["urls"].([]interface{})[0].(string) filename := path.Join(dirname, name+".m4a") fmt.Printf("開始下載:%s 到:%s\n", url, filename) res, err := http.Get(url) if err != nil{ fmt.Printf("下載錯誤:%s\n", err) return false,err } defer res.Body.Close() data, err := ioutil.ReadAll(res.Body) if err != nil{ fmt.Printf("下載失敗:%s\n", err) return false,err } err2 := ioutil.WriteFile(filename, data, 0666) if err2 != nil{ fmt.Printf("下載失敗:%s\n", err2) return false, err2 } fmt.Printf("!!!下載成功:%s 到:%s\n", url, filename) return true, nil}//-----------------------------------------------------------------type QingTingFM struct { Channel int "頻道" ID int "頻道ID"}func (this *QingTingFM) Crawl() ([]QingTingPlayInfo, error) { jsonbyte, err := this.readJson() if err != nil { return nil, err } if jsonbyte == nil{ return nil,errors.New("返回了無效資料") } //go語言和json的映射關係 :對應結構體資料 []對應數組 pis := make([]QingTingPlayInfo,0) jerr := json.Unmarshal(jsonbyte, &pis) if jerr != nil{ return nil,jerr } downloadDir := "music_go" os.MkdirAll(downloadDir, 0666) //for _,pi := range pis{ // fmt.Println(pi["name"]) //} var wg sync.WaitGroup //串連緩衝最大數目 maxIndex := len(pis) - 1 cacheMax := 5 for i,pi := range pis{ wg.Add(1) cacheMax++ //wg必須是傳引用,這裡必須要限制非同步個數,否則與伺服器的串連過多,伺服器會拒絕,從而導致下載失敗 go Download(pi,downloadDir, &wg) //使用緩衝 if cacheMax > 5 || maxIndex == i{ wg.Wait() cacheMax = 0 } } return pis, nil}func (this *QingTingFM) readJson() ([]byte, error) { //ajaxAddr := "http://www.golangtc.com/t/533c0ad2320b520cc400004f" //http.Get自動gzip解壓縮資料流 res, err := http.Get(this.getAajxAddr()) if err != nil { return nil, err } readerCloser := res.Body //讀取之後需要關閉 defer readerCloser.Close() if res.StatusCode != 200 { return nil, errors.New("返回錯誤狀態代碼") } bytes := make([]byte, 0, 1024) //buffer必須是數組,或者可以使用ioutil ReadAll來讀取 var buffera [128]byte buffer := buffera[:] for { readed, rerr := readerCloser.Read(buffer) if readed == 0 && rerr == nil{ return nil, errors.New("readed=0 rerr=nil") } //切片...,自動將數組解壓成很多個參數輸入 //實現原理和...,變參支援的原理一樣 //這句應該在 ==io.EOF之前,如果寫到後面,則會丟失最後部分的資料 bytes = append(bytes,buffer[:readed]...) //資料流讀取完畢 if rerr == io.EOF{ break } if rerr != nil { return nil, rerr } } //將bytes轉換成string return bytes, nil}func (this *QingTingFM) getAajxAddr() string { return fmt.Sprintf(QingTingAjaxAddrFmt, this.Channel, this.ID)}//-----------------------------------------------------------------func main() { qtfm := QingTingFM{Channel: 136962, ID: 5745196} qtfm.Crawl()}