go-colly官方文档翻译(持续翻译中)

GoColly是一个强大的Go语言爬虫框架,用于高效的数据抓取。它易于使用,提供了回调、调试、分布式抓取等功能。通过设置回调函数,可以控制请求、响应、错误处理等。Colly还支持代理、多线程、存储后端自定义、HTTP配置等高级特性,允许灵活的爬虫配置和分布式爬虫实现。
摘要由CSDN通过智能技术生成

介绍

如何安装

煤灰只有一个前提,那就是Golang编程语言。 你可以使用他们的安装指南 https://golang.org/doc/install

在终端输入以下命令安装煤灰和回车。

go get -u github.com/gocolly/colly/...

入门

在使用Colly之前,请确保您具有最新版本。有关更多详细信息,请参见

让我们从一些简单的例子开始。

首先,您需要将Colly导入您的代码库:

import "github.com/gocolly/colly"

收集器

煤灰的主要实体是一个 收集器对象。 收集器管理网络通信,并负责执行附加收集器工作运行时回调。 与煤灰,你必须初始化 收集器:

c := colly.NewCollector()

回调

你可以把不同类型的回调函数 收集器控制或检索信息收集工作。 检查 相关的部分 在包的文档。

添加回调 收集器

c.OnRequest(func(r *colly.Request) {
    fmt.Println("Visiting", r.URL)
})

c.OnError(func(_ *colly.Response, err error) {
    log.Println("Something went wrong:", err)
})

c.OnResponseHeaders(func(r *colly.Response) {
    fmt.Println("Visited", r.Request.URL)
})

c.OnResponse(func(r *colly.Response) {
    fmt.Println("Visited", r.Request.URL)
})

c.OnHTML("a[href]", func(e *colly.HTMLElement) {
    e.Request.Visit(e.Attr("href"))
})

c.OnHTML("tr td:nth-of-type(1)", func(e *colly.HTMLElement) {
    fmt.Println("First column of a table row:", e.Text)
})

c.OnXML("//h1", func(e *colly.XMLElement) {
    fmt.Println(e.Text)
})

c.OnScraped(func(r *colly.Response) {
    fmt.Println("Finished", r.Request.URL)
})

回调函数的调用顺序

1. OnRequest

在请求之前调用

2. OnError

如果请求期间发生错误,则调用

3. OnResponseHeaders

在收到响应标头后调用

4. OnResponse

收到回复后调用

5. OnHTML

OnResponse如果接收到的内容是HTML ,则在此之后立即调用

6. OnXML

OnHTML如果接收到的内容是HTML或XML ,则在之后调用

7. OnScraped

OnXML回调之后调用

配置

Colly是一个高度可定制的抓取框架。它具有合理的默认值,并提供了很多选项来更改它们。

收集器的配置

收集器属性的完整列表可以在这里找到。建议使用初始化收集器的方法colly.NewCollector(options...)

使用默认设置创建收集器:

c1 := colly.NewCollector()

创建另一个收集器,并更改User-Agent和url重新访问选

c2 := colly.NewCollector(
	colly.UserAgent("xy"),
	colly.AllowURLRevisit(),
)

或者

c2 := colly.NewCollector()
c2.UserAgent = "xy"
c2.AllowURLRevisit = true

通过覆盖收集器的属性,可以在刮削作业的任何时候更改配置。

