第一次在掘金水文章,有一点点小激动,哈哈

本次使用Golang抓取著名(la ji)游戏媒体 游民星空

主要使用的第三方包是 goquery ,来解析HTML,如果你没有使用过goquery也不要紧,非常简单。

其次是使用Golang将数据插入MySql。

首先,使用net/http包请求网页

func main() {url := "https://www.gamersky.com/news/"resp, err := http.Get(url)if err != nil {log.Fatal(err)}defer resp.Body.Close()if res.StatusCode != 200 {log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)}
}
复制代码

这里请求网页错误是不能容忍的,所以使用log.Fatal出现错误时直接退出。

接下来使用goquery.NewDocumentFromReader将HTML加载为可解析的类型。

    // NewDocumentFromReader returns a Document from an io.Reader.html, err := goquery.NewDocumentFromReader(resp.Body)
复制代码

接下里我们就可以使用goquery解析HTML页面了。

首先我们获取这一页所有的新闻链接

这里新闻链接出现在class="tt"a标签下,所以我们使用goquery,解析出该页面下所有属性为'tt'的a标签的href属性,就可以拿到所有改页面下的新闻链接了。

func getNewsList(html *goquery.Document, newsList []string) []string {html.Find("a[class=tt]").Each(func(i int, selection *goquery.Selection) {url, _ := selection.Attr("href")newsList = append(newsList, url)})return newsList
}
复制代码

这样我们就拿到了所有新闻首页的新闻链接,并把所有的链接放在了newsList这个slice中。

接下来我们就开始爬取这些新闻链接中的具体新闻吧。

使用goroutine实现并发的请求这些新闻链接,并解析出结果。

        var newsList []stringnewsList = getNewsList(html, newsList)var wg sync.WaitGroupfor i := 0; i < len(newsList); i++ {wg.Add(1)go getNews(newsList[i], &wg)}wg.Wait()
复制代码

首先我们初始化一个sync.WaitGroup,用来控制goroutine的运行,确保所有的goroutine运行完成。

遍历我们存放了所有新闻链接的这个newsList,一个新闻链接开启一个对应的goroutine来处理接下来的处理过程。

wg.Wait()用来阻塞程序运行,直到wg中所有的任务都完成。

接下来开始解析每个新闻页面,得到我们想要的数据。

首先我们定义News这个结构体。

type News struct {Title   stringMedia   stringUrl     stringPubTime stringContent string
}
复制代码

与第一步相同的是首先我们需要请求新闻链接。

