前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二次元属性被稀释,B站还剩什么?| 数据获取

二次元属性被稀释,B站还剩什么?| 数据获取

作者头像
木东居士
发布2020-03-06 15:02:30
9200
发布2020-03-06 15:02:30
举报

本文为HeoiJin原创投稿文章,欢迎更多读者投稿!

作者简介

HeoiJin:立志透过数据看清世界的产品策划、数据分析、产品策划领域。 万物皆营销 | 资本永不眠 | 数据恒真理 CSDN:https://me.csdn.net/weixin_40679090

由于篇幅过大原因,文章将分为上篇与下篇:上篇为数据获取,下篇为数据分析。今天为大家带来的是上篇:获取B站数据!

本篇目录

一、项目背景

最近看了一篇对B站2019年数据解读的文章,文章最后得出结论:B站的二次元属性已被稀释,逐渐走向大众。

那么走过2020年的春节后,二次元属性的稀释情况如何?什么分区是B站的龙头?b站的主流用户喜欢什么标签的视频?各分区的情况能带来什么社会价值?本项目将通过数据带你一起窥探B站的变化。

项目特色:

  1. 利用Scrapy框架进行网页获取
  2. 利用pandas、numpy进行数据分析
  3. 利用pyecharts进行数据可视化
  4. 利用scipy进行相关性分析

二、工具和环境

  • 语言:python 3.7
  • IDE:Pycharm
  • 浏览器:Chrome
  • 框架:Scrapy 1.8.0

三、需求分析

B站是我们熟悉的弹幕视频分享网站,根据百度百科的资料显示,B站的主要业务包括直播、游戏、广告、电商、漫画、电竞。

在这么多项的业务当中,我们不难看到一个共同点,B站的主要盈利模式是高度依赖用户,其次是主播和UP主。

因此要分析B站的变化,就要从用户喜爱变化情况切入分析,本次项目将采集以下数据:

  1. 排行榜的分区名
  2. 排行页:视频的标题、作者、综合评分、排名、视频链接
  3. 详情页:视频的播放量、三连量、评论量、弹幕量、转发量、热门标签

四、页面分析

4.1 排行榜页解析

首先从排行榜页面进行过分析。禁用Javascript后,发现要提取的信息都是在静态网页当中,那么在编写代码的时候通过xpath定位抓取信息即可。

完成单个分区排行榜页面的分析后,只需找到各排行榜对应的url即可实现爬取多个分区。通过检查网页源码,发现每一个分区都只有文字描述,并没有相关的url,因此通过分析url变化再自行构造请求的url。

url规则即对应编号:https://www.bilibili.com/ranking/all/编号/0/30

以下是各分类对应的编号:

我们创建一个编号的列表,并用循环拼接url即可完成url的批量生成

代码语言:javascript
复制
from pprint import pprint
labels_num=[0,1,168,3,129,4,36,188,160,119,155,5,181]

url_list=[f'https://www.bilibili.com/ranking/all/{i}/0/30' for i in labels_num]
#利用pprint方法,我们能够在输出时实现一个url一行
pprint(url_list)

4.2 详细信息页面api解析

我们还需要获取视频的播放量、三连量、评论量、弹幕量、转发量、热门标签,但在排行榜页中并没有体现,因此要进一步请求视频的详情页。

进入视频详情页,同样禁用Javascript后,可以发现要找的信息都是ajax异步加载的,在这里考虑抓取api文件来获取信息,这样能够大大提升解析网页的效率,也不容易被封ip

通过一轮的分析之后,找到了视频的播放量、三连量、评论量、弹幕量、转发量数据在stat?aid=文件当中,url末端的数字即视频的id,后续对视频链接进行切片获取id再拼接Request URL即可。

访问该Request URL,是标准的json数据。

对数据页面进行json解析,后续只需获得键:[‘data ‘]下的数据即可

到这里还缺少了热门标签的数据,继续抓包找到另外一个api的url,同样需要通过视频的id进行url构造。

但直接访问这个url,会显示要找的页面不存在。观察url发现,?后包含了很多参数,尝试仅保留关键的视频id参数再次访问后,能够获取需要的信息。也是非常工整的json数据。

解析json后,只需要获取键[“data”]下的所有[‘tag_name ‘]即可。

至此所有需要的url及相关定位信息都基本找到,下面就可以开始编写文件了。

五、分析

5.1 Scrapy框架概述

Scrapy是一个为了获取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy架构图(绿色箭头为数据流向)

本次项目涉及的组件介绍

Scrapy Engine 引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

调度器(Scheduler) 调度器从引擎接收request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader) 下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。每个spider负责处理一个特定(或一些)网站。

Item Pipeline Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)

5.2 为什么用Scrapy框架

