python爬蟲 TapTap

做業要求來自於https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3075html

 

  • 對象 - TapTap

TapTap 是一個高品質手遊玩家社區,只提供原版和官服遊戲下載購買的平臺。開發者無需接入SDK,便可上傳遊戲,海內外開發者都有機會在這裏售賣正版安卓遊戲。TapTap 提供真實排行榜單和玩家評價,堅持編輯獨立評測推薦。在TapTap 社區,用戶與開發者直接交流,推進遊戲改進。mysql

 

  • 範圍 - 「Android遊戲榜」各大榜單的TOP150遊戲

taptap安卓遊戲榜有五個榜單,分別爲熱門榜(根據下載量)新品榜(根據近期發行遊戲下載量)預定榜(根據預定量)熱賣榜(根據遊戲售賣量)熱玩榜(根據玩家遊戲啓動量)。注:熱賣榜只有TOP35。ajax

 

  • 爬取限制 - ajax異步請求連接獲取數據

 在爬取過程當中,若是隻是根據html元素來爬取網站數據,只能爬到30條數據。因爲排行榜的數據是分頁的,在點擊「更多」的按鈕以後纔會顯示下一頁的數據,並且網址並無發生變化。經過觀察發現網站是經過ajax異步請求了一條連接獲取數據,爲了爬取整個榜單的數據信息,所以要分析該頁面的請求。sql

首先打開瀏覽器的開發者工具Network中的XHR(經過XMLHttpRequest方法發送的請求),點擊「更多」後發現一條新請求,進去就會發現這是查看更多數據的異步請求。數據庫

其次找到須要爬取的內容——爬取data裏的html全部內容。json

最後把json格式的響應內容用BeautifulSoup(html, 'html.parser')方法解析,經過對標籤的篩選得到須要的信息。瀏覽器

爲了防止在爬取過程當中ip被限制,這裏設置了合理的爬取間隔和使用user-agent模擬真實的瀏覽器提取內容(詳細見下面代碼)。user-agent能夠在開發者工具→Network→Headers裏面找到。app

 

  • 爬取內容 - 遊戲名、廠商、分類、標籤、評分、排名

 詳細代碼以下:dom

import pymysql
from sqlalchemy import create_engine
import pandas as pd
import requests
from bs4 import BeautifulSoup
import time
import random
from urllib.parse import urlencode
import jieba
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from os import path
from PIL import Image
import numpy as np
#爬取一條遊戲的信息
def agame(url):
    gamesDetail = {}
    res = requests.get(url)
    res.encoding = 'utf-8'
    soup = BeautifulSoup(res.text,'html.parser')
    gamesDetail['遊戲名'] = soup.select('h1')[0].text.rstrip(' CN')#截取掉遊戲名後面的空格和CN標籤
    gamesDetail['廠商'] = soup.select('.header-text-author')[0].select('span')[1].text
    if len(soup.select('.app-rating-score')) >0:#若是遊戲存在評分
        gamesDetail['評分'] = soup.select('.app-rating-score')[0].text
    else:#若是遊戲不存在評分
        gamesDetail['評分'] = soup.select('.app-rating-no-score')[0].text
    gamesDetail['分類'] = soup.select('li')[12].text.lstrip().rstrip()
    gamesDetail['標籤'] =' '.join(soup.select('#appTag')[0].text.lstrip().rstrip().split())#獲取標籤並轉爲字符串
    return gamesDetail
#將一頁遊戲編碼爲utf-8
def toalist(url):
    res = requests.get(url)
    res.encoding = 'utf-8'
    soup = my_get_soup(url)#模擬真實瀏覽器訪問
    return alist(soup)
#獲取一頁遊戲信息
def alist(soup):
    sleep()#設置合理的爬取間隔
    gamesList = []
    for games in soup.select('.taptap-top-card'):
        if len(games.select('div'))>0:#若是存在遊戲信息
            gamesUrl = games.select('a')[0]['href']#獲取每一個遊戲詳情頁面的網址
            gamesRank = games.select('span')[1].text#獲取遊戲排名
            gamesDict = agame(gamesUrl)
            gamesDict['排名'] = gamesRank
            gamesList.append(gamesDict)#把每一個遊戲的信息放進字典擴展到列表裏
    return gamesList