func getNews(url string, wg *sync.WaitGroup) {resp, err := http.Get(url)if err != nil {log.Println(err)wg.Done()return}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {log.Printf("Error: status code %d", resp.StatusCode)wg.Done()return}html, err := goquery.NewDocumentFromReader(resp.Body)news := News{}
复制代码

通过以上的这些步骤,我们成功请求到的HTML已经转成了可以使用goquer解析的对象了。

标题在class="Mid2L_tit"的div下的h1中。

html.Find("div[class=Mid2L_tit]>h1").Each(func(i int, selection *goquery.Selection) {news.Title = selection.Text()})if news.Title == "" {wg.Done()return}
复制代码

这里个别新闻专栏是和普通新闻页面格式是不同的,暂时就不错处理了,所以当没有解析出Title时就返回。

接下来是时间的处理,我们可以看到时间在div class="detail"下,但是这样解析出来的时间是不能直接保存在数据库中的,在这里我使用正则表达式将所有的日期时间提取出来,在拼接成可以保存在数据库中的格式。

    var tmpTime stringhtml.Find("div[class=detail]").Each(func(i int, selection *goquery.Selection) {tmpTime = selection.Text()})reg := regexp.MustCompile(`\d+`)timeString := reg.FindAllString(tmpTime, -1)news.PubTime = fmt.Sprintf("%s-%s-%s %s:%s:%s", timeString[0], timeString[1], timeString[2], timeString[3], timeString[4], timeString[5])
复制代码

如果有更好的办法,大家一定要教我啊!!!

接下里是解析新闻正文

新闻正文都在div class="Mid2L_con"下的p标签中。

html.Find("div[class=Mid2L_con]>p").Each(func(i int, selection *goquery.Selection) {news.Content = news.Content + selection.Text()
})
复制代码

现在我们拿到了所有我们需要的数据,接下来就是将这些数据存入MySql。

首先建立一张名为gamesky的表。

create table gamesky
(id          int auto_incrementprimary key,title       varchar(256)                        not null,media       varchar(16)                         not null,url         varchar(256)                        not null,content     varchar(4096)                       null,pub_time    timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP,create_time timestamp default CURRENT_TIMESTAMP not null
);
复制代码

接下来我们建立Mysql连接。

package mysqlimport ("database/sql""fmt""os"_ "github.com/go-sql-driver/mysql"
)var db *sql.DBfunc init() {db, _ = sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/game_news?charset=utf8")db.SetMaxOpenConns(1000)err := db.Ping()if err != nil {fmt.Println("Failed to connect to mysql, err:" + err.Error())os.Exit(1)}
}func DBCon() *sql.DB {return db
}
复制代码

接下来就是使用我们建立的MySql连接,保存我们获取到的数据了。

    db := mysql.DBCon()stmt, err := db.Prepare("insert into news (`title`, `url`, `media`, `content`, `pub_time`) values (?,?,?,?,?)")if err != nil {log.Println(err)wg.Done()}defer stmt.Close()rs, err := stmt.Exec(news.Title, news.Url, news.Media, news.Content, news.PubTime)if err != nil {log.Println(err)wg.Done()}if id, _ := rs.LastInsertId(); id > 0 {log.Println("插入成功")}wg.Done()
复制代码

rs.LastInsertId()是用来获取刚刚插入数据库的数据的id的,插入成功的话就会返回对应记录的id,由此我们可以知道是否插入成功。

新闻正文的长度有时长度会超过MySql中设定好的列长度,可以修改列长度或者截取一部分正文保存。

在一个goroutine中出现错误,或者保存数据库结束之后,要记得wg.Done()来让wg中的任务数减1。

这样我们的爬虫就并发的将新闻抓取下来,并保存入数据库中了。

可以看到由于我们抓取的速度太快,已经触发了游民星空的反爬虫,所以需要降低频率才可以,但是这样就失去了Golang并发的优势,所以说既想并发抓取数据又不想被反爬虫,配置一个不错的代理池很有必要,但是这里就不做说明了。

接下来上完整的爬虫代码

package mainimport ("fmt""game_news/mysql""log""net/http""regexp""sync""github.com/PuerkitoBio/goquery"
)type News struct {Title   stringMedia   stringUrl     stringPubTime stringContent string
}func main() {url := "https://www.gamersky.com/news/"resp, err := http.Get(url)if err != nil {log.Fatal(err)}defer resp.Body.Close()if resp.StatusCode != 200 {log.Fatalf("status code error: %d %s", resp.StatusCode, resp.Status)}html, err := goquery.NewDocumentFromReader(resp.Body)var newsList []stringnewsList = getNewsList(html, newsList)var wg sync.WaitGroupfor i := 0; i < len(newsList); i++ {wg.Add(1)go getNews(newsList[i], &wg)}wg.Wait()
}func getNewsList(html *goquery.Document, newsList []string) []string {// '//a[@class="tt"]/@href'html.Find("a[class=tt]").Each(func(i int, selection *goquery.Selection) {url, _ := selection.Attr("href")newsList = append(newsList, url)})return newsList
}func getNews(url string, wg *sync.WaitGroup) {resp, err := http.Get(url)if err != nil {log.Println(err)wg.Done()return}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {log.Printf("Error: status code %d", resp.StatusCode)wg.Done()return}html, err := goquery.NewDocumentFromReader(resp.Body)news := News{}news.Url = urlnews.Media = "GameSky"html.Find("div[class=Mid2L_tit]>h1").Each(func(i int, selection *goquery.Selection) {news.Title = selection.Text()})if news.Title == "" {wg.Done()return}html.Find("div[class=Mid2L_con]>p").Each(func(i int, selection *goquery.Selection) {news.Content = news.Content + selection.Text()})var tmpTime stringhtml.Find("div[class=detail]").Each(func(i int, selection *goquery.Selection) {tmpTime = selection.Text()})reg := regexp.MustCompile(`\d+`)timeString := reg.FindAllString(tmpTime, -1)news.PubTime = fmt.Sprintf("%s-%s-%s %s:%s:%s", timeString[0], timeString[1], timeString[2], timeString[3], timeString[4], timeString[5])db := mysql.DBCon()stmt, err := db.Prepare("insert into gamesky (`title`, `url`, `media`, `content`, `pub_time`) values (?,?,?,?,?)")if err != nil {log.Println(err)wg.Done()}defer stmt.Close()rs, err := stmt.Exec(news.Title, news.Url, news.Media, news.Content, news.PubTime)if err != nil {log.Println(err)wg.Done()}if id, _ := rs.LastInsertId(); id > 0 {log.Println("插入成功")}wg.Done()
}复制代码

到此,本篇文章就结束了,如果在以上文章中有任何问题,都请各位赐教,非常感谢!!!

转载于:https://juejin.im/post/5cdd0da16fb9a0322564e4f9

Golang 并发爬虫 爬取某著名游戏媒体相关推荐

  1. 简单python爬虫爬取游戏wiki立绘

    简单python爬虫爬取游戏wiki立绘 玩二次元手游是感叹美少女立绘真好看啊,可惜就是抽不到,于是看到b站wiki上有角色立绘,就写了个爬虫准备将立绘趴下来欣赏(舔). 本人爬虫的技术只算是初学,代 ...

  2. Python爬虫入门(四):实战,爬取4399小游戏首页

    目录 robots.txt robots协议 robots.txt 语法 君子协定 何时需要robots协议? 查看4399.com的robots.txt 设定并分析目标 代码 urllib2& ...

  3. python酒店评论分析_手把手用Python网络爬虫带你爬取全国著名高校附近酒店评论...

    点击蓝色"Python空间"关注我丫 加个"星标",每天一起快乐的学习 今 日 鸡 汤 我站在鼓楼下边,一切繁华与我无关. /1 前言/ 简介:本文介绍如何用p ...

  4. mysql scrapy 重复数据_大数据python(scrapy)爬虫爬取招聘网站数据并存入mysql后分析...

    基于Scrapy的爬虫爬取腾讯招聘网站岗位数据视频(见本头条号视频) 根据TIOBE语言排行榜更新的最新程序语言使用排行榜显示,python位居第三,同比增加2.39%,为什么会越来越火,越来越受欢迎 ...

  5. 怎么把4399小游戏的代码_25行代码带你爬取4399小游戏数据,看下童年的游戏是否还在...

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 还记得童年的网页小游戏吗?今天带大家爬取4399小游戏网站的数据,游戏名字+链接地址 目标网 ...

  6. java爬虫拉勾网_[Java教程]node.js爬虫爬取拉勾网职位信息

    [Java教程]node.js爬虫爬取拉勾网职位信息 0 2017-03-14 00:00:21 简介 用node.js写了一个简单的小爬虫,用来爬取拉勾网上的招聘信息,共爬取了北京.上海.广州.深圳 ...

  7. 25行代码带你爬取4399小游戏数据,看下童年的游戏是否还在

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 还记得童年的网页小游戏吗?今天带大家爬取4399小游戏网站的数据,游戏名字+链接地址 目标网 ...

  8. JAVA爬取虎嗅网截图_java爬虫爬取网站使用多线程(虎嗅网站)

    java爬虫爬取网站使用多线程(虎嗅网站) java爬虫爬取网站使用多线程(虎嗅网站) 图解虎嗅爬虫优化方案 pom 如下: org.apache.httpcomponents httpclient ...

  9. python爬取4399小游戏数据_25行代码带你爬取4399小游戏数据,看下童年的游戏是否还在...

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 还记得童年的网页小游戏吗?今天带大家爬取4399小游戏网站的数据,游戏名字+链接地址 目标网 ...

最新文章

  1. 数据库中如何判断某参数为空就不执行where条件
  2. linux nexus bulid
  3. 打开新窗口的js代码
  4. 根据表名如何查找使用它的程序名、接口等
  5. Waymo 2020 | 2D/3D目标检测、跟踪和域自适应性冠军解决方案解析
  6. Windows内存管理 - 隐藏在new和malloc背后的heap
  7. 求带权中位数的Select算法
  8. [BuildRelease Management]ElectricCommander
  9. STM32使用LWIP库新建tcp_sever
  10. TeraTerm自动登录(Login)
  11. php enum 数字类型插入失败的解决办法
  12. basis问题专区(文档)
  13. Go基础系列:Go实现工作池的两种方式(一)
  14. 求助:ATI HD3200 LINUX驱动
  15. 最新摸头GIF在线生成工具源码+实测可用
  16. Android 开源项目和文章集合(更新:2022.03.21)
  17. 判断一颗二叉树是否为二叉平衡树 python 代码
  18. 使用 validation 验证参数
  19. CSS实现兼容浏览器的文字阴影效果
  20. 樱花动漫视频数据表分析樱花动漫

热门文章

  1. java窗口背景颜色的设定----setBackground()的用法
  2. 大二暑假实习生的第一次面试
  3. 获取百度地图商圈,小区边界
  4. C#-创建txt文本
  5. c语言中逗号运算符用法
  6. JS学习笔记之左边列表移到到右边列表
  7. sql学习周总结——赵俊杰
  8. 怎么彻底删除users下的文件夹_win7系统中c盘的user文件夹可以删除吗
  9. 使用drawBitmap绘制图片
  10. G - Millionaire Madness(bfs+优先队列)