一个很好的例子是一个User-Agent切换器,它可以在每个请求上更改User-Agent:

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func RandomString() string {
	b := make([]byte, rand.Intn(10)+10)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

c := colly.NewCollector()

c.OnRequest(func(r *colly.Request) {
	r.Headers.Set("User-Agent", RandomString())
})

过环境变量进行配置

可以通过环境变量来更改收集器的默认配置。这使我们可以微调收集器而无需重新编译。环境解析是收集器初始化的最后一步,因此初始化之后的每个配置更改都会覆盖从环境解析的配置。

环境变量配置

  • COLLY_ALLOWED_DOMAINS (以逗号分隔的域列表)
  • COLLY_CACHE_DIR (细绳)
  • COLLY_DETECT_CHARSET (是/否)
  • COLLY_DISABLE_COOKIES (是/否)
  • COLLY_DISALLOWED_DOMAINS (以逗号分隔的域列表)
  • COLLY_IGNORE_ROBOTSTXT (是/否)
  • COLLY_FOLLOW_REDIRECTS (是/否)
  • COLLY_MAX_BODY_SIZE (int)
  • COLLY_MAX_DEPTH (int-0表示无穷大)
  • COLLY_PARSE_HTTP_ERROR_RESPONSE (是/否)
  • COLLY_USER_AGENT (细绳)

HTTP配置

Colly使用Golang的默认http客户端作为网络层。可以通过更改默认的HTTP roundtripper来调整HTTP选项。

c := colly.NewCollector()
c.WithTransport(&http.Transport{
	Proxy: http.ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		DualStack: true,
	}).DialContext,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}

最佳实战

调试

有时一些就足够了 log.Println ()回调函数调用,但有时它不是。 煤灰有内置的收集器调试能力。 调试器调试器接口和不同类型的实现。

将调试器附加到一个收集器

将调试器需要一个基本的日志记录 调试( github.com/gocolly/colly/debug从煤灰的回购)包。

import (
	"github.com/gocolly/colly"
	"github.com/gocolly/colly/debug"
)

func main() {
    c := colly.NewCollector(colly.Debugger(&debug.LogDebugger{}))
    // [..]
}

实现一个自定义调试器

您可以创建任何类型的自定义调试器实现 debug.Debugger 接口。 就是一个很好的例子 LogDebugger

分布式抓取

分布式抓取可以以不同的方式实现根据抓取任务的要求是什么。 大部分时间是足够规模的网络通信层可以很容易地通过使用代理和煤灰的代理转换器。

代理转换器

使用代理扳道工刮仍然集中分布在多个代理服务器的HTTP请求。 通过其“煤灰支持代理切换 SetProxyFunc成员。 任何可以通过自定义函数 SetProxyFunc()的签名 func(*http.Request) (*url.URL, error)

在这里插入图片描述

煤灰有一个内置的代理切换器,旋转代理对每个请求的列表。

使用

package main

import (
	"github.com/gocolly/colly"
	"github.com/gocolly/colly/proxy"
)

func main() {
	c := colly.NewCollector()

	if p, err := proxy.RoundRobinProxySwitcher(
		"socks5://127.0.0.1:1337",
		"socks5://127.0.0.1:1338",
		"http://127.0.0.1:8080",
	); err == nil {
		c.SetProxyFunc(p)
	}
	// ...
}

实现自定义代理切换器:

var proxies []*url.URL = []*url.URL{
	&url.URL{Host: "127.0.0.1:8080"},
	&url.URL{Host: "127.0.0.1:8081"},
}

func randomProxySwitcher(_ *http.Request) (*url.URL, error) {
	return proxies[random.Intn(len(proxies))], nil
}

// ...
c.SetProxyFunc(randomProxySwitcher)

分布式刮刀

独立管理和分布式刮刀你能做的最好的就是包装的刮刀服务器。 服务器可以是任何类型的服务像HTTP、TCP服务器或Google App Engine。 使用自定义 存储 实现集中和持久的饼干和访问url处理。

在这里插入图片描述

可以找到一个示例实现 在这里

分布式存储

访问URL和饼干默认数据存储内存中。 短住刮刀的工作,这很方便,但它可以是一个严重的限制在处理大规模或爬行需要长时间运行的工作。

煤灰有能力取代默认的内存中存储任何存储后端实现 煤灰/ storage.Storage 接口。 看看 现有的存储

存储后端

存储后端

煤灰有一个内存中的存储后端存储饼干和访问url,但它可以覆盖任何自定义存储后端实现 煤灰/ storage.Storage

现有存储后端

内存中后端

默认端锅灰。 使用 collector.SetStorage () 覆盖。

复述,后端

看到 复述,例子 获取详细信息。

boltdb后端

SQLite3的后端

MongoDB的后端

