学习Golang有一阵子了,本文从一个有趣的切入点(男生感觉有趣吧)。开始了一段Golang之路。
需求场景描述
我有一个叫威威的大兄弟,对于老师们渴慕已久,但是又不慎了解。所以我打算写一个爬虫,爬一点老师的图片,来慰藉一下这位大兄弟。
简单的来说就是写一个爬虫,实战一下Go。当然我是初学者,里面涉及到的包我想细致的讲解,若有不足之处还请指出。
老师爬取器
思路是这样的,找一个网站,然后爬取其中的HTML下来。里面有许多图片的URL,然后下载图片的URL至本地即大功告成。
爬虫主体代码
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页面,并通过正则取出其中的Image标签。
- 第一步 创建请求: http.NewRequest("GET", url, nil),NewRequest使用指定的方法、网址和可选的主题创建并返回一个新的*Request。
- 第二步 发送请求:
- client := http.DefaultClient获取一个默认的HTTP客户端。
- res, err := client.Do(req),Do方法发送请求,返回HTTP回复。它会遵守客户端c设置的策略。这里这个Do请求是一个同步请求,也就是这个请求不返回,改Gorountine就会卡在这条语句上。
- 第三步 获取HTTP请求Body:
- 注意的是要在defer中将body close掉,原因参考link
- 将io.Reader对象转变成[]byte --> bodyByte, _ := ioutil.ReadAll(body)
- 第四步 正则匹配。
- 最好将得到的ImageTags的Slice放入Channel中,因为这里我们要开多个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发出一个GET请求,如果回应的状态码如下,Get会在调用c.CheckRedirect后执行重定向 --> imageRequest, err := http.Get(image)
- 第三步:确定image的名字,创建文件夹目录
- 第四步:创建一个空文件,Create方法。doc
- 最后拷贝数据,写入空文件。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)
}
}
- 设置多核并行(并行于Gorotine并发有本质区别)
- 开启多个Gorotine并行下载。
总结
这里是从一个奇葩的切入点,熟悉一下 CHannel和Gorotine的使用场景以及一些官方标准包的API熟悉。
希望能在Go的路上越走越远。