Scrapy 使用了异步网络框架来处理网络通讯。相比于普通的Request或者多线程,Scrapy在获取页面上具有更高的效率(详细的效率比较可以看这篇《Python x虫的N种姿势》:https://www.cnblogs.com/jclian91/p/9799697.html)

同时完善的框架意味着只需要定制开发其中的模块就能轻松实现,有清晰的逻辑路径。

六、编写

如果之前还没有安装Scrapy,可在cmd中使用pip语句进行安装

代码语言:javascript
复制
pip3 install Scrapy

6.1 新建项目

去到要新建文件的文件夹中,在地址栏输入cmd,进入cmd模式。

代码语言:javascript
复制
scrapy startproject blbl
cd blbl
scrapy genspider bl "bilibili.com"

命令解读:

  • scrapy startproject blbl:创建项目,项目文件命名为blbl
  • sd blbl :进入项目文件
  • scrapy genspider bl “bilibili.com” :创建文件,名为bl(注意名应有别于项目文件名,且在该项目中是唯一的),限定获取的url范围”bilibili.com”

到这里我们创建就完成了,目录结构如下:

简单介绍下本项目所需文件的作用:

  • scrapy.cfg :项目的配置文件
  • blbl/blbl :项目的Python模块,将会从这里引用代码
  • items.py :项目的目标文件
  • pipelines.py :项目的管道文件
  • settings.py :项目的设置文件
  • spiders/ :存储获取代码目录
  • bl.py :我们通过命令新建的文件

6.2 创建并编写start.py

通常启动Scrapy都是在shell或者cmd命令中进行。为了方便启动或者进行debug测试,创建一个start.py用来控制启动

目标:

  • 在py文件中执行cmd命令
代码语言:javascript
复制
from scrapy import cmdline

cmdline.execute('scrapy crawl bl'.split())

创建完成后,我们每次运行或者debug测试时,只需要执行这个文件即可。

6.3 编写settings.py

目标:

  • 关闭遵循君子协议
  • 设置延迟(好的程序不应对别人服务器造成过大压力)
  • 构造请求头
  • 打开Pipeline(用于储存数据,取消注释即可)
代码语言:javascript
复制
ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 1

DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36'
}

ITEM_PIPELINES = {
   'bilibili.pipelines.BlblPipeline': 300, 
}

6.4 编写bl.py

bl.py是我们通过cmd的命令符创建的文件,主要用于解析网站内容,并将解析后的数据传给items pipeline。

目标:

  • 获得排名、视频标题、作者、得分
  • 获得视频id,构造api链接
  • 向api链接发送请求
  • 获得三连、弹幕、评论和热门标签等数据
代码语言:javascript
复制
import scrapy
from blbl.items import BlblItem
import json 

class BlSpider(scrapy.Spider):
    name = 'bl'
    allowed_domains = ['bilibili.com']
    #start_urls默认为'http://'+allowed_domains[0]
    #所以这里我们要重写start_urls,把排行榜页面的url列表赋值给start_urls
    start_urls = [
        'https://www.bilibili.com/ranking/all/0/0/30',
        'https://www.bilibili.com/ranking/all/1/0/30',
        'https://www.bilibili.com/ranking/all/168/0/30',
        'https://www.bilibili.com/ranking/all/3/0/30',
        'https://www.bilibili.com/ranking/all/129/0/30',
        'https://www.bilibili.com/ranking/all/4/0/30',
        'https://www.bilibili.com/ranking/all/36/0/30',
        'https://www.bilibili.com/ranking/all/188/0/30',
        'https://www.bilibili.com/ranking/all/160/0/30',
        'https://www.bilibili.com/ranking/all/119/0/30',
        'https://www.bilibili.com/ranking/all/155/0/30',
        'https://www.bilibili.com/ranking/all/5/0/30',
        'https://www.bilibili.com/ranking/all/181/0/30'
                  ]

    def parse(self, response):
        #获取当前爬取的榜单
        rank_tab=response.xpath('//ul[@class="rank-tab"]/li[@class="active"]/text()').getall()[0]
        print('='*50,'当前爬取榜单为:',rank_tab,'='*50)

        #视频的信息都放在li标签中,这里先获取所有的li标签
        #之后遍历rank_lists获取每个视频的信息
        rank_lists=response.xpath('//ul[@class="rank-list"]/li')
        for rank_list in rank_lists:
            rank_num=rank_list.xpath('div[@class="num"]/text()').get()
            title=rank_list.xpath('div/div[@class="info"]/a/text()').get()
            # 抓取视频的url,切片后获得视频的id
            id=rank_list.xpath('div/div[@class="info"]/a/@href').get().split('/av')[-1]
            # 拼接详情页api的url
            Detail_link=f'https://api.bilibili.com/x/web-interface/archive/stat?aid={id}'
            Labels_link=f'https://api.bilibili.com/x/tag/archive/tags?aid={id}'
            author=rank_list.xpath('div/div[@class="info"]/div[@class="detail"]/a/span/text()').get()
            score=rank_list.xpath('div/div[@class="info"]/div[@class="pts"]/div/text()').get()
            #如用requests库发送请求,要再写多一次请求头
            # 因此我们继续使用Scrapy向api发送请求
            # 这里创建一个字典去储存我们已经抓到的数据
            # 这样能保证我们的详细数据和排行数据能一 一对应无需进一步合并
            # 如果这里直接给到Scrapy的Item的话,最后排行页的数据会有缺失
            items={
                'rank_tab':rank_tab,
                'rank_num' : rank_num ,
                'title' :title ,
                'id' : id ,
                'author' : author ,
                'score' : score ,
                'Detail_link':Detail_link
            }
            # 将api发送给调度器进行详情页的请求,通过meta传递排行页数据
            yield scrapy.Request(url=Labels_link,callback=self.Get_labels,meta={'item':items},dont_filter=True)

    def Get_labels(self,response):
        #获取热门标签数据
        items=response.meta['item']
        Detail_link=items['Detail_link']
        # 解析json数据
        html=json.loads(response.body)
        Tags=html['data'] #视频标签数据
        #利用逗号分割列表,返回字符串
        tag_name=','.join([i['tag_name'] for i in Tags])
        items['tag_name']=tag_name
        yield scrapy.Request(url=Detail_link,callback=self.Get_detail,meta={'item':items},dont_filter=True)

    def Get_detail(self,response):
        # 获取排行页数据
        items=response.meta['item']
        rank_tab=items['rank_tab']
        rank_num=items['rank_num']
        title=items['title']
        id=items['id']
        author=items['author']
        score=items['score']
        tag_name=items['tag_name']

        # 解析json数据
        html=json.loads(response.body)

        # 获取详细播放信息
        stat=html['data']

        view=stat['view']
        danmaku =stat['danmaku']
        reply =stat['reply']
        favorite =stat['favorite']
        coin =stat['coin']
        share =stat['share']
        like =stat['like']

        # 把所有爬取的信息传递给Item
        item=BlblItem(
            rank_tab=rank_tab,
            rank_num = rank_num ,
            title = title ,
            id = id ,
            author = author ,
            score = score ,
            view = view ,
            danmaku = danmaku ,
            reply = reply ,
            favorite = favorite ,
            coin = coin ,
            share = share ,
            like = like ,
            tag_name = tag_name
        )
        yield item