PostgreSQL的后端

使用多个收集器

如果任务足够复杂或具有不同类型的子任务,建议使用多个收集器来执行一个抓取作业。一个很好的例子是Coursera课程抓取工具,其中使用了两个收集器-一个解析列表视图并处理分页,另一个则收集课程详细信息。

Colly具有一些内置方法来支持多个收集器的使用。

在这里插入图片描述

克隆采集器

Clone()如果收集器具有类似的配置,则可以使用收集器的方法。Clone()复制具有相同配置但没有附加回调的收集器。

c := colly.NewCollector(
	colly.UserAgent("myUserAgent"),
	colly.AllowedDomains("foo.com", "bar.com"),
)
// Custom User-Agent and allowed domains are cloned to c2
c2 := c.Clone()

在收集器之间传递自定义数据

使用收集器的Request()功能可以与其他收集器共享上下文。

共享上下文示例:

c.OnResponse(func(r *colly.Response) {
	r.Ctx.Put(r.Headers.Get("Custom-Header"))
	c2.Request("GET", "https://foo.com/", nil, r.Ctx, nil)
})

爬虫程序配置

Colly的默认配置经过优化,可以在一项作业中抓取较少数量的站点。如果您想抓取数百万个网站,则此设置不是最佳选择。以下是一些调整:

使用永久性存储后端

默认情况下,Colly将cookie和已访问的URL存储在内存中。您可以使用任何自定义后端替换内置的内存中存储后端。在这里查看更多详细信息。

将异步用于具有递归调用的长时间运行的作业

默认情况下,在请求未完成时Colly会阻塞,因此Collector.Visit从回调递归调用会产生不断增长的堆栈。有了Collector.Async = true这可避免。(不要忘了c.Wait()与async一起使用。)

禁用或限制连接保持活动状态

Colly使用HTTP保持活动来提高抓取速度。它需要打开文件描述符,因此长时间运行的作业很容易达到max-fd限制。

可以使用以下代码禁用HTTP Keep-alive:

c := colly.NewCollector()
c.WithTransport(&http.Transport{
    DisableKeepAlives: true,
})

扩展

扩展是Colly附带的小型帮助程序实用程序。插件列表可在此处获得

使用

下面的例子使随机代理切换器和两次引用setter扩展并访问httpbin.org。

import (
    "log"

    "github.com/gocolly/colly"
    "github.com/gocolly/colly/extensions"
)

func main() {
    c := colly.NewCollector()
    visited := false

    extensions.RandomUserAgent(c)
    extensions.Referer(c)

    c.OnResponse(func(r *colly.Response) {
        log.Println(string(r.Body))
        if !visited {
            visited = true
            r.Request.Visit("/get?q=2")
        }
    })

    c.Visit("http://httpbin.org/get")
}

例子

基本

package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector(
		// Visit only domains: hackerspaces.org, wiki.hackerspaces.org
		colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
	)

	// On every a element which has href attribute call callback
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Attr("href")
		// Print link
		fmt.Printf("Link found: %q -> %s\n", e.Text, link)
		// Visit link found on page
		// Only those links are visited which are in AllowedDomains
		c.Visit(e.Request.AbsoluteURL(link))
	})

	// Before making a request print "Visiting ..."
	c.OnRequest(func(r *colly.Request) {
		fmt.Println("Visiting", r.URL.String())
	})

	// Start scraping on https://hackerspaces.org
	c.Visit("https://hackerspaces.org/")
}

错误处理

package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {
	// Create a collector
	c := colly.NewCollector()

	// Set HTML callback
	// Won't be called if error occurs
	c.OnHTML("*", func(e *colly.HTMLElement) {
		fmt.Println(e)
	})

	// Set error handler
	c.OnError(func(r *colly.Response, err error) {
		fmt.Println("Request URL:", r.Request.URL, "failed with response:", r, "\nError:", err)
	})

	// Start scraping
	c.Visit("https://definitely-not-a.website/")
}

登录

package main

import (
	"log"

	"github.com/gocolly/colly"
)

