zoukankan      html  css  js  c++  java
  • go语言爬虫

    前面的废话

    说到爬虫,首先想到的当然是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
    ", 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
    ")
    
    	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("
    ")
    	}
    
    	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("
    ")
    	}
    	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
    ", gameInfo)
    }
    
    

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

    总结

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

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

  • 相关阅读:
    通达OA 新旧两种数据库连接方式
    c++ 如何获取系统时间
    性能测试开源小工具——http_load介绍
    http_load安装与测试参数分析
    不错的C++框架: Thrift(2)-传输和网络相关
    管理处理器的亲和性(affinity)
    300元内,此耳机是首选。不亏千人好评,对的起你的耳朵。
    [品质生活] 舒适 Schick HYDRO 5剃须刀
    巴氏刷牙法_百度百科
    Amazon.com : The Odyssey of the Manual Toothbrusher
  • 原文地址:https://www.cnblogs.com/lijiajia/p/9388356.html
Copyright © 2011-2022 走看看