Go语言写爬取糗百热门帖子

闲来无事,想着也用Go来写个爬虫之类的东西,我并不知道这算不算严格意义上的爬虫。

思前想后,觉得写个爬糗百热门的脚本吧,一来足够简单,二来大概熟悉下流程。

首先,选了goquery这个包来解析HTML,声称与jquery相似的用法,事实上也确实是这样,非常方便。

定个目标,只爬取列表页的帖子内容,作者和回帖都不管。

package main

import (
	"github.com/PuerkitoBio/goquery"
	"log"
)

//定义结构体
type Qb struct {
	Id int `json:"id"`
	Content string `json:"content"`
}

func main() {
	var url = "http://www.qiushibaike.com/hot"

	doc, err := goquery.NewDocument(url)
	if err != nil {
		log.Fatal(err)
	}

	var qb []Qb
	doc.Find("#content-left .article").Each(func(i int, s *goquery.Selection) {
		//s即为当前的 .article 元素,查找下级中的span元素的内容。
		content := s.Find(".content span").Text()
		qb = append(qb, Qb{Id: i, Content: content})
	})

	log.Println(qb)
}

”#content-left .article” 即每一条帖子作为元素的class。

将会输出:

[
	{0 结婚十三周年那天,老婆望着一大桌子菜不禁泪流满面。我帮她拭去泪水:瞧你,都激动的哭了!老婆却说:我激动个屁!想想这十三年跟着你受的罪,我实在忍不住啊!} 
	{1 前几天天冷,就给妹妹买了条围巾,然后她说谢谢哥,本人本着组织精神说你应该谢谢你嫂子,她惊讶的对我说:哥,你谈女朋友了。我说:没有,你应该感谢她一直到现在都没出现,哥才有钱给你买东西} 
	{2 跟哥们去理发,剪头的是个妹纸。。妹纸:“你有女朋友么?”哥们一听,突然兴奋的说:“没有!”妹纸:“我是个实习生,本来想给你换大工的,看你没有女朋友,我就随意剪了!”哥们你别看我,我就是一口水没忍住,喷你脸上了而已!} 
	{3 老妈比较胖,小时候每次打我我都是撒腿就跑,老妈没一次抓到我的。直到老妈学会骑自行车以后,那鞭子挥得………真像套马杆的汉子,威武雄壮……}
]

那么如何展示到页面中呢。

我选择了 gin 框架。

修改一下代码。

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("public/*")
	r.GET("/", Index)
	r.Run()
}

func Index(c *gin.Context) {
	var url = "http://www.qiushibaike.com/hot"

	doc, err := goquery.NewDocument(url)
	if err != nil {
		log.Fatal(err)
	}

	var result []Qb
	doc.Find("#content-left .article").Each(func(i int, s *goquery.Selection) {
		content := s.Find(".content span").Text()
		result = append(result, Qb{Id: i, Content: content})
	})

	c.HTML(http.StatusOK, "index.html", gin.H{
		"items": result,
		"title": "糗百热门"
	})
}

可以看到,

r := gin.Default()
r.LoadHTMLGlob("public/*")
r.GET("/", Index)

这里加载了public目录中的模板,然后下一行,表示,接收到 “/” 的请求时,调用Index方法去处理。

到这里,文档的抓取,解析,构造数据就已经完成,下一步,看一下怎么显示到页面中。

{% raw %}
<div class="col-md-12">
    <h2>{{ .title }}</h2>
    <table class="table table-striped table-bordered table-hover">
        {{ range $item := .items }}
        <tr>
            <td>{{ $item.Content }}</td>
        </tr>
        {{ end }}
    </table>
</div>
{% endraw %}

使用 “{% raw %}{{ }}{% endraw %}” 输出后端发送过来的数据。使用 range 迭代数据。与

for pos, char := range str {
...
}

一样。

完整的模板代码:

{% raw %}
<!-- public/index.html -->

<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>糗百</title>

        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">
        <link rel="stylesheet"  href="https://cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.min.css">

    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col-md-12">
                    <h2>{{ .title }}</h2>
                    <table class="table table-striped table-bordered table-hover">
                        {{ range $item := .items }}
                        <tr>
                            <td>{{ $item.Content }}</td>
                        </tr>
                        {{ end }}
                    </table>
                </div>
            </div>
        </div>
    </body>
</html>
{% endraw %}

这样,运行一下,就可以了。

gin框架默认使用8080端口,打开 http://localhost:8080 就可以看到一个极简版的糗百热门了。

问题来了,怎么增加一个分页呢?

完整代码见:

Github地址

后记

其实早就写完了这篇,但是hexo生成的时候由于 ”{% raw %}{{{% endraw %}“的问题,生成一直失败,一直拖到现在。

实际代码中需要去掉 “{ % raw % }” 相关部分。