前面的废话

说到爬虫,首先想到的当然是python~ 它在机器学习、爬虫数据分析领域可谓是如日中天,十分热门。但我最近在学习go语言,所以就用go写了

TapTap社区

这是一个高品质的游戏分享社区,可以说是手机上的steam。上面的用户质量非常高,核心玩家多,看到他们那么用心的写那么多长评论,让我惊叹,所以这次打算拿它来爬取数据练练手,下面先看看成果

先看效果,这里的玩家,都喜欢玩啥类型游戏呀?

根据 下载榜 里游戏标签的词频统计出:



发现单机、二次元、MOBA、策略等标签比较突出

让我们加入玩家评分的权重,评分是根据数以万计的玩家打的分数来的,多个游戏相同标签会求平均值。

看看有什么变化?

词云完全不一样了呢,视觉错位、脑洞、哲理等标签的评分较高,这些才是玩家真实的喜好,为啥加入评分权重变化这么大呢,让我们看一下究竟是哪些游戏评分这么高!

原来是纪念碑谷、猿骑、艾希等游戏。而纪念碑谷(tag:视觉错位)的评分竟然达到了10分!!(7951条评价)

不过这款游戏也确实让我服气,连我妈妈、老婆她们不太玩游戏的,都很喜欢这款游戏呢~



那么下面就都把评分权重加进去,看看玩家心里的真实需求

接着分析新品榜

游戏名称(根据排名权重+评分权重)

看看我们分析出来的跟榜单上的有什么不一样?

可以看到,加入评分权重后,像《我叫MT4》、《王牌战争:代号英雄》这种虽然排名靠前,但是口碑很差的游戏,几乎在我们的分析图上就看不见啦。(所以在taptap上,就算你花钱刷榜上去了,也并没有太多用,玩家的眼睛是雪亮的,哈哈哈)

预约榜



游戏名称(根据排名权重+评分权重)

这里可以看出未来市场的玩家需求,《全职觉醒》、《堡垒之夜》等都是期待比较高的

热玩榜

游戏名称(根据排名权重+评分权重)



《绝地求生、刺激战场》也是突出游戏之一,看来taptap的玩家,也是很喜欢吃鸡的

实现方式

goquery解析html

iconv-go进行编码转换

sego用来中文分词

wordart实现词云效果

现在先做了个简单的版本,完整版是还想实现抓取某个游戏的玩家评论,进行分词,情感分析的。

先分析html结构,找到一个游戏信息里包含哪些html元素,然后用goquery解析

使用谷歌浏览器,按F12可以很方便的找到元素哦

然后定义一个结构体,用来存放数据

type GameInfo struct {
Rank int //排名
TapTapID string //游戏ID
Name string //游戏名
Company string //公司名
Score float64 //游戏评分
IconUrl string //图标地址
Type string //游戏类型
tags []string //标签
}

分析单个游戏信息

//解析一个游戏信息
func ParseGameInfoCell(selection *goquery.Selection) {
gameInfo := GameInfo{}
nameA := selection.Find(".card-middle-title ")
gameInfo.TapTapID = nameA.AttrOr("href", "")
gameInfo.TapTapID = gameInfo.TapTapID[strings.LastIndex(gameInfo.TapTapID, "/")+1:]
gameInfo.Name = nameA.Find("h4").Text()
gameInfo.Company = selection.Find(".card-middle-author").Find("a").Text()
score, _ := strconv.ParseFloat(selection.Find(".middle-footer-rating").Find("span").Text(), 64)
gameInfo.Score = score
gameInfo.IconUrl = selection.Find(".card-left-image").Find("img").AttrOr("src", "")
tempRank, _ := strconv.ParseInt(selection.Find(".top-card-order-text").Text(), 10, 32)
gameInfo.Rank = int(tempRank) gameInfo.Type = selection.Find(".card-middle-footer").Find("a").Text() tagsAList := selection.Find(".card-tags").Find("a") tagsAList.Each(func(i int, selectionA *goquery.Selection) {
gameInfo.tags = append(gameInfo.tags, selectionA.Text())
}) GameInfoList = append(GameInfoList, gameInfo)
//fmt.Printf("%v\n", gameInfo)
}