6.5 编写Items.py

将爬取到的数据名称按照Scrapy的模板填写好即可

目标:

  • 收集爬取到数据
代码语言:javascript
复制
import scrapyclass BlblItem(scrapy.Item):    rank_tab=scrapy.Field()    rank_num =scrapy.Field()    id=scrapy.Field()    title =scrapy.Field()    author =scrapy.Field()    score =scrapy.Field()    view=scrapy.Field()    danmaku=scrapy.Field()    reply=scrapy.Field()    favorite=scrapy.Field()    coin=scrapy.Field()    share=scrapy.Field()    like=scrapy.Field()    tag_name=scrapy.Field() 

6.6 编写pipeline.py

运用scrapy原生的CsvItemExporter能够让我们从编写表头以及写writerow语句中解放出来,比传统写入csv的方法更简便。

目标:

  • 利用CsvItemExporter把数据写入csv文件
代码语言:javascript
复制
from scrapy.exporters import CsvItemExporter

class BlblPipeline(object):
    def __init__(self):
        # a为追加写入模式,这里要用二进制的方式打开
        self.fp=open('bilibili.csv','ab')
        #include_headers_line默认为True
        # 能够帮我们自动写入表头,并且在追加写入数据的时候不会造成表头重复
        self.exportre=CsvItemExporter(
            self.fp,
            include_headers_line=True,
            encoding='utf-8-sig'
        )
    def open_spider(self,spider):
        pass

    # 向csv文件中写入数据
    def process_item(self,item,spider):
        self.exportre.export_item(item)
        return item

    def close_spider(self,spider):
        self.fp.close()

最后打开bilibili.csv,可以看到数据都完整爬取下来了!

七、本篇小结

最后回顾下本次的重点内容:

  • 对ajax异步加载的网页进行抓包,通过抓取Request URL访问异步加载数据
  • 使用Scrapy框架进行数据采集
  • 利用scrapy.Request向api发送请求并通过meta传递已获取的排行页数据
  • 利用Scrapy内置的CsvItemExporter将数据存储到csv中

下周二将推出本篇文章的下部分:数据分析实战环节,敬请期待吧~

源码地址(或阅读原文):https://github.com/heoijin/Bilibili-Rnak

郑重声明:本项目及所有相关文章,仅用于经验技术交流,禁止将相关技术应用到不正当途径,因为滥用技术产生的风险与本人无关

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 木东居士 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、项目背景
  • 二、工具和环境
  • 三、需求分析
  • 四、页面分析
    • 4.1 排行榜页解析
      • 4.2 详细信息页面api解析
      • 五、分析
        • 5.1 Scrapy框架概述
          • 5.2 为什么用Scrapy框架
          • 六、编写
            • 6.1 新建项目
              • 6.2 创建并编写start.py
                • 6.3 编写settings.py
                  • 6.4 编写bl.py
                    • 6.5 编写Items.py
                      • 6.6 编写pipeline.py
                      • 七、本篇小结
                      相关产品与服务
                      命令行工具
                      腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档