#設置合理的爬取間隔
def sleep():
    for i in range(5):
        time.sleep(random.random()*3)#沉睡隨機數的3倍秒數
#隨機選擇user-agent
def get_ua():
    au = random.choice(uas)
    return au
#模擬真實瀏覽器訪問
def my_get_soup(url):
    headers = {'user-agent':get_ua()}
    res = requests.get(url,headers = headers)
    res.encoding = 'utf-8'
    soup = BeautifulSoup(res.text,'html.parser')
    return soup
#獲取ajax異步請求的網址
def get_page(i,page):
    params = {
        'page':page+1,
        'total': 30 * page,
    }
    url = 'https://www.taptap.com/ajax/top/{}?'.format(i)+ urlencode(params)  #拼接URL
    try:
        r = requests.get(url)
        if r.status_code == 200:
            return r.json()  # 返回json格式的響應內容
    except:
        return None
#在異步請求裏找到須要的信息
def get_html(jsondata):
    if jsondata.get('data'):
        data = jsondata.get('data')
        yield {
            data.get('html'),
        }
#解析json返回的內容
def get_soup(i,page):
    jsondata = get_page(i, page)
    for item in get_html(jsondata):
        html = ''.join(item)
        soup = BeautifulSoup(html, 'html.parser')
        return soup
#保存每一個遊戲的標籤
def save_tags(wtxt,games):
    wclist = []
    for j in range(len(games)):
        wclist.append(games[j]['標籤'])
    for x in wclist:
        wtxt.write(x)
        wtxt.write('\n')
    wtxt.close()
#保存評分最高的前30的遊戲標籤
def save_score(wtxt,games):
    wclist = []
    sorted_x = sorted(games, key=lambda x : x['評分'], reverse=True)#遊戲以評分降序排列
    # 輸出詞頻最大TOP30
    for j in range(len(sorted_x[:30])):
        wclist.append(sorted_x[j]['標籤'])
    for x in wclist:
        wtxt.write(x)
        wtxt.write('\n')
    wtxt.close()
#生成詞雲
def wordCloud(txt):
    # 分詞
    wordsls = jieba.lcut(txt)
    wcdict = {}
    for word in wordsls:
        if word != ' ':
            wcdict[word] = wcdict.get(word, 0) + 1
    # 排序
    wcls = list(wcdict.items())
    wcls.sort(key=lambda x: x[1], reverse=True)
    # 去掉文件名,返回目錄
    d = path.dirname(__file__)
    # 打開蒙版圖片
    alice_mask = np.array(Image.open(path.join(d, "mask.jpg")))
    # 設置詞雲的一些屬性
    wc = WordCloud(background_color="white", max_words=2000, mask=alice_mask)
    # 生成詞雲
    wc.generate(txt)
    # 保存到本地
    wc.to_file(path.join(d, "image.png"))
    # 展現
    plt.imshow(wc, interpolation='bilinear')
    plt.axis("off")
    plt.show()

downloadGames = []#熱門榜
newGames = []#新品榜
reserveGames = []#預定榜
sellGames = []#熱賣榜
playedGames = []#熱門榜
rankList={"download","new","reserve","sell","played"}
#不一樣瀏覽器訪問
uas = ["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36",\
       "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134",\
       "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"]