func main() {
	// create a new collector
	c := colly.NewCollector()

	// authenticate
	err := c.Post("http://example.com/login", map[string]string{"username": "admin", "password": "admin"})
	if err != nil {
		log.Fatal(err)
	}

	// attach callbacks after login
	c.OnResponse(func(r *colly.Response) {
		log.Println("response received", r.StatusCode)
	})

	// start scraping
	c.Visit("https://example.com/")
}

最大深度

package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector(
		// MaxDepth is 1, so only the links on the scraped page
		// is visited, and no further links are followed
		colly.MaxDepth(1),
	)

	// On every a element which has href attribute call callback
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Attr("href")
		// Print link
		fmt.Println(link)
		// Visit link found on page
		e.Request.Visit(link)
	})

	// Start scraping on https://en.wikipedia.org
	c.Visit("https://en.wikipedia.org/")
}

多部分

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/gocolly/colly"
)

func generateFormData() map[string][]byte {
	f, _ := os.Open("gocolly.jpg")
	defer f.Close()

	imgData, _ := ioutil.ReadAll(f)

	return map[string][]byte{
		"firstname": []byte("one"),
		"lastname":  []byte("two"),
		"email":     []byte("onetwo@example.com"),
		"file":      imgData,
	}
}

func setupServer() {
	var handler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("received request")
		err := r.ParseMultipartForm(10000000)
		if err != nil {
			fmt.Println("server: Error")
			w.WriteHeader(500)
			w.Write([]byte("<html><body>Internal Server Error</body></html>"))
			return
		}
		w.WriteHeader(200)
		fmt.Println("server: OK")
		w.Write([]byte("<html><body>Success</body></html>"))
	}

	go http.ListenAndServe(":8080", handler)
}

func main() {
	// Start a single route http server to post an image to.
	setupServer()

	c := colly.NewCollector(colly.AllowURLRevisit(), colly.MaxDepth(5))

	// On every a element which has href attribute call callback
	c.OnHTML("html", func(e *colly.HTMLElement) {
		fmt.Println(e.Text)
		time.Sleep(1 * time.Second)
		e.Request.PostMultipart("http://localhost:8080/", generateFormData())
	})

	// Before making a request print "Visiting ..."
	c.OnRequest(func(r *colly.Request) {
		fmt.Println("Posting gocolly.jpg to", r.URL.String())
	})

	// Start scraping
	c.PostMultipart("http://localhost:8080/", generateFormData())
	c.Wait()
}

平行

package main

import (
	"fmt"
	"github.com/gocolly/colly"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector(
		// MaxDepth is 2, so only the links on the scraped page
		// and links on those pages are visited
		colly.MaxDepth(2),
		colly.Async(true),
	)

	// Limit the maximum parallelism to 2
	// This is necessary if the goroutines are dynamically
	// created to control the limit of simultaneous requests.
	//
	// Parallelism can be controlled also by spawning fixed
	// number of go routines.
	c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2})

	// On every a element which has href attribute call callback
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Attr("href")
		// Print link
		fmt.Println(link)
		// Visit link found on page on a new thread
		e.Request.Visit(link)
	})

	// Start scraping on https://en.wikipedia.org
	c.Visit("https://en.wikipedia.org/")
	// Wait until threads are finished
	c.Wait()
}

Proxy 切换

package main

import (
	"bytes"
	"log"

	"github.com/gocolly/colly"
	"github.com/gocolly/colly/proxy"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector(colly.AllowURLRevisit())

	// Rotate two socks5 proxies
	rp, err := proxy.RoundRobinProxySwitcher("socks5://127.0.0.1:1337", "socks5://127.0.0.1:1338")
	if err != nil {
		log.Fatal(err)
	}
	c.SetProxyFunc(rp)

	// Print the response
	c.OnResponse(func(r *colly.Response) {
		log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
	})

	// Fetch httpbin.org/ip five times
	for i := 0; i < 5; i++ {
		c.Visit("https://httpbin.org/ip")
	}
}

队列

package main

