写在前面
竟然大半夜不睡觉写博客。这个标题,是不是像一些无良卖课吸睛公众号,点开一看味同嚼蜡。
好吧,大半夜码字,因为实在激动。从下午六点多开始学习golang,花了三个小时了解变量、指针、数据类型、结构体、语法、函数以及golang的切片、管道、(go程)协程等特性,用半小时思考我该写个啥,花了一个半小时写了个小白入门爬虫,最后傻乐了半小时。
文章目录
写在前面前言一、Golang是什么?1.简介
二、使用步骤1.下载镜像2.下载GoLang(别的也行,VSCode)3.开干
三、核心特性1.goroutine协程1.1 什么是 Go 协程?1.2 goroutine和线程的比较1.3 goroutine协程实现原理
2.channel2.1 什么是 channel?2.2 咋用?
四、小小爬虫1.爬虫四步2.代码3.结果
总结
前言
我是写Java的,所以本文是浅尝辄止golang后一得之见,才疏学浅,希望对想了解golang的小伙伴有所帮助。
一、Golang是什么?
1.简介
官话:golang是Google开发的一种 静态强类型、编译型,并发型,并具有垃圾回收功能的编程语言。
个人看法:
有很多C语言的影子,比如语法、控制流结构、基础数据类型、传参、指针等,方便上手(Java写习惯了,顺手扔出来的分号,是那么的格格不入无处安放)能访问底层操作系统,还提供了强大的网络编程和并发编程支持(channel和Goroutine是核心)
二、使用步骤
1.下载镜像
镜像地址
下载对应的版本就行
2.下载GoLang(别的也行,VSCode)
链接在此
3.开干
个人途径
官方文档:文档一号,文档二号视频:视频在此
三、核心特性
1.goroutine协程
大道至简
1.1 什么是 Go 协程?
先理解进程、线程 之间概念的区别
对于 进程、线程,都是有内核进行调度,有 CPU 时间片的概念,进行 抢占式调度(有多种调度算法)
协程是用户级线程,对内核是透明的,系统并不知道有协程的存在。完全由用户自己的程序进行调度,通常只能进行协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到。
goroutine 和协程区别
本质上,goroutine 就是协程。 不同的是,Golang 在 runtime、系统调用等多方面对 goroutine调度进行了封装和处理,当遇到长时间执行或者进行系统调用时,会主动把当前 goroutine 的CPU 转让出去,让其他goroutine 能被调度并执行。从语言层面原生支持协程,在函数或者方法前面加 go关键字就可创建一个协程。
1.2 goroutine和线程的比较
内存消耗切换调度开销每个 goroutine (协程) 默认占用内存远比 Java 、C 的线程少。goroutine:2KB 线程:8MBgoroutine 远比线程小;线程:涉及模式切换(从用户态切换到内核态)、16个寄存器、PC、SP…等寄存器的刷新等。goroutine:只有三个寄存器的值修改 - PC / SP / DX.
1.3 goroutine协程实现原理
协程是基于线程的。内部实现上,维护了一组数据结构和 n 个线程,真正的执行还是线程,协程执行的代码被扔进一个待执行队列中,由这 n 个线程从队列中拉出来执行。这就解决了协程的执行问题。 暂时只了解到这里,待续
2.channel
2.1 什么是 channel?
channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于Linux中的管道。
2.2 咋用?
1.声明channel
2.引用类型
3.单向channel
var 变量名
chan 数据类型
channel和和
map类似,channel也一个对应
make创建的底层数据结构的引用。
当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是
nil。定义一个channel时,也需要定义发送到channel的值的类型。
var ch
chan int;
ch
= make(chan int);
ch
:=make(chan int);
var ch
= make(chan int);
单向
chan
read_only
:= make (<-chan int)
write_only
:= make (chan<- int)
四、小小爬虫
1.爬虫四步
踩点(找目标)http://www.netbian.com/fengjing/index_2.htm爬(拿数据)过滤(获取想要的)数据处理
2.代码
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
var (
chanImageUrls
chan string
wg sync
.WaitGroup
chanTask
chan string
)
func main() {
chanImageUrls
= make(chan string, 1000000)
chanTask
= make(chan string, 26)
for i
:= 2; i
< 10; i
++ {
wg
.Add(1)
go getImageUrls("http://www.netbian.com/fengjing/index_" + strconv
.Itoa(i
) + ".htm")
}
wg
.Add(1)
go checkOK()
for i
:= 0; i
< 3; i
++ {
wg
.Add(1)
go downloadImg()
}
wg
.Wait()
}
func downLoadFile(url
string, filename
string) (ok
bool) {
resp
, err
:= http
.Get(url
)
HandleError(err
, "http.get.url")
defer resp
.Body
.Close()
bytes
, err
:= ioutil
.ReadAll(resp
.Body
)
HandleError(err
, "res.Body")
filename
= "D:/test/" + filename
err
= ioutil
.WriteFile(filename
, bytes
, 0666)
if err
!= nil {
return false
} else {
return true
}
}
func downloadImg() {
for url
:= range chanImageUrls
{
filename
:= getFilenameFromUrl(url
)
ok
:= downLoadFile(url
, filename
)
if ok
{
fmt
.Println(filename
+ "下载成功")
} else {
fmt
.Println(filename
+ "下载失败")
}
}
}
func getFilenameFromUrl(url
string) (filename
string) {
lastIndex
:= strings
.LastIndex(url
, "/")
filename
= url
[lastIndex
+1:]
timePrefix
:= strconv
.Itoa(int(time
.Now().UnixNano()))
filename
= timePrefix
+ "_" + filename
return
}
func checkOK() {
var count
int
for {
url
:= <-chanTask
fmt
.Println("完成爬取 " + url
)
count
++
if count
== 26 {
close(chanImageUrls
)
break
}
}
wg
.Done()
}
func getImageUrls(url
string) {
urls
:= getImages(url
)
for _, url
:= range urls
{
chanImageUrls
<- url
}
chanTask
<- url
wg
.Done()
}
func getImages(url
string) (urls
[]string) {
pageStr
:= getResByPage(url
)
re
:= regexp
.MustCompile(reImage
)
result
:= re
.FindAllStringSubmatch(pageStr
, -1)
for _, res
:= range result
{
url
:= res
[0]
urls
= append(urls
, url
)
}
return urls
}
3.结果
总结
睡觉