#鏈接mysql的帳戶
conInfo = "mysql+pymysql://root:123456@localhost:3306/taptap?charset=utf8"
engine = create_engine(conInfo, encoding='utf-8')#初始化引擎
for i in rankList:
    rankUrl = 'https://www.taptap.com/top/{}'.format(i)#獲取不一樣榜單的連接
    if(i == 'download'):
        for page in range(5):
            if(page == 0):#第一頁,沒有異步請求
                downloadGames.extend(toalist(rankUrl))#把一頁的遊戲信息添加到列表裏
            if (page > 0):#二到五頁,有異步請求
                soup = get_soup(i,page)
                downloadGames.extend(alist(soup))
        twtxt = open('tagsDownload.txt', 'w', encoding='utf-8')#將每一個遊戲標籤寫到文本里
        save_tags(twtxt,downloadGames)#保存
        tagstxt = open('tagsDownload.txt', 'r', encoding='utf-8').read()#打開標籤文本
        wordCloud(tagstxt)#生成標籤詞雲
        swtxt = open('scoreDownload.txt', 'w', encoding='utf-8')#將每一個遊戲評分高的標籤寫到文本里
        save_score(swtxt,downloadGames)#保存
        scoretxt = open('scoreDownload.txt', 'r', encoding='utf-8').read()#打開評分文本
        wordCloud(scoretxt)#生成評分高的標籤詞雲
        gamesdf = pd.DataFrame(downloadGames)#造成表格
        gamesdf.to_sql(name='download', con=engine, if_exists='append', index=False)#存儲表
        conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='taptap', charset='utf8')#鏈接數據庫
    if(i == 'new'):
        for page in range(5):
            if (page == 0):
                newGames.extend(toalist(rankUrl))
            if (page > 0):
                soup = get_soup(i,page)
                newGames.extend(alist(soup))
        twtxt = open('tagsNew.txt', 'w', encoding='utf-8')
        save_tags(twtxt,newGames)
        tagstxt = open('tagsNew.txt', 'r', encoding='utf-8').read()
        wordCloud(tagstxt)
        swtxt = open('scoreNew.txt', 'w', encoding='utf-8')
        save_score(swtxt,newGames)
        scoretxt = open('scoreNew.txt', 'r', encoding='utf-8').read()
        wordCloud(scoretxt)
        gamesdf = pd.DataFrame(newGames)
        gamesdf.to_sql(name='new', con=engine, if_exists='append', index=False)
        conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='taptap', charset='utf8')
    if (i == 'reserve'):
        for page in range(5):
            if (page == 0):
                reserveGames.extend(toalist(rankUrl))
            if (page > 0):
                soup = get_soup(i,page)
                reserveGames.extend(alist(soup))
        twtxt = open('tagsReserve.txt', 'w', encoding='utf-8')
        save_tags(twtxt,reserveGames)
        tagstxt = open('tagsReserve.txt', 'r', encoding='utf-8').read()
        wordCloud(tagstxt)
        swtxt = open('scoreReserve.txt', 'w', encoding='utf-8')
        save_score(swtxt,reserveGames)
        scoretxt = open('scoreReserve.txt', 'r', encoding='utf-8').read()
        wordCloud(scoretxt)
        gamesdf = pd.DataFrame(reserveGames)
        gamesdf.to_sql(name='reserve', con=engine, if_exists='append', index=False)
        conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='taptap', charset='utf8')
    if (i == 'sell'):
        for page in range(5):
            if (page == 0):
                sellGames.extend(toalist(rankUrl))
            if (page > 0):
                soup = get_soup(i,page)
                sellGames.extend(alist(soup))
        twtxt = open('tagsSell.txt', 'w', encoding='utf-8')
        save_tags(twtxt,sellGames)
        tagstxt = open('tagsSell.txt', 'r', encoding='utf-8').read()
        wordCloud(tagstxt)
        swtxt = open('scoreSell.txt', 'w', encoding='utf-8')
        save_score(swtxt,sellGames)
        scoretxt = open('scoreSell.txt', 'r', encoding='utf-8').read()
        wordCloud(scoretxt)
        gamesdf = pd.DataFrame(sellGames)
        gamesdf.to_sql(name='sell', con=engine, if_exists='append', index=False)
        conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='taptap', charset='utf8')
    if (i == 'played'):
        for page in range(5):
            if (page == 0):
                playedGames.extend(toalist(rankUrl))
            if (page > 0):
                soup = get_soup(i,page)
                playedGames.extend(alist(soup))
        twtxt = open('tagsPlayed.txt', 'w', encoding='utf-8')
        save_tags(twtxt,playedGames)
        tagstxt = open('tagsPlayed.txt', 'r', encoding='utf-8').read()
        wordCloud(tagstxt)
        swtxt = open('scorePlayed.txt', 'w', encoding='utf-8')
        save_score(swtxt,playedGames)
        scoretxt = open('scorePlayed.txt', 'r', encoding='utf-8').read()
        wordCloud(scoretxt)
        gamesdf = pd.DataFrame(playedGames)
        gamesdf.to_sql(name='played', con=engine, if_exists='append', index=False)
        conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='123456', db='taptap', charset='utf8')
