项目起因

其实我从很久之前看了B站相关的数据可视化视频之后就很想尝试一下,但是一直没有动力。正好最近在推vtuber,想着为圈子做些贡献,并且WHUer无学可上,闲着也是闲着。我就自学了一些网络爬虫和数据可视化,虽然不多但是勉强够用,于是就开始了这个项目。

项目设计

简单的来说这个项目可以分成三个部分:

1.根据uid访问用户关注页并获得其关注者的信息(uid)

2.根据获得的up主uid逐一访问他们的粉丝页并且找到粉丝数和播放量的api

3.整理数据并制作图表

下面我来分别介绍项目的三个部分。

项目实现

第一部分

之前我们已经实现了根据uid获得用户简介和名称。其实原理完全一样,只不过换了网页。但是不同之处在于这里我们要用selenium来模拟浏览器,不然无法看到相关信息。

from selenium import webdriver
from bs4 import BeautifulSoup
import re

user=input('请输入你的uid号:')
uid_href=''
href=[]
driver=webdriver.Firefox()
driver.get('https://space.bilibili.com/'+user+'/fans/follow')
html=driver.page_source
soup=BeautifulSoup(html,'html.parser')
tags=soup.find_all(attrs={'href':re.compile(r"//space.bilibili.com/(\d+)/")})
for tag in tags:
    if 'href' in tag.attrs:
        href+=tag.attrs['href']
    uid_href=''.join(href)
pattern=re.compile(r'\d+')
uids=pattern.findall(uid_href)
uids=list(set(uids))

第二部分

这一步我们要获得up主的粉丝数和播放数。如果尝试过你就会发现:这两个数据不是静态的存在于当前页面的源代码中的,而是有一个api将数据传给当前页面的。我们经过观察可以发现API的url分别为:

https://api.bilibili.com/x/relation/stat?vmid=’+uid (粉丝)

https://api.bilibili.com/x/space/upstat?mid='+uid (播放)

接下来我们就获得里面的数据并存为列表就可以了。

#获得粉丝数
followers=[]
for i in range(0,len(uids)):
    driver.get('https://api.bilibili.com/x/relation/stat?vmid='+uids[i])
    html=driver.page_source
    soup=BeautifulSoup(html,'html.parser')
    data=soup.find('div',id='json')
    data=data.string
    data=data.split('"follower":')
    follower=data[-1].split('}}')
    followers+=follower
    followers= [i for i in followers if i != '']
#获得播放量
views=''  
for i in range(0,len(uids)):
    driver.get('https://api.bilibili.com/x/space/upstat?mid='+uids[i])
    html=driver.page_source
    soup=BeautifulSoup(html,'html.parser')
    data=soup.find('div',id='json')
    data=data.string
    data=data.split('{"code":0,"message":"0","ttl":1,"data":{"archive":{"view":')
    data=data[1]
    data=data.split('},"article"')
    view=data[0]+','
    views+=view

views=views.split(',')

当然为了制作包含名字而不是uid的图表,我们还需要获得up主的名称,这里可以直接用到上一篇文章的代码,这里就不再展示。

第三部分

最后我们制作图表就可以了,这里我们使用的是echarts的python版本pyecharts,这个工具可以生成动态图标而且有多个主题可以直接使用,很方便。

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.globals import ThemeType

c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
    .add_xaxis(name)
    .add_yaxis("粉丝数量", followers)
    .add_yaxis("播放量", views)
    .set_global_opts(
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=60)),
        title_opts=opts.TitleOpts(title="hololive", subtitle="粉丝数和播放量"),
    )
    .render("hololive.html")
)

至此,项目全部完成,效果如下:

hololive20位vtuber的粉丝数和播放数图表

结语

其实我开始项目的五天前还完全不会任何爬虫,也不会数据可视化。学习并做到这些东西我拢共用了可能不到20个小时。这当然不是我有天分什么的。ted有一个演讲叫’How To Learn Anything You Want In Just 20 Hours‘,当然前提是1. Deconstruct the skill 2. Learn Enough to Self-Correct 3. Remove Practice Barriers 4. Practice at least 20 hours。

我觉得编程十分符合这些:1.编程的代码实现调用了许多第三方库,这些第三方库实际帮助你知道了代码的功能区块,帮助你分解了任务。2.代码的实现与否清晰可见。3.我用手机看视频,用电脑写代码,根本没有其他电子设备让我分神了…可能这就是我这段时间执行力比较高的原因吧。

代码展示(以爬取hololive为例)

from selenium import webdriver
import requests
import selenium
from bs4 import BeautifulSoup
import re
from selenium.webdriver.chrome.options import Options
import csv
import xlwt


def getuid(uid):
    url='https://space.bilibili.com/'+str(uid)
    r=requests.get(url,timeout=30)
    return r.text


def cutuid(html):
    soup=BeautifulSoup(html,'html.parser')
    ls=soup.find_all('meta',content=re.compile('bilibili'))
    #获得简介框(因为简介tag包含'bilibili')
    content=str(ls)
    small_content=re.split(',bilibili是国内知名的视频弹幕网站,这里有最及时的动漫新番,最棒的ACG氛围,最有创意的Up主。大家可以在这里找到许多欢乐。" name="description">\n</meta>',content)
    a=small_content[0]
    b=re.split('meta content="',a)
    c=b[1]
    final_content=re.split(',',c,1)
    name=final_content[0]
    return name


#获得hololive众人的uid,286700005是holo哥的uid
uid_href=''
href=[]
driver=webdriver.Firefox()
driver.get('https://space.bilibili.com/'+user+'/fans/follow')
html=driver.page_source
soup=BeautifulSoup(html,'html.parser')
tags=soup.find_all(attrs={'href':re.compile(r"//space.bilibili.com/(\d+)/")})
for tag in tags:
    if 'href' in tag.attrs:
        href+=tag.attrs['href']
    uid_href=''.join(href)
pattern=re.compile(r'\d+')
uids=pattern.findall(uid_href)
uids=list(set(uids))

#获得名称
name=[]
for i in range(0,len(uids)):
    html=getuid(uids[i])
    name.append(cutuid(html))
#获得粉丝数
followers=[]
for i in range(0,len(uids)):
    driver.get('https://api.bilibili.com/x/relation/stat?vmid='+uids[i])
    html=driver.page_source
    soup=BeautifulSoup(html,'html.parser')
    data=soup.find('div',id='json')
    data=data.string
    data=data.split('"follower":')
    follower=data[-1].split('}}')
    followers+=follower
    followers= [i for i in followers if i != '']
#获得播放量
views=''  
for i in range(0,len(uids)):
    driver.get('https://api.bilibili.com/x/space/upstat?mid='+uids[i])
    html=driver.page_source
    soup=BeautifulSoup(html,'html.parser')
    data=soup.find('div',id='json')
    data=data.string
    data=data.split('{"code":0,"message":"0","ttl":1,"data":{"archive":{"view":')
    data=data[1]
    data=data.split('},"article"')
    view=data[0]+','
    views+=view

views=views.split(',')
print(uids)
print(name)
print(followers)
print(views)

#作图
from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.globals import ThemeType

c = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
    .add_xaxis(name)
    .add_yaxis("粉丝数量", followers)
    .add_yaxis("播放量", views)
    .set_global_opts(
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=60)),
        title_opts=opts.TitleOpts(title="hololive", subtitle="粉丝数和播放量"),
    )
    .render("hololive.html")
)