學(xué)習(xí)Golang有一陣子了满葛,本文從一個(gè)有趣的切入點(diǎn)(男生感覺(jué)有趣吧)昌罩。開(kāi)始了一段Golang之路。
需求場(chǎng)景描述
我有一個(gè)叫威威的大兄弟饭冬,對(duì)于老師們渴慕已久使鹅,但是又不慎了解。所以我打算寫(xiě)一個(gè)爬蟲(chóng)昌抠,爬一點(diǎn)老師的圖片患朱,來(lái)慰藉一下這位大兄弟。
簡(jiǎn)單的來(lái)說(shuō)就是寫(xiě)一個(gè)爬蟲(chóng)炊苫,實(shí)戰(zhàn)一下Go裁厅。當(dāng)然我是初學(xué)者,里面涉及到的包我想細(xì)致的講解侨艾,若有不足之處還請(qǐng)指出执虹。
老師爬取器
思路是這樣的,找一個(gè)網(wǎng)站蒋畜,然后爬取其中的HTML下來(lái)声畏。里面有許多圖片的URL,然后下載圖片的URL至本地即大功告成姻成。
爬蟲(chóng)主體代碼
func checkError(err error) bool {
if err != nil {
fmt.Println(err)
return false
}
return true
}
func CrawlingImageTags(url string, imageChan chan []string) {
req, err := http.NewRequest("GET", url, nil)
if !checkError(err) {
return
}
client := http.DefaultClient
res, err := client.Do(req)
if !checkError(err) {
return
}
if res.StatusCode == 200 {
body := res.Body
defer body.Close()
bodyByte, _ := ioutil.ReadAll(body)
resStr := string(bodyByte)
reg, _ := regexp.Compile("<img .*>")
imageTags := reg.FindAllString(resStr, -1)
imageChan <- imageTags
}
}
代碼解釋:
- checkError方法插龄,檢查err。
- CrawlingImageTags方法科展,爬取HTML頁(yè)面均牢,并通過(guò)正則取出其中的Image標(biāo)簽。
- 第一步 創(chuàng)建請(qǐng)求: http.NewRequest("GET", url, nil)才睹,NewRequest使用指定的方法徘跪、網(wǎng)址和可選的主題創(chuàng)建并返回一個(gè)新的*Request。
- 第二步 發(fā)送請(qǐng)求:
- client := http.DefaultClient獲取一個(gè)默認(rèn)的HTTP客戶端琅攘。
- res, err := client.Do(req)垮庐,Do方法發(fā)送請(qǐng)求,返回HTTP回復(fù)坞琴。它會(huì)遵守客戶端c設(shè)置的策略哨查。這里這個(gè)Do請(qǐng)求是一個(gè)同步請(qǐng)求,也就是這個(gè)請(qǐng)求不返回剧辐,改Gorountine就會(huì)卡在這條語(yǔ)句上寒亥。
- 第三步 獲取HTTP請(qǐng)求Body:
- 注意的是要在defer中將body close掉邮府,原因參考link
- 將io.Reader對(duì)象轉(zhuǎn)變成[]byte --> bodyByte, _ := ioutil.ReadAll(body)
- 第四步 正則匹配。
- 最好將得到的ImageTags的Slice放入Channel中溉奕,因?yàn)檫@里我們要開(kāi)多個(gè)Gorountine去爬取褂傀。
下載圖片
func getImageByUrl(url string) {
reg, err := regexp.Compile("/.*jpg")
if checkError(err) {
return
}
image := host + reg.FindString(url)
imageRequest, err := http.Get(image)
if checkError(err) {
return
}
data, err := ioutil.ReadAll(imageRequest.Body)
defer imageRequest.Body.Close()
if checkError(err) {
return
}
path := strings.Split(image, "/")
var name string
if len(path) > 1 {
name = "image/" + path[len(path)-1]
}
os.Mkdir("image", os.ModeType)
out, err := os.Create(name)
if checkError(err) {
return
}
io.Copy(out, bytes.NewReader(data))
}
func getImageBySlice(imageSlice []string) {
fmt.Println("back")
for _, value := range imageSlice {
getImageByUrl(value)
}
}
代碼解釋:
- 第一步:正則匹配挑出.jpg的URL地址,并拼接上host加勤。
- 第二步:Get向指定的URL發(fā)出一個(gè)GET請(qǐng)求仙辟,如果回應(yīng)的狀態(tài)碼如下,Get會(huì)在調(diào)用c.CheckRedirect后執(zhí)行重定向 --> imageRequest, err := http.Get(image)
- 第三步:確定image的名字胸竞,創(chuàng)建文件夾目錄
- 第四步:創(chuàng)建一個(gè)空文件欺嗤,Create方法。doc
- 最后拷貝數(shù)據(jù)卫枝,寫(xiě)入空文件。doc
最后是main方法
const url string = "http://www.ttpaihang.com/vote/rank.php?voteid=1089&page="
const host string = "http://www.ttpaihang.com"
func main() {
runtime.GOMAXPROCS(4)
i := 0
for {
i++
imageChan := make(chan []string, 10)
go CrawlingImageTags(url+strconv.Itoa(i), imageChan)
go getImageBySlice(<-imageChan)
}
}
- 設(shè)置多核并行(并行于Gorotine并發(fā)有本質(zhì)區(qū)別)
- 開(kāi)啟多個(gè)Gorotine并行下載讹挎。
總結(jié)
這里是從一個(gè)奇葩的切入點(diǎn)校赤,熟悉一下 CHannel和Gorotine的使用場(chǎng)景以及一些官方標(biāo)準(zhǔn)包的API熟悉。
希望能在Go的路上越走越遠(yuǎn)筒溃。