爬取taptap

 數據已存到數據庫,熱門榜以下圖所示:異步

新品榜以下圖所示:

 預定榜以下圖所示:

熱賣榜以下圖所示:

熱玩榜以下圖所示:

 

  • 數據分析及文本分析

 首先分別爬取了五個榜單TOP150的遊戲標籤,看看玩家都喜歡玩什麼類型的遊戲。詞雲生成結果以下圖所示。

其中發現玩家比較喜歡玩的類型集中於多人、聯機、中文、冒險和策略的遊戲(基於熱門榜和熱玩榜),而對新遊戲的期待類型則爲角色扮演和策略這方面(基於新品榜和預定榜)。因爲大部分的玩家爲免費玩家,那麼付費玩家對於遊戲更熱衷於單機、益智、解密和獨立遊戲(基於熱賣榜)。

因而可知,現今對於玩家來講可玩性較高的遊戲大多爲聯機的多人冒險遊戲,,但對於玩家所期待的角色扮演和策略遊戲在市場上受大衆歡迎的很少,另外付費玩家想在遊戲體現的更趨向於獨立完成和須要動腦的,所以各大遊戲廠商要想作出一個大賣、口碑又好的遊戲須要按期瞭解玩家的遊戲喜愛

 

接下來分析排行榜中高分遊戲TOP30的遊戲有哪些。因爲評分是根據數以萬計的玩家打的分數來的,高分遊戲的類型更能體現玩家真實的喜愛。詞雲生成結果以下圖所示。

其中高分遊戲排行與綜合榜單的遊戲類型有所不一樣,比較受歡迎的是單機、中文、獨立遊戲和UP主推薦的遊戲(基於熱門榜和熱玩榜),最受期待的是角色扮演和單機遊戲(基於新品榜和預定榜),但對於付費玩家來講遊戲類型並無過多的變化(熱賣榜只有TOP35因此有影響)。

能夠看出,口碑好的高分遊戲大多集中於單機和獨立遊戲,而多數付費遊戲基本上評分比免費遊戲要高。通常來講,單機和獨立遊戲更加註重玩家的體驗性,而多人聯機的遊戲更注重於商業化,要作出好口碑並不容易。所以,遊戲製做公司若是把大多心思放在遊戲劇本、提升玩家的遊戲體驗性和玩家與遊戲的融合度,將能獲得更好的口碑,也會有更多的玩家願意爲這個遊戲付費。

 

 以後來分析各大榜單上評分的數值分佈。能上榜的遊戲與宣傳力度也有很大關係,可是遊戲是否與宣傳所說的那麼好玩仍是須要參考一下評分。分析圖以下圖所示。

從中發現,預定榜和熱賣榜高分段的遊戲居多,熱門榜和熱玩榜分值偏中上,只有新品榜的遊戲低分接近通常。由此可知,遊戲在未面世時廠商的宣傳度會大大影響玩家的期待值,所以許多玩家對新遊戲抱有很大的期待,可當開始內測、公測的時候,遊戲可能並無廠商說的那麼好,致使大量玩家對該遊戲失望甚至「脫坑」,以後只有經歷過玩家的一番篩選,好的遊戲才慢慢脫穎而出,最終受到玩家的追捧並大賣。

 

最後看一下各大榜單評分TOP10的遊戲。結果以下圖所示。

 

爬蟲測試到此結束。

相關文章
相關標籤/搜索