import (
	"fmt"

	"github.com/gocolly/colly"
	"github.com/gocolly/colly/queue"
)

func main() {
	url := "https://httpbin.org/delay/1"

	// Instantiate default collector
	c := colly.NewCollector()

	// create a request queue with 2 consumer threads
	q, _ := queue.New(
		2, // Number of consumer threads
		&queue.InMemoryQueueStorage{MaxSize: 10000}, // Use default queue storage
	)

	c.OnRequest(func(r *colly.Request) {
		fmt.Println("visiting", r.URL)
	})

	for i := 0; i < 5; i++ {
		// Add URLs to the queue
		q.AddURL(fmt.Sprintf("%s?n=%d", url, i))
	}
	// Consume URLs
	q.Run(c)
}

随机延迟

package main

import (
	"fmt"
	"time"

	"github.com/gocolly/colly"
	"github.com/gocolly/colly/debug"
)

func main() {
	url := "https://httpbin.org/delay/2"

	// Instantiate default collector
	c := colly.NewCollector(
		// Attach a debugger to the collector
		colly.Debugger(&debug.LogDebugger{}),
		colly.Async(true),
	)

	// Limit the number of threads started by colly to two
	// when visiting links which domains' matches "*httpbin.*" glob
	c.Limit(&colly.LimitRule{
		DomainGlob:  "*httpbin.*",
		Parallelism: 2,
		RandomDelay: 5 * time.Second,
	})

	// Start scraping in four threads on https://httpbin.org/delay/2
	for i := 0; i < 4; i++ {
		c.Visit(fmt.Sprintf("%s?n=%d", url, i))
	}
	// Start scraping on https://httpbin.org/delay/2
	c.Visit(url)
	// Wait until threads are finished
	c.Wait()
}

速率限制

package main

import (
	"fmt"

	"github.com/gocolly/colly"
	"github.com/gocolly/colly/debug"
)

func main() {
	url := "https://httpbin.org/delay/2"

	// Instantiate default collector
	c := colly.NewCollector(
		// Turn on asynchronous requests
		colly.Async(true),
		// Attach a debugger to the collector
		colly.Debugger(&debug.LogDebugger{}),
	)

	// Limit the number of threads started by colly to two
	// when visiting links which domains' matches "*httpbin.*" glob
	c.Limit(&colly.LimitRule{
		DomainGlob:  "*httpbin.*",
		Parallelism: 2,
		//Delay:      5 * time.Second,
	})

	// Start scraping in five threads on https://httpbin.org/delay/2
	for i := 0; i < 5; i++ {
		c.Visit(fmt.Sprintf("%s?n=%d", url, i))
	}
	// Wait until threads are finished
	c.Wait()
}

Redis后端

package main

import (
	"log"

	"github.com/gocolly/colly"
	"github.com/gocolly/colly/queue"
	"github.com/gocolly/redisstorage"
)

func main() {
	urls := []string{
		"http://httpbin.org/",
		"http://httpbin.org/ip",
		"http://httpbin.org/cookies/set?a=b&c=d",
		"http://httpbin.org/cookies",
	}

	c := colly.NewCollector()

	// create the redis storage
	storage := &redisstorage.Storage{
		Address:  "127.0.0.1:6379",
		Password: "",
		DB:       0,
		Prefix:   "httpbin_test",
	}

	// add storage to the collector
	err := c.SetStorage(storage)
	if err != nil {
		panic(err)
	}

	// delete previous data from storage
	if err := storage.Clear(); err != nil {
		log.Fatal(err)
	}

	// close redis client
	defer storage.Client.Close()

	// create a new request queue with redis storage backend
	q, _ := queue.New(2, storage)

	c.OnResponse(func(r *colly.Response) {
		log.Println("Cookies:", c.Cookies(r.Request.URL.String()))
	})

	// add URLs to the queue
	for _, u := range urls {
		q.AddURL(u)
	}
	// consume requests
	q.Run(c)
}

请求上下文

package main

