Go语言实战爬虫项目
Go语言爬虫框架之Colly和Goquery
Python爬虫框架比较多有requests、urllib, pyquery,scrapy等,解析库有BeautifulSoup、pyquery、Scrapy和lxml等等,基于Go的爬虫框架是比较强健的,尤其Colly和Goquery是比较强大的工具,其灵活性和 表达性都比较优秀。
网络爬虫
网络爬虫是什么?从本质上讲,网络爬虫的工作原理通过检查web页面的HTML内容和执行某种类型的行动基于内容。通常,抓取暴露的链接,爬虫按照队列的去爬取。我们也可以从当前页面保存数据提取。例如,如果我们的维基百科页面上开始,我们可能保存页面的文本和标题。
爬虫的简单算法
initialize Queue
enqueue SeedURL
while Queue is not empty:
URL = Pop element from Queue
Page = Visit(URL)
Links = ExtractLinks(Page)
Enqueue Links on Queue
12345678
Visit和ExtractLinks函数是改变的地方,两个函数的应用都是特定的。我们的爬虫会尽力解释整个WEB的图,就像google一样,或者像Wikipedia一样简单一些。
随着你使用的用例的增加许多事情会变得复杂起来,许多许多的页面会被抓取,你可能需要一个更尖端的爬虫同时运行,对于更为复杂的页面,你需要一个更强大的HTML解释器。
Colly
Colly是一个基于Go语言的灵活的爬虫框架,开箱即用,你会获得一些速率限制,并行爬行等支持。
Colly基本组件之一是Collector,Collector保持跟踪那些需要被爬取的页面,并且保持回调当页面被爬取的时候。
一、开始
创造一个Collector是容易的,但是我们有许多可选项我们可以使用。
c := colly.NewCollector(
// Restrict crawling to specific domains
colly.AllowedDomains("godoc.org"),
// Allow visiting the same page multiple times
colly.AllowURLRevisit(),
// Allow crawling to be done in parallel / async
colly.Async(true),
)
12345678
你可以只有colly.NewCollector(),然后自己添加那些可选项。
我们也可以使用一些特别的限制让我们的爬虫表现的像一个行为良好的网络公民,Colly添加速率限制是简单的。
c.Limit(&colly.LimitRule{
// Filter domains affected by this rule
DomainGlob: "godoc.org/*",
// Set a delay between requests to these domains
Delay: 1 * time.Second
// Add an additional random delay
RandomDelay: 1 * time.Second,
})
12345678
某些网页可能对高流量的访问比较挑剔,他会将你断线。通常设置一个延迟维持几秒中就可让你里淘气榜单远一点。
从这里开始,我们能开始我们的collector通过一个URL种子。
c.Visit("https://godoc.org")
二、OnHTML
我们有一个好的collector他可以从任意网站开始工作,现在我们希望我们的collector做一些什么的话他需要检查页面以便提取链接和其他的数据。
colly.Collector.OnHTML方法允许注册一个回调为当收集器达到页面相匹配的一部分特定的HTML标签说明符。首先,我们可以得到一个回调时当爬虫看到[标记包含一个href链接。]()
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
// Extract the link from the anchor HTML element
link := e.Attr("href")
// Tell the collector to visit the link
c.Visit(e.Request.AbsoluteURL(link))
})
123456
就像和上面看到的一样,在这个回调中你得到一个colly.HTMLElement它包含了匹配到的HTML的数据。
现在,我们有一个实际的网络爬虫的开始:我们发现页面上的链接访问,并告诉我们的collector在后续请求访问这些链接。
OnHTML是一个功能强大的工具。它可以搜索CSS选择器(即div.my_fancy_class或# someElementId),你可以连接多个OnHTML回调你的收集器处理不同类型的页面。
Colly的HTMLElement结构非常有用。除了使用Attr函数获得那些属性之外,还可以提取文本。例如,我们可能想要打印页面的标题:
c.OnHTML("title", func(e *colly.HTMLElement) {
fmt.Println(e.Text)
})
123
三、OnRequest / OnResponse
有些时候你不需要一个特定的HTML元素从一个页面,而是想知道当你的爬虫检索或刚刚检索页面。为此,Colly暴露OnRequest OnResponse回调。
所有这些回调将被调用当访问到每个页面的时候。至于如何在符合OnHTML的使用要求。回调被调用的时候有一些顺序:1。OnRequest 2。OnResponse 3。OnHTML 4。OnScraped(在这边文章中没有提及到,但可能对你有用)
尤其使用的是OnRequest中止回调的能力。这可能是有用的,当你想让你的collector停止。
c.OnHTML("title", func(e *colly.HTMLElement) {
fmt.Println(e.Text)
})
123
在OnResponse,可以访问整个HTML文档,这可能是有用的在某些情况下:
c.OnResponse(func(r *colly.Response) {
fmt.Println(r.Body)
})
123
四、HTMLElement
除了colly.HTMLElement的Attr()方法和text,我们还可以使用它来遍历子元素。ChildText(),ChildAttr()特别是ForEach()方法非常有用。
例如,我们可以使用ChildText()获得所有段落的文本部分:
c.OnHTML("#myCoolSection", func(e *colly.HTMLElement) {
fmt.Println(e.ChildText("p"))
})
123
我们可以使用ForEach()循环遍历一个孩子匹配一个特定的元素选择器:
c.OnHTML("#myCoolSection", func(e *colly.HTMLElement) {
e.ForEach("p", func(_ int, elem *colly.HTMLElement) {
if strings.Contains(elem.Text, "golang") {
fmt.Println(elem.Text)
}
})
})
1234567
五、Bringing in Goquery
Colly的内置HTMLElement对大多数抓取任务都很有用,但是如果我们想对DOM进行特别高级的遍历,我们就必须去别处寻找。 例如,(目前)没有办法将DOM遍历到父元素或通过兄弟元素横向遍历。
输入Goquery,“就像那个j-thing,只在Go中”。 它基本上是jQuery。 在Go。 (这很棒)对于你想从HTML文档中删除的任何内容,可以使用Goquery完成。
虽然Goquery是以jQuery为模型的,但我发现它在很多方面与BeautifulSoup API非常相似。 所以,如果你来自Python抓取世界,那么你可能会对Goquery感到满意。
Goquery允许我们进行比Colly的HTMLElement提供的更复杂的HTML选择和DOM遍历。 例如,我们可能想要找到我们的锚元素的兄弟元素,以获得我们已经抓取的链接的一些上下文:
dom, _ := qoquery.NewDocument(htmlData)
dom.Find("a").Siblings().Each(func(i int, s *goquery.Selection) {
fmt.Printf("%d, Sibling text: %s\n", i, s.Text())
})
1234
此外,我们可以轻松找到所选元素的父级。 如果我们从Colly给出一个锚标记,并且我们想要找到页面
anchor.ParentsUntil("~").Find("title").Text()
1
ParentsUntil遍历DOM,直到找到与传递的选择器匹配的东西。 我们可以使用〜遍历DOM的顶部,然后允许我们轻松获取标题标记。
这实际上只是抓住了Goquery可以做的事情。 到目前为止,我们已经看到了DOM遍历的示例,但Goquery也对DOM操作提供了强大的支持 - 编辑文本,添加/删除类或属性,插入/删除HTML元素等。
将它带回网络抓取,我们如何将Goquery与Colly一起使用? 它很简单:每个Colly HTMLElement都包含一个Goquery选项,您可以通过DOM属性访问它。
c.OnHTML("div", func(e *colly.HTMLElement) {
// Goquery selection of the HTMLElement is in e.DOM
goquerySelection := e.DOM // Example Goquery usage
fmt.Println(qoquerySelection.Find(" span").Children().Text())
})
1234567
值得注意的是,大多数抓取任务都可以以不需要使用Goquery的方式构建! 只需为html添加一个OnHTML回调,就可以通过这种方式访问整个页面。 但是,我仍然发现Goquery是我的DOM遍历工具带的一个很好的补充。
实战项目
1. metalsucks专辑评论排名信息
代码
// go get github.com/PuerkitoBio/goquery
// git clone https://github.com/golang/net package main import (
"fmt"
"log"
"net/http" "github.com/PuerkitoBio/goquery"
) func main() {
// 请求html页面
res, err := http.Get("http://metalsucks.net")
if err != nil {
// 错误处理
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
}
// 加载 HTML document对象
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
// Find the review items
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
// For each item found, get the band and title
band := s.Find("a").Text()
title := s.Find("i").Text()
fmt.Printf("Review %d: %s - %s\n", i, band, title)
})
}
输出
Review 0: Darkthrone - Old Star
Review 1: Baroness - Gold & Grey
Review 2: Death Angel - Humanicide
Review 3: Devin Townsend - Empath
Review 4: Whitechapel - The Valley
2. emojipedia表情抓取(colly + goquery)
代码
package main import (
"fmt"
"strings"
"time" "github.com/PuerkitoBio/goquery"
"github.com/gocolly/colly"
) func main() {
c := colly.NewCollector(
colly.AllowedDomains("emojipedia.org"),
) // Callback for when a scraped page contains an article element
c.OnHTML("article", func(e *colly.HTMLElement) {
isEmojiPage := false
// Extract meta tags from the document
metaTags := e.DOM.ParentsUntil("~").Find("meta")
metaTags.Each(func(_ int, s *goquery.Selection) {
// Search for og:type meta tags
property, _ := s.Attr("property")
if strings.EqualFold(property, "og:type") {
content, _ := s.Attr("content") // Emoji pages have "article" as their og:type
isEmojiPage = strings.EqualFold(content, "article")
}
}) if isEmojiPage {
// Find the emoji page title
fmt.Println("Emoji: ", e.DOM.Find("h1").Text())
// Grab all the text from the emoji's description
fmt.Println(
"Description: ",
e.DOM.Find(".description").Find("p").Text())
}
}) // Callback for links on scraped pages
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
// Extract the linked URL from the anchor tag
link := e.Attr("href")
// Have our crawler visit the linked URL
c.Visit(e.Request.AbsoluteURL(link))
}) c.Limit(&colly.LimitRule{
DomainGlob: "*",
RandomDelay: 1 * time.Second,
}) c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL.String())
}) c.Visit("https://emojipedia.org")
}
运行结果
3.校花网图片爬取
- 代码
// 知识点
// 1. http 的用法,返回数据的格式、编码
// 2. 正则表达式
// 3. 文件读写
package main import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time" "github.com/axgle/mahonia"
) var workResultLock sync.WaitGroup func check(e error) {
if e != nil {
panic(e)
}
} func ConvertToString(src string, srcCode string, tagCode string) string {
srcCoder := mahonia.NewDecoder(srcCode)
srcResult := srcCoder.ConvertString(src)
tagCoder := mahonia.NewDecoder(tagCode)
_, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
result := string(cdata)
return result
} func download_img(request_url string, name string, dir_path string) {
image, err := http.Get(request_url)
check(err)
image_byte, err := ioutil.ReadAll(image.Body)
defer image.Body.Close()
file_path := filepath.Join(dir_path, name+".jpg")
err = ioutil.WriteFile(file_path, image_byte, 0644)
check(err)
fmt.Println(request_url + "\t下载成功")
} func spider(i int, dir_path string) {
defer workResultLock.Done()
url := fmt.Sprintf("http://www.xiaohuar.com/list-1-%d.html", i)
response, err2 := http.Get(url)
check(err2)
content, err3 := ioutil.ReadAll(response.Body)
check(err3)
defer response.Body.Close()
html := string(content)
html = ConvertToString(html, "gbk", "utf-8")
// fmt.Println(html)
match := regexp.MustCompile(`<img width="210".*alt="(.*?)".*src="(.*?)" />`)
matched_str := match.FindAllString(html, -1)
for _, match_str := range matched_str {
var img_url string
name := match.FindStringSubmatch(match_str)[1]
src := match.FindStringSubmatch(match_str)[2]
if strings.HasPrefix(src, "http") != true {
var buffer bytes.Buffer
buffer.WriteString("http://www.xiaohuar.com")
buffer.WriteString(src)
img_url = buffer.String()
} else {
img_url = src
}
download_img(img_url, name, dir_path)
}
} func main() {
start := time.Now()
dir := filepath.Dir(os.Args[0])
dir_path := filepath.Join(dir, "images")
err1 := os.MkdirAll(dir_path, os.ModePerm)
check(err1)
for i := 0; i < 4; i++ {
workResultLock.Add(1)
go spider(i, dir_path)
}
workResultLock.Wait()
fmt.Println(time.Now().Sub(start))
}
- 运行结果
- 下载的图片
Go语言实战爬虫项目的更多相关文章
- Go语言之高级篇Beego框架之爬虫项目实战
一.爬虫项目 1.爬虫基础 a.网页上面会有相同的数据 b.去重处理 布隆过滤器哈希存储 c.标签匹配: 正则表达式beautiful soup或lxml这种标签提取库 d.动态内容 phantomj ...
- 手把手和你一起实现一个Web框架实战——EzWeb框架(二)[Go语言笔记]Go项目实战
手把手和你一起实现一个Web框架实战--EzWeb框架(二)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 上一篇文章我们实现了框架的雏形,基本地 ...
- 手把手和你一起实现一个Web框架实战——EzWeb框架(三)[Go语言笔记]Go项目实战
手把手和你一起实现一个Web框架实战--EzWeb框架(三)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo3 这一篇文章 ...
- 手把手和你一起实现一个Web框架实战——EzWeb框架(四)[Go语言笔记]Go项目实战
手把手和你一起实现一个Web框架实战--EzWeb框架(四)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 这一篇文章主要实现路由组功能.实现路由 ...
- 手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战
手把手和你一起实现一个Web框架实战--EzWeb框架(五)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo5 中间件实现 ...
- C语言Linix服务器网络爬虫项目(二)项目设计和通过一个http请求抓取网页的简单实现
我们通过上一篇了解了爬虫具体要实现的工作之后,我们分析得出的网络爬虫的基本工作流程如下: 1.首先选取一部分精心挑选的种子URL: 2.将这些URL放入待抓取URL队列: 3.从待抓取URL队列中取出 ...
- C语言Linix服务器网络爬虫项目(一)项目初衷和网络爬虫概述
一.项目初衷和爬虫概述 1.项目初衷 本人的大学毕设就是linux上用c写的一个爬虫,现在我想把它完善起来,让他像一个企业级别的项目.为了重复发明轮子来学习轮子的原理,我们不使用第三方框架(这里是说的 ...
- go语言实战教程:实战项目资源导入和项目框架搭建
从本节内容开始,我们将利用我们所学习的Iris框架的相关知识,进行实战项目开发. 实战项目框架搭建 我们的实战项目是使用Iris框架开发一个关于本地服务平台的后台管理平台.平台中可以管理用户.商品.商 ...
- 【Python高级工程师之路】入门+进阶+实战+爬虫+数据分析整套教程
点击了解更多Python课程>>> 全网最新最全python高级工程师全套视频教程学完月薪平均2万 什么是Python? Python是一门面向对象的编程语言,它相对于其他语言,更加 ...
随机推荐
- 安全刻不容缓「GitHub 热点速览 v.21.50」
作者:HelloGitHub-小鱼干 本周最热的事件莫过于 Log4j 漏洞,攻击者仅需向目标输入一段代码,不需要用户执行任何多余操作即可触发该漏洞,使攻击者可以远程控制用户受害者服务器,90% 以上 ...
- Docker(4)-docker常用命令
帮助命令 docker version # 查看docker的版本信息 docker info # 查看docker的系统信息,包含镜像和容器的数量 docker --help # 帮助命令 dock ...
- Mysql配置文件 binlog和慢日志
目录 binlog binlog_format log_slave_updates log-bin|log-bin-index expire_logs_days relay-log|relay-log ...
- 如何在eclipse jee中检出项目并转换为Maven project
如何在eclipse jee中检出项目并转换为Maven project,最后转换为Dynamic web project 注意:该文档只针对以下eclipse版本,如图 为了方便,我将我本地的压缩包 ...
- Linux 编译安装、压缩打包、定时任务
目录 Linux 编译安装 知识储备: wget命令 编译安装 Linux 压缩打包 gzip压缩 bzip2压缩 tar打包 Linux 定时任务 相关文件及操作 Linux 编译安装 编译安装就是 ...
- Codeforces GYM 100876 J - Buying roads 题解
Codeforces GYM 100876 J - Buying roads 题解 才不是因为有了图床来测试一下呢,哼( 题意 给你\(N\)个点,\(M\)条带权边的无向图,选出\(K\)条边,使得 ...
- 用 Go 实现一个 LRU cache
前言 早在几年前写过关于 LRU cache 的文章: https://crossoverjie.top/2018/04/07/algorithm/LRU-cache/ 当时是用 Java 实现的,最 ...
- JS自动播放音频 无效chrome设置 (Uncaught (in promise) DOMException: play() failed because the user didn't interact)
Chrome的autoplay政策在2018年4月做了更改.新的行为:浏览器为了提高用户体验,减少数据消耗,现在都在遵循autoplay政策,Chrome的autoplay 政策非常简单 muted ...
- 缓存Bigkey坚决不要用,拆分是王道
大家好,我是架构摆渡人.这是实践经验系列的第四篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友. 背景介绍 在高并发的业务场景中,缓存是必须要上的,用来扛高并发 ...
- 【LeetCode】506. Relative Ranks 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 排序 argsort 堆 日期 题目地址:https ...