但是很快就遇到了问题,因为排行榜的数据是分页的,我们请求一次只能得到30条数据,于是我们找到了“更多”按钮,发现里面通过ajax异步的请求了一条链接获取数据。

https://www.taptap.com/ajax/top/played?page=2&total=30

page就代表的页数,根据排行榜总数量150,每页30条可以得出一共有5页。这样我们就可以循环5次去请求所有的数据了

func ReqRankPage(page int) {
res, err := http.Get("https://www.taptap.com/ajax/top/" + rankTypeName + "?page=" + strconv.Itoa(page))
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)
} jsonBs, err := ioutil.ReadAll(res.Body)
tPageJson := TPageJson{}
err = json.Unmarshal(jsonBs, &tPageJson)
if err != nil {
fmt.Println("解析json错误", err)
} var htmlRead io.Reader = strings.NewReader(tPageJson.Data.Html)
doc, err := goquery.NewDocumentFromReader(htmlRead)
if err != nil {
log.Fatal(err)
} doc.Find(".taptap-top-card").Each(func(i int, selection *goquery.Selection) {
ParseGameInfoCell(selection)
})
}

全部代码

package main

import (
"bytes"
"encoding/json"
"fmt"
"github.com/PuerkitoBio/goquery"
"io"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"math"
) type TPageJson struct {
Success bool `json:"success"`
Data TPageDataJson `json:"data"`
} type TPageDataJson struct {
Html string `json:"html"`
Next string `json:"next"`
} type GameInfo struct {
Rank int //排名
TapTapID string //游戏ID
Name string //游戏名
Company string //公司名
Score float64 //游戏评分
IconUrl string //图标地址
Type string //游戏类型
tags []string //标签
} var GameInfoList []GameInfo
var rankTypeName = "reserve" var rankTypes = []string{"download", "new", "reserve", "sell", "played"} func main() { for _, typeName := range rankTypes {
GameInfoList = []GameInfo{} rankTypeName = typeName
//每个排行榜有5页数据(根据总数150条,每页30条得出)
for i := 1; i <= 5; i++ {
ReqRankPage(i)
}
//生成标签词典
GenerateTags()
GenerateGameNames()
fmt.Println("生成排行榜:", rankTypeName, "完毕")
}
} func GenerateGameNames() {
var tagsBuffer bytes.Buffer
tagsBuffer.WriteString("word;weight\n") for _, gameInfo := range GameInfoList {
//weightSize := 150 - gameInfo.Rank //把排名的权值加上
//weightSize := int(math.Ceil(float64(150-gameInfo.Rank) * gameInfo.Score)) //把排名的权值加上
weightSize := int(math.Ceil(gameInfo.Score*100)) //把排名的权值加上 tagsBuffer.WriteString(gameInfo.Name)
tagsBuffer.WriteString(";")
tagsBuffer.WriteString(strconv.Itoa(weightSize))
tagsBuffer.WriteString("\n")
} WriteFile(rankTypeName+"_names_score.csv", tagsBuffer.String())
} func GenerateTags() {
tagsCountDic := make(map[string]int)
tagsScoreDic := make(map[string]float64) var tagsBuffer bytes.Buffer
tagsBuffer.WriteString("word;weight;") for _, gameInfo := range GameInfoList {
for _, tag := range gameInfo.tags {
tagsCountDic[tag]++
tagsScoreDic[tag] += gameInfo.Score*100
}
} for key, value := range tagsCountDic {
tagsBuffer.WriteString(key)
tagsBuffer.WriteString(";")
//tagsBuffer.WriteString(strconv.Itoa( value))
tagsBuffer.WriteString(strconv.Itoa( int(tagsScoreDic[key]/float64(value))))
tagsBuffer.WriteString("\n")
}
WriteFile(rankTypeName+"_tags_score.csv", tagsBuffer.String())
} func WriteFile(name, content string) {
data := []byte(content)
if ioutil.WriteFile(name, data, 0644) == nil {
fmt.Println("写入文件成功:", name)
}
} func ReqRankPage(page int) {
res, err := http.Get("https://www.taptap.com/ajax/top/" + rankTypeName + "?page=" + strconv.Itoa(page))
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)
} jsonBs, err := ioutil.ReadAll(res.Body)
tPageJson := TPageJson{}
err = json.Unmarshal(jsonBs, &tPageJson)
if err != nil {
fmt.Println("解析json错误", err)
} var htmlRead io.Reader = strings.NewReader(tPageJson.Data.Html)
doc, err := goquery.NewDocumentFromReader(htmlRead)
if err != nil {
log.Fatal(err)
} doc.Find(".taptap-top-card").Each(func(i int, selection *goquery.Selection) {
ParseGameInfoCell(selection)
})
} //解析一个游戏信息
func ParseGameInfoCell(selection *goquery.Selection) {
gameInfo := GameInfo{}
nameA := selection.Find(".card-middle-title ")
gameInfo.TapTapID = nameA.AttrOr("href", "")
gameInfo.TapTapID = gameInfo.TapTapID[strings.LastIndex(gameInfo.TapTapID, "/")+1:]
gameInfo.Name = nameA.Find("h4").Text()
gameInfo.Company = selection.Find(".card-middle-author").Find("a").Text()
score, _ := strconv.ParseFloat(selection.Find(".middle-footer-rating").Find("span").Text(), 64)
gameInfo.Score = score
gameInfo.IconUrl = selection.Find(".card-left-image").Find("img").AttrOr("src", "")
tempRank, _ := strconv.ParseInt(selection.Find(".top-card-order-text").Text(), 10, 32)
gameInfo.Rank = int(tempRank) gameInfo.Type = selection.Find(".card-middle-footer").Find("a").Text() tagsAList := selection.Find(".card-tags").Find("a") tagsAList.Each(func(i int, selectionA *goquery.Selection) {
gameInfo.tags = append(gameInfo.tags, selectionA.Text())
}) GameInfoList = append(GameInfoList, gameInfo)
//fmt.Printf("%v\n", gameInfo)
}