import (
	"fmt"

	"github.com/gocolly/colly"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector()

	// Before making a request put the URL with
	// the key of "url" into the context of the request
	c.OnRequest(func(r *colly.Request) {
		r.Ctx.Put("url", r.URL.String())
	})

	// After making a request get "url" from
	// the context of the request
	c.OnResponse(func(r *colly.Response) {
		fmt.Println(r.Ctx.Get("url"))
	})

	// Start scraping on https://en.wikipedia.org
	c.Visit("https://en.wikipedia.org/")
}

Scraper 服务

package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gocolly/colly"
)

type pageInfo struct {
	StatusCode int
	Links      map[string]int
}

func handler(w http.ResponseWriter, r *http.Request) {
	URL := r.URL.Query().Get("url")
	if URL == "" {
		log.Println("missing URL argument")
		return
	}
	log.Println("visiting", URL)

	c := colly.NewCollector()

	p := &pageInfo{Links: make(map[string]int)}

	// count links
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Request.AbsoluteURL(e.Attr("href"))
		if link != "" {
			p.Links[link]++
		}
	})

	// extract status code
	c.OnResponse(func(r *colly.Response) {
		log.Println("response received", r.StatusCode)
		p.StatusCode = r.StatusCode
	})
	c.OnError(func(r *colly.Response, err error) {
		log.Println("error:", r.StatusCode, err)
		p.StatusCode = r.StatusCode
	})

	c.Visit(URL)

	// dump results
	b, err := json.Marshal(p)
	if err != nil {
		log.Println("failed to serialize response:", err)
		return
	}
	w.Header().Add("Content-Type", "application/json")
	w.Write(b)
}

