標籤:代號 爬取 mes mt4 names https 成功 fun ada
前面的廢話
說到爬蟲,首先想到的當然是python~ 它在機器學習、爬蟲資料分析領域可謂是如日中天,十分熱門。但我最近在學習go語言,所以就用go寫了
TapTap社區
這是一個高品質的遊戲分享社區,可以說是手機上的steam。上面的使用者品質非常高,核心玩家多,看到他們那麼用心的寫那麼多長評論,讓我驚歎,所以這次打算拿它來爬取資料練練手,下面先看看成果
先看效果,這裡的玩家,都喜歡玩啥類型遊戲呀?
根據 下載榜 裡遊戲標籤的詞頻統計出:
發現單機、二次元、MOBA、策略等標籤比較突出
讓我們加入玩家評分的權重,評分是根據數以萬計的玩家打的分數來的,多個遊戲相同標籤會求平均值。
看看有什麼變化?
詞雲完全不一樣了呢,視覺錯位、腦洞、哲理等標籤的評分較高,這些才是玩家真實的喜好,為啥加入評分權重變化這麼大呢,讓我們看一下究竟是哪些遊戲評分這麼高!
原來是紀念碑穀、猿騎、艾希等遊戲。而紀念碑穀(tag:視覺錯位)的評分竟然達到了10分!!(7951條評價)
不過這款遊戲也確實讓我服氣,連我媽媽、老婆她們不太玩遊戲的,都很喜歡這款遊戲呢~
那麼下面就都把評分權重加進去,看看玩家心裡的真實需求
接著分析新品榜
遊戲名稱(根據排名權重+評分權重)
看看我們分析出來的跟榜單上的有什麼不一樣?
可以看到,加入評分權重後,像《我叫MT4》、《王牌戰爭:代號英雄》這種雖然排名靠前,但是口碑很差的遊戲,幾乎在我們的分析圖上就看不見啦。(所以在taptap上,就算你花錢刷榜上去了,也並沒有太多用,玩家的眼睛是雪亮的,哈哈哈)
預約榜
遊戲名稱(根據排名權重+評分權重)
這裡可以看出未來市場的玩家需求,《全職覺醒》、《堡壘之夜》等都是期待比較高的
熱玩榜
遊戲名稱(根據排名權重+評分權重)
《絕地求生、刺激戰場》也是突出遊戲之一,看來taptap的玩家,也是很喜歡吃雞的
實現方式
goquery解析html
iconv-go進行編碼轉換
sego用來中文分詞
wordart實現詞雲效果
現在先做了個簡單的版本,完整版是還想實現抓取某個遊戲的玩家評論,進行分詞,情感分析的。
先分析html結構,找到一個遊戲資訊裡包含哪些html元素,然後用goquery解析
使用Google瀏覽器,按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 mainimport ( "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 []GameInfovar 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使用者都喜歡些什麼遊戲