GoColly教程展示了如何在Golang中进行网页抓取和爬行。
Colly是一个用于Golang的快速网络抓取和爬行框架。它可用于数据挖掘、数据处理或归档等任务。
Colly具有自动cookie和会话处理功能。它支持同步、异步和并行抓取。它支持缓存,尊重robots.txt文件,并支持分布式抓取。
$ go version go version go1.18.1 linux/amd64
我们使用Go版本1.18。
牧羊犬收藏家
Collector是Colly的主要界面。它管理网络通信并负责在收集器作业运行时执行附加的回调。收集器使用Visit
函数启动。
Colly是一个基于事件的框架。我们将代码放入各种事件处理程序中。
Colly具有以下事件处理程序:
- OnRequest-在请求之前调用
- OnError-如果在请求期间发生错误则调用
- OnResponseHeaders-在收到响应标头之后调用
- OnResponse-在收到响应后调用
- OnHTML-如果接收到的内容是HTML,则在OnResponse之后立即调用
- OnXML-如果接收到的内容是HTML或XML,则在OnHTML之后立即调用
- OnScraped-在OnXML回调之后调用
GoColly简单示例
我们从一个简单的例子开始。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println(e.Text) }) c.Visit("http://webcode.me") }
该程序检索网站的标题。
import ( "fmt" "github.com/gocolly/colly/v2" )
首先,我们导入库。
c := colly.NewCollector()
收集器已创建。
c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println(e.Text) })
在OnHTML
处理程序中,我们注册了一个匿名函数,它会为每个标题标签调用。我们打印标题的文本。
c.Visit("http://webcode.me")
Visit
通过创建对指定URL的请求来启动收集器的收集工作。
$ go run title.go My html page
GoColly事件处理程序
我们可以对不同的事件处理程序做出反应。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() c.UserAgent = "Go program" c.OnRequest(func(r *colly.Request) { for key, value := range *r.Headers { fmt.Printf("%s: %s\n", key, value) } fmt.Println(r.Method) }) c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println("-----------------------------") fmt.Println(e.Text) }) c.OnResponse(func(r *colly.Response) { fmt.Println("-----------------------------") fmt.Println(r.StatusCode) for key, value := range *r.Headers { fmt.Printf("%s: %s\n", key, value) } }) c.Visit("http://webcode.me") }
在代码示例中,我们有OnRequest
、OnHTML
和OnResponse
的事件处理程序。
c.OnRequest(func(r *colly.Request) { fmt.Println("-----------------------------") for key, value := range *r.Headers { fmt.Printf("%s: %s\n", key, value) } fmt.Println(r.Method) })
在OnRequest
处理程序中,我们打印请求标头和请求方法。
c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println("-----------------------------") fmt.Println(e.Text) })
我们在OnHTML
处理程序中处理标题标签。
c.OnResponse(func(r *colly.Response) { fmt.Println("-----------------------------") fmt.Println(r.StatusCode) for key, value := range *r.Headers { fmt.Printf("%s: %s\n", key, value) } })
最后,我们在OnResponse
处理程序中打印响应的状态代码及其标头值。
$ go run callbacks.go User-Agent: [Go program] GET ----------------------------- 200 Connection: [keep-alive] Access-Control-Allow-Origin: [*] Server: [nginx/1.6.2] Date: [Sun, 23 Jan 2022 14:13:04 GMT] Content-Type: [text/html] Last-Modified: [Sun, 23 Jan 2022 10:39:25 GMT] ----------------------------- My html page
GoColly抓取本地文件
我们可以抓取本地磁盘上的文件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document title</title> </head> <body> <p>List of words</p> <ul> <li>dark</li> <li>smart</li> <li>war</li> <li>cloud</li> <li>park</li> <li>cup</li> <li>worm</li> <li>water</li> <li>rock</li> <li>warm</li> </ul> <footer>footer for words</footer> </body> </html>
我们有这个HTML文件。
package main import ( "fmt" "net/http" "github.com/gocolly/colly/v2" ) func main() { t := &http.Transport{} t.RegisterProtocol("file", http.NewFileTransport(http.Dir("."))) c := colly.NewCollector() c.WithTransport(t) words := []string{} c.OnHTML("li", func(e *colly.HTMLElement) { words = append(words, e.Text) }) c.Visit("file://./words.html") for _, p := range words { fmt.Printf("%s\n", p) } }
要抓取本地文件,我们必须注册一个文件协议。我们从列表中抓取所有单词。
$ go run local.go dark smart war cloud park cup worm water rock warm
顶级零售商
在下面的示例中,我们将获取美国的顶级零售商。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() i := 0 scan := true c.OnHTML("#stores-list--section-16266 td.data-cell-0,td.data-cell-1,td.data-cell-2,td.data-cell-3", func(e *colly.HTMLElement) { if scan { fmt.Printf("%s ", e.Text) } i++ if i%4 == 0 && i < 40 { fmt.Println() } if i == 40 { scan = false fmt.Println() } }) c.Visit("https://nrf.com/resources/top-retailers/top-100-retailers/top-100-retailers-2019") }
该示例打印2019年美国前10大零售商。
c.OnHTML("#stores-list--section-16266 td.data-cell-0,td.data-cell-1,td.data-cell-2,td.data-cell-3", func(e *colly.HTMLElement) {
我们必须查看HTML源代码并确定要查找的ID或类。在我们的例子中,我们从表中取出四列。
$ go run retail.go 1 Walmart $387.66 Bentonville, AR 2 Amazon.com $120.93 Seattle, WA 3 The Kroger Co. $119.70 Cincinnati, OH 4 Costco $101.43 Issaquah, WA 5 Walgreens Boots Alliance $98.39 Deerfield, IL 6 The Home Depot $97.27 Atlanta, GA 7 CVS Health Corporation $83.79 Woonsocket, RI 8 Target $74.48 Minneapolis, MN 9 Lowe's Companies $64.09 Mooresville, NC 10 Albertsons Companies $59.71 Boise, ID
GoColly抓取链接
在更复杂的任务中,我们需要抓取我们在文档中找到的链接。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println(e.Text) }) c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") c.Visit(e.Request.AbsoluteURL(link)) }) c.Visit("http://webcode.me/small.html") }
对于我们的示例,我们创建了一个包含三个链接的小型HTML文档。这三个链接不包含其他链接。我们打印每个文档的标题。
c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") c.Visit(e.Request.AbsoluteURL(link)) })
在OnHTML
处理程序中,我们找到所有链接,获取它们的href
属性并访问它们。
$ go run links.go Small My html page Something.
Stackoverflow问题
在下一个示例中,我们将抓取Stackoverflow问题。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) type question struct { title string excerpt string } func main() { c := colly.NewCollector() qs := []question{} c.OnHTML("div.summary", func(e *colly.HTMLElement) { q := question{} q.title = e.ChildText("a.question-hyperlink") q.excerpt = e.ChildText(".excerpt") qs = append(qs, q) }) c.OnScraped(func(r *colly.Response) { for idx, q := range qs { fmt.Println("---------------------------------") fmt.Println(idx + 1) fmt.Printf("Q: %s \n\n", q.title) fmt.Println(q.excerpt) } }) c.Visit("https://stackoverflow.com/questions/tagged/perl") }
程序从主页打印当前的Perl问题。
type question struct { title string excerpt string }
我们创建一个结构来保存问题标题及其摘录。
qs := []question{}
为问题创建切片。
c.OnHTML("div.summary", func(e *colly.HTMLElement) { q := question{} q.title = e.ChildText("a.question-hyperlink") q.excerpt = e.ChildText(".excerpt") qs = append(qs, q) })
我们将数据放入结构中。
c.OnScraped(func(r *colly.Response) { for idx, q := range qs { fmt.Println("---------------------------------") fmt.Println(idx + 1) fmt.Printf("Q: %s \n\n", q.title) fmt.Println(q.excerpt) } })
OnScraped
在所有工作完成后调用。这时候我们可以遍历切片并打印所有抓取的数据。
进入Colly异步模式
Colly工作的默认模式是同步的。我们使用Async
函数启用异步模式。在异步模式下,我们需要调用Wait
等待收集器作业完成。
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { urls := []string{ "http://webcode.me", "https://example.com", "http://httpbin.org", "https://www.perl.org", "https://www.php.net", "https://www.python.org", "https://code.visualstudio.com", "https://clojure.org", } c := colly.NewCollector( colly.Async(), ) c.OnHTML("title", func(e *colly.HTMLElement) { fmt.Println(e.Text) }) for _, url := range urls { c.Visit(url) } c.Wait() }
请注意,每次我们运行该程序时,我们都会得到不同顺序的返回标题。
在本教程中,我们使用Colly在Golang中执行网页抓取和爬取。
列出所有Go教程。