二次元属性被稀释,B站还剩什么?| 数据获取
本文为HeoiJin原创投稿文章,欢迎更多读者投稿!
作者简介
HeoiJin:立志透过数据看清世界的产品策划、数据分析、产品策划领域。
万物皆营销 | 资本永不眠 | 数据恒真理
CSDN:https://me.csdn.net/weixin_40679090
由于篇幅过大原因,文章将分为上篇与下篇:上篇为数据获取,下篇为数据分析。今天为大家带来的是上篇:获取B站数据!
本篇目录
一、项目背景
最近看了一篇对B站2019年数据解读的文章,文章最后得出结论:B站的二次元属性已被稀释,逐渐走向大众。
那么走过2020年的春节后,二次元属性的稀释情况如何?什么分区是B站的龙头?b站的主流用户喜欢什么标签的视频?各分区的情况能带来什么社会价值?本项目将通过数据带你一起窥探B站的变化。
项目特色:
利用Scrapy框架进行网页获取
利用pandas、numpy进行数据分析
利用pyecharts进行数据可视化
利用scipy进行相关性分析
二、工具和环境
语言:python 3.7
IDE:Pycharm
浏览器:Chrome
框架:Scrapy 1.8.0
三、需求分析
B站是我们熟悉的弹幕视频分享网站,根据百度百科的资料显示,B站的主要业务包括直播、游戏、广告、电商、漫画、电竞。
在这么多项的业务当中,我们不难看到一个共同点,B站的主要盈利模式是高度依赖用户,其次是主播和UP主。
因此要分析B站的变化,就要从用户喜爱变化情况切入分析,本次项目将采集以下数据:
排行榜的分区名
排行页:视频的标题、作者、综合评分、排名、视频链接
详情页:视频的播放量、三连量、评论量、弹幕量、转发量、热门标签
四、页面分析
4.1 排行榜页解析
首先从排行榜页面进行过分析。禁用Javascript后,发现要提取的信息都是在静态网页当中,那么在编写代码的时候通过xpath定位抓取信息即可。
完成单个分区排行榜页面的分析后,只需找到各排行榜对应的url即可实现爬取多个分区。通过检查网页源码,发现每一个分区都只有文字描述,并没有相关的url,因此通过分析url变化再自行构造请求的url。
url规则即对应编号:https://www.bilibili.com/ranking/all/编号/0/30
以下是各分类对应的编号:
我们创建一个编号的列表,并用循环拼接url即可完成url的批量生成
from pprint import pprintlabels_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语句进行安装
pip3 install Scrapy
6.1 新建项目
去到要新建文件的文件夹中,在地址栏输入cmd,进入cmd模式。
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命令
from scrapy import cmdlinecmdline.execute('scrapy crawl bl'.split())
创建完成后,我们每次运行或者debug测试时,只需要执行这个文件即可。
6.3 编写settings.py
目标:
关闭遵循君子协议
设置延迟(好的程序不应对别人服务器造成过大压力)
构造请求头
打开Pipeline(用于储存数据,取消注释即可)
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链接发送请求
获得三连、弹幕、评论和热门标签等数据
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_urlsstart_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,切片后获得视频的idid=rank_list.xpath('div/div[@class="info"]/a/@href').get().split('/av')[-1]# 拼接详情页api的urlDetail_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_nameyield 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']# 把所有爬取的信息传递给Itemitem=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的模板填写好即可
目标:
收集爬取到数据
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文件
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 itemdef 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
郑重声明:本项目及所有相关文章,仅用于经验技术交流,禁止将相关技术应用到不正当途径,因为滥用技术产生的风险与本人无关
热门文章
直戳泪点!数据从业者权威嘲讽指南!
数据分析师做成了提数工程师,该如何破局?
全栈型VS专精型,团队到底需要什么样的人?
数据驱动业务,比技术更重要的是思维的转变
最近面了十多个数据分析师,聊一聊我发现的一些问题
二次元属性被稀释,B站还剩什么?| 数据获取相关推荐
- 二次元属性被稀释,B站还剩什么?(下篇)
作者简介 HeoiJin:立志透过数据看清世界的产品策划,专注爬虫.数据分析.产品策划领域. 万物皆营销 | 资本永不眠 | 数据恒真理 CSDN:https://me.csdn.net/weixin ...
- 二次元属性被稀释,B站多了魔幻现实?
作者简介 HeoiJin:立志透过数据看清世界的产品策划,专注爬虫.数据分析.产品策划领域. 万物皆营销 | 资本永不眠 | 数据恒真理 CSDN:https://me.csdn.net/weixin ...
- php时间到期提醒功能,php还剩多长时间过期函数
还剩多长时间过期 public function TimeToSeconds( $end_time ) { date_default_timezone_set('Asia/Shanghai'); $n ...
- SAP技术专家Jerry的技术分享微信群 - 2021年1月14日更新 - 还剩27个名额
SAP技术专家Jerry的技术分享微信群 2020年10月19日才创建的微信群,里面会不定期分享我的工作感受和对SAP技术发展方向的个人看法,欢迎扫码加入. 加入群后请将自己的昵称改成如下格式: &l ...
- 树上有10只鸟,打死1只,还剩几只?答案太牛了!
树上有10只鸟,猎人开枪打死了1只,还剩几只?最后的答案太亮了! ---- 编辑 ∑Gemini 来源:侦探娱乐组 ☞泰勒定理的奇闻轶事 ☞丘成桐:漫谈微分几何 ☞Leibniz 如何想出微积分?(一 ...
- python多大学_用Python看看你的大学A4纸还剩多少!
原标题:用Python看看你的大学A4纸还剩多少! 这是一篇朴素的推送 将通过一张A4大小的方格纸展示大学四年 如果你想看看"你大学的A4纸" 可执行文件(.exe免安装)在 文末 ...
- textarea 字体限制,超出部分不显示并及时显示还剩字体个数
1)HTML <textarea class="box" ></textarea > 2)JQ: $(function(){$(".box&quo ...
- 查看计算机硬件配置方法(包括内存条、显卡型号,卡槽还剩几个)
查看计算机硬件配置的方法(包括内存条.显卡.卡槽还剩几个) 1.从开始搜索"任务管理器",再点性能,即可查看 2.快捷键:同时按下Ctrl+Shift+Esc键,再点击" ...
- 20211104:Excel今天距某个日期后90天还剩多少天
=A4+90-TODAY() A4是发生日期,比如保质期是90天,这个公式就是离保质期还剩几天.剩余0天的时候就是到期当天.
最新文章
- 无盘工作站 服务器 性能,无盘工作站与有盘工作站比较,突出的优势有哪些?...
- Spring Boot/Cloud干货汇总(持续更新:20180226版)
- object-c 代理反向传值
- 2020,这些前沿技术成全球关注热点
- java 打印ascii字符串_简单使用JAVA打印纯ASCII字符构成的酷图效果
- php windows 网络流量,PHP系统流量分析的程序
- python mysql输出的字符带括号_python 将dataframe插入到mysql库中出现表头加括号的情况...
- 被拿走的雨伞——我想到的
- 微信小程序控制台 报错 对应的服务器证书无效 控制台输入 showRequestInfo() 可以获取更详细信息 原因是ssl证书过期 重新申请即可
- 稀里糊涂的解决了 cuda 和cudnn的安装以及conda安装pytorch出现的torch.cuda.is_available()为false的问题
- 牛B学生上课气死老师
- 计算机学院宣传橱窗,校园橱窗、报栏、展板、宣传标语管理办法
- 为什么按序发射只有RAW冲突?
- 计算机一级题库及答案2019百度云,2018-2019-计算机一级考试题库和答案-优秀word参考范文-(6页)...
- PayPal轮询收单系统升级之PayPalme亲友支付
- 2021年N1叉车司机模拟考试及N1叉车司机证考试
- 有限元 弧长法 matlab,有没有有关弧长法的程序!!!
- Cisco AP镜像中的ap1g1 ap1g2,ap3g1什么含义?
- 求方程的解(简化版)
- chromium编译与支持H264编码支持