这样就可以把爬取下来的数据,写成文件,生成出一张张的词云进行分析啦

总结

第一次玩爬虫,所以写的不是很好,爬虫还有很多技术,本文里都没有涉及。如防止反爬,账号登陆等。写这个也是想多写一点go代码,以后可能会把go作为我的主语言进行开发

接下来研究下爬取网易云音乐~ 嘿嘿嘿

go语言爬虫 - TapTap用户都喜欢些什么游戏的更多相关文章

  1. CSDN数据库被爆 统计CSDN用户都喜欢哪些密码

    今天有黑客在网上公开了知名网站CSDN的用户数据库,这是一次严重的暴库泄密事件,涉及到的账户总量高达600万个.有人写了一个小程序,统计了这次公布的 6428632 个 CSDN 哪些密码出镜率较高? ...

  2. python爬虫 TapTap

    作业要求来自于https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3075 对象 - TapTap TapTap 是一个高品质手游玩家社区, ...

  3. [No0000197]Windows用户都应该知道的运行命令

    通过"运行"命令,运行Windows丰富工具的方法.如果您知道工具或任务的相应"运行"命令,那么您就知道访问所述工具或任务的最快方法. 以下是我们最喜欢的Run ...

  4. go语言每个工程是不是都要单独设置GOPATH?

      go语言每个工程是不是都要单独设置GOPATH?比如我的go项目都统一管理在d:/workspace/go_work/目录下面,该目录下有3个项目project_01,project_02,pro ...

  5. R语言爬虫初尝试-基于RVEST包学习

    注意:这文章是2月份写的,拉勾网早改版了,代码已经失效了,大家意思意思就好,主要看代码的使用方法吧.. 最近一直在用且有维护的另一个爬虫是KINDLE 特价书爬虫,blog地址见此: http://w ...

  6. 为什么二流程序员都喜欢黑php?

    为什么二流程序员都喜欢黑php? 为什么程序员都喜欢黑php?这个嘛!你骂一句php是垃圾试试,保准php程序员不揍扁你!这就好像自己的母校,纵然有很多不好的地方,但是只允许自己调侃,不允许外人骂半句 ...

  7. 在中国使用苹果Mac电脑的都是些什么人?

    来源:PConline 资讯 文章收录于:风云社区(提供上千款各类Mac软件下载)   (图片来源于互联网分享,如涉及版权问题请联系作者删除) 在中国用Mac的都是什么人?腾讯CDC用户研究中心对 Q ...

  8. 简单R语言爬虫

    R爬虫实验 R爬虫实验 PeRl 简单的R语言爬虫实验,因为比较懒,在处理javascript翻页上用了取巧的办法. 主要用到的网页相关的R包是: {rvest}. 其余的R包都是常用包. libra ...

  9. cookie、session和application都是些什么神?——图文加案例,不怕你不会,就怕你不看

    cookie.session和application都是些什么神? 前言: 一直想写一篇关于cookie和session的博客,由于种种原因,一直没有整理,这不,今天还就遇到问题了,之前虽然会,但是好 ...