func main() {
	// example usage: curl -s 'http://127.0.0.1:7171/?url=http://go-colly.org/'
	addr := ":7171"

	http.HandleFunc("/", handler)

	log.Println("listening on", addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

Url 筛选

package main

import (
	"fmt"
	"regexp"

	"github.com/gocolly/colly"
)

func main() {
	// Instantiate default collector
	c := colly.NewCollector(
		// Visit only root url and urls which start with "e" or "h" on httpbin.org
		colly.URLFilters(
			regexp.MustCompile("http://httpbin\\.org/(|e.+)$"),
			regexp.MustCompile("http://httpbin\\.org/h.+"),
		),
	)

	// On every a element which has href attribute call callback
	c.OnHTML("a[href]", func(e *colly.HTMLElement) {
		link := e.Attr("href")
		// Print link
		fmt.Printf("Link found: %q -> %s\n", e.Text, link)
		// Visit link found on page
		// Only those links are visited which are matched by  any of the URLFilter regexps
		c.Visit(e.Request.AbsoluteURL(link))
	})

	// Before making a request print "Visiting ..."
	c.OnRequest(func(r *colly.Request) {
		fmt.Println("Visiting", r.URL.String())
	})

	// Start scraping on http://httpbin.org
	c.Visit("http://httpbin.org/")
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go 编程语言是一个使得程序员更加有效率的开源项目。Go 是有表 达力、简洁、清晰和有效率的。它的并行机制使其很容易编写多核 和网络应用,而新的类型系统允许构建有性的模块化程序。Go 编译到机器码非常快速,同时具有便利的垃圾回收和强大的运行 时反射。它是快速的、静态类型编译语言,但是感觉上是动态类型 的,解释型语言。 1 简介 1 官方文档 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 前身. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 获得Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 在Windows 下获得Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2 基础 6 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 编译和运行代码. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 本书使用的设置. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 变量、类型和保留字 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 运算符和内建函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Go 保留字. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 控制结构 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 内建函数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 array 、slices 和map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3 函数 30 作用域 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 多值返回. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 命名返回值. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 延迟代码 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 变参. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 函数作为值. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 回调. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 恐慌(Panic)和恢复(Recover). . . . . . . . . . . . . . . . . . . . . . . 36 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4 包 48 标识符 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 包的文档. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 测试包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 常用的包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5 进阶 58 内存分配. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 定义自己的类型. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 ----------------------- 页面 5----------------------- ii Chapter: Contents 转换. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 6 接口 70 方法. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 接口名字 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 简短的例子. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 7 并发 82 更多关于channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 8 通讯 90 io.Reader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 一些例子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 命令行参数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 执行命令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 网络. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 练习. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 答案. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 A 版权 106 贡献者 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 许可证和版权 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 B 索引 108 C Bibliography 110 ListofFigures 1.1 Go 编年史. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2.1 array 与slice 对比 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.1 一个简单的LIFO 栈 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 6.1 使用反射去除层次关系. . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 ListofCodeExamples 2.1 Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Declaration with . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.3 Declaration with : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.4 Familiar types are still distinct . . . . . . . . . . . . . . . . . . . . . . . . . . 9 ----------------------- 页面 6----------------------- ListofCodeExamples iii 2.5 array 和slice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.6 Simple for loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.7 For loop with an array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.8 Fizz-Buzz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.9 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.10 Runes in strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.11 Reverse a string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.1 函数定义 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.2 递归函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.3 局部作用域 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.4 全局作用域 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.5 当函数调用函数时的作用域. . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.6 没有defer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.7 With defer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.8 函数符号 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.9 带参数的函数符号 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.10 在defer 访问返回值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.11 匿名函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.12 使用map 的函数作为值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.13 Go 的平均值函数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.14 stack.String() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.15 有变参的函数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.16 Go 编写的斐波那契函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.17 Map 函数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.18 冒泡排序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.1 A small package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.2 even 包的使用. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.3 even 包的测试. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.4 包里的Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.5 Push/Pop 测试 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.6 逆波兰计算器. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 5.1 Use of a pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.2 获取指针指向的值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.3 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.4 Go 更加通用的map 函数 . . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.5 cat 程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 6.1 定义结构和结构的方法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 6.2 用空接口作为参数的函数. . . . . . . . . . . . . . . . . . . . . . . . . . . 72 6.3 实现接口失败. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 6.4 扩展内建类型错误 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6.5 扩展非本地类型错误. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6.6 使用反射自省. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.7 反射类型和值. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 6.8 私有成员的反射 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 6.9 公有成员的反射 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 6.10 通用的计算最大值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 7.1 Go routine 实践 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 7.2 Go routines 和channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 7.3 使用select . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 7.4 Go 的channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 ----------------------- 页面 7----------------------- iv Chapter: Contents 7.5 添加额外的退出channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 7.6 Go 的斐波那契函数. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 8.1 从文件读取(无缓冲) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 8.2 从文件读取(缓冲). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 8.3 Processes in Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 8.6 uniq(1) 的Perl 实现 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 8.4 Go 的进程. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 8.5 wc(1) 的Go 实现. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 8.7 uniq(1) 的Go 实现. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 8.8 一个Go quine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 8.9 简易echo 服务器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 8.10 数字游戏 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 8.11 finger 守护进程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 ListofExercises 1 (1) 文档. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2 (0) For-loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3 (0) FizzBuzz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4 (1) Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 5 (1) Average . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 6 (0) 平均值 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 7 (0) 整数顺序. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 8 (1) 作用域 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 9 (1) 栈 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 10 (1) 变参. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 11 (1) 斐波那契. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 12 (1) Map function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 13 (0) 最小值和最大值. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 14 (1) 冒泡排序. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 15 (1) 函数返回一个函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 16 (0) stack 包. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 17 (2) 计算器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 18 (1) 指针运算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 19 (2) 使用interface 的map 函数 . . . . . . . . . . . . . . . . . . . . . . . . . 65 20 (1) 指针. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 21 (1) 链表. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 22 (1) Cat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 23 (2) 方法调用. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 24 (1) 接口和编译 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 25 (1) 指针和反射 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 26 (2) 接口和max() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 27 (1) Channel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 28 (2) 斐波那契II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 29 (2) 进程. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 30 (0) 单词和字母统计. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 31 (0) Uniq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 32 (2) Quine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 33 (1) Echo 服务. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 ----------------------- 页面 8----------------------- ListofExercises v 34 (2) 数字游戏. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 35 (1) *Finger 守护进程. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值