随机推荐

  1. 浏览器调用接口发现Provisional headers are shown

    一次请求时候报错 无论如何也找不到错误,后台接口和前端请求都是正确的.后来发现是 自己浏览器上装了广告拦截的插件 把我这个请求给拦截 果断卸载插件立马就好了.

  2. 解决ping 127.0.0.1 一般故障 问题

    ​故障如下图: 绕了好一大圈才发现是goupi防火墙搞的鬼,弄得我一些软件一直运行不了!!!!! 废话不多说,关了防火墙就行了:操作步骤如下图示 ​ 关闭之后,美滋滋: ​ ​

  3. Dubbo服务容错(整合hystrix)

    简介:Hystrix旨在通过控制那些访问远程系统.服务和第三方库的节点从而对延迟和故障提供更强大的容错能力,Hystrix具备拥有回退机制和断路器功能的线程和信号隔离.请求缓存和请求打包以及监控和配置 ...

  4. 使用Idea初始化SpringMvc项目

    (1) (2) (3) (4) (5)感谢http://www.cnblogs.com/feiyujun/p/6537510.html (6)

  5. js 鼠标拖拽元素

    基础知识 event.clientX.event.clientY 鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条.IE事件和标准事件都定义了这2个属性 event. ...

  6. 【CTF WEB】ISCC 2016 web 2题记录

      偶然看到的比赛,我等渣渣跟风做两题,剩下的题目工作太忙没有时间继续做. 第1题 sql注入: 题目知识 考察sql注入知识,题目地址:http://101.200.145.44/web1//ind ...

  7. manjaro 的配置

    一.更新源的配置: 1).自动方法: 在 终端 执行下面的命令从官方的源列表中对中国源进行测速和设置 sudo pacman-mirrors -c China 2).手动方法 自动方法(上面的方法1, ...

  8. /etc/fstab文件详解【转】

    ******************************************************************************* 有很多人经常修改/etc/fstab文件 ...

  9. spring mvc 返回类型

    spring mvc处理方法支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void 小结:1.使用 String 作为请求处理方 ...

  10. 多个SpingBoot项目的搭建与部署

    最近几年SpringBoot大热,很多IT公司都开始采用SpringBoot来替换传统的SpringMVC项目.那么如何搭建一个适合开发的项目架构呢? 这里我主要介绍Java程序员使用最多的两种工具进 ...