整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

用Python爬取六大平台的弹幕、评论,看这一篇就够了

天讲解如何用python爬取芒果TV、腾讯视频、B站、爱奇艺、知乎、微博这几个常见常用的影视、舆论平台的弹幕和评论,这类爬虫得到的结果一般用于娱乐、舆情分析,如:新出一部火爆的电影,爬取弹幕评论分析他为什么这么火;微博又出大瓜,爬取底下评论看看网友怎么说,等等这娱乐性分析。

本文爬取一共六个平台,十个爬虫案例,如果只对个别案例感兴趣的可以根据:芒果TV、腾讯视频、B站、爱奇艺、知乎、微博这一顺序进行拉取观看。完整的实战源码已在文中,我们废话不多说,下面开始操作!

芒果TV

本文以爬取电影《悬崖之上》为例,讲解如何爬取芒果TV视频的弹幕和评论!

网页地址:

https://www.mgtv.com/b/335313/12281642.html?fpa=15800&fpos=8&lastp=ch_movie


弹幕


分析网页

弹幕数据所在的文件是动态加载的,需要进入浏览器的开发者工具进行抓包,得到弹幕数据所在的真实url。当视频播放一分钟它就会更新一个json数据包,里面包含我们需要的弹幕数据。

得到的真实url:

https://bullet-ali.hitv.com/bullet/2021/08/14/005323/12281642/0.json
https://bullet-ali.hitv.com/bullet/2021/08/14/005323/12281642/1.json

可以发现,每条url的差别在于后面的数字,首条url为0,后面的逐步递增。视频一共120:20分钟,向上取整,也就是121条数据包。


实战代码

import requests
import pandas as pd

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
df = pd.DataFrame()
for e in range(0, 121):
    print(f'正在爬取第{e}页')
    resposen = requests.get(f'https://bullet-ali.hitv.com/bullet/2021/08/3/004902/12281642/{e}.json', headers=headers)
    # 直接用json提取数据
    for i in resposen.json()['data']['items']:
        ids = i['ids']  # 用户id
        content = i['content']  # 弹幕内容
        time = i['time']  # 弹幕发生时间
        # 有些文件中不存在点赞数
        try:  
            v2_up_count = i['v2_up_count']
        except:
            v2_up_count = ''
        text = pd.DataFrame({'ids': [ids], '弹幕': [content], '发生时间': [time]})
        df = pd.concat([df, text])
df.to_csv('悬崖之上.csv', encoding='utf-8', index=False)

结果展示:

评论


分析网页

芒果TV视频的评论需要拉取到网页下面进行查看。评论数据所在的文件依然是动态加载的,进入开发者工具,按下列步骤进行抓包:Network→js,最后点击查看更多评论。

加载出来的依然是js文件,里面包含评论数据。得到的真实url:

https://comment.mgtv.com/v4/comment/getCommentList?page=1&subjectType=hunantv2014&subjectId=12281642&callback=jQuery1820749973529821774_1628942431449&_support=10000000&_=1628943290494
https://comment.mgtv.com/v4/comment/getCommentList?page=2&subjectType=hunantv2014&subjectId=12281642&callback=jQuery1820749973529821774_1628942431449&_support=10000000&_=1628943296653

其中有差别的参数有page_,page是页数,_是时间戳;url中的时间戳删除后不影响数据完整性,但里面的callback参数会干扰数据解析,所以进行删除。最后得到url:

https://comment.mgtv.com/v4/comment/getCommentList?page=1&subjectType=hunantv2014&subjectId=12281642&_support=10000000

数据包中每页包含15条评论数据,评论总数是2527,得到最大页为169。


实战代码

import requests
import pandas as pd

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
df = pd.DataFrame()
for o in range(1, 170):
    url = f'https://comment.mgtv.com/v4/comment/getCommentList?page={o}&subjectType=hunantv2014&subjectId=12281642&_support=10000000'
    res = requests.get(url, headers=headers).json()
    for i in res['data']['list']:
        nickName = i['user']['nickName']  # 用户昵称
        praiseNum = i['praiseNum']  # 被点赞数
        date = i['date']  # 发送日期
        content = i['content']  # 评论内容
        text = pd.DataFrame({'nickName': [nickName], 'praiseNum': [praiseNum], 'date': [date], 'content': [content]})
        df = pd.concat([df, text])
df.to_csv('悬崖之上.csv', encoding='utf-8', index=False)

结果展示:


腾讯视频

本文以爬取电影《革命者》为例,讲解如何爬取腾讯视频的弹幕和评论!

网页地址:

https://v.qq.com/x/cover/mzc00200m72fcup.html

弹幕


分析网页

依然进入浏览器的开发者工具进行抓包,当视频播放30秒它就会更新一个json数据包,里面包含我们需要的弹幕数据。

得到真实url:

https://mfm.video.qq.com/danmu?otype=json&callback=jQuery19109541041335587612_1628947050538&target_id=7220956568%26vid%3Dt0040z3o3la&session_key=0%2C32%2C1628947057×tamp=15&_=1628947050569
https://mfm.video.qq.com/danmu?otype=json&callback=jQuery19109541041335587612_1628947050538&target_id=7220956568%26vid%3Dt0040z3o3la&session_key=0%2C32%2C1628947057×tamp=45&_=1628947050572

其中有差别的参数有timestamp_。_是时间戳。timestamp是页数,首条url为15,后面以公差为30递增,公差是以数据包更新时长为基准,而最大页数为视频时长7245秒。依然删除不必要参数,得到url:

https://mfm.video.qq.com/danmu?otype=json&target_id=7220956568%26vid%3Dt0040z3o3la&session_key=0%2C18%2C1628418094×tamp=15&_=1628418086509


实战代码

import pandas as pd
import time
import requests

headers = {
    'User-Agent': 'Googlebot'
}
# 初始为15,7245 为视频秒长,链接以三十秒递增
df = pd.DataFrame()
for i in range(15, 7245, 30):
    url = "https://mfm.video.qq.com/danmu?otype=json&target_id=7220956568%26vid%3Dt0040z3o3la&session_key=0%2C18%2C1628418094×tamp={}&_=1628418086509".format(i)
    html = requests.get(url, headers=headers).json()
    time.sleep(1)
    for i in html['comments']:
        content = i['content']
        print(content)
        text = pd.DataFrame({'弹幕': [content]})
        df = pd.concat([df, text])
df.to_csv('革命者_弹幕.csv', encoding='utf-8', index=False)

结果展示:

评论


分析网页

腾讯视频评论数据在网页底部,依然是动态加载的,需要按下列步骤进入开发者工具进行抓包:

点击查看更多评论后,得到的数据包含有我们需要的评论数据,得到的真实url:

https://video.coral.qq.com/varticle/6655100451/comment/v2?callback=_varticle6655100451commentv2&orinum=10&oriorder=o&pageflag=1&cursor=0&scorecursor=0&orirepnum=2&reporder=o&reppageflag=1&source=132&_=1628948867522
https://video.coral.qq.com/varticle/6655100451/comment/v2?callback=_varticle6655100451commentv2&orinum=10&oriorder=o&pageflag=1&cursor=6786869637356389636&scorecursor=0&orirepnum=2&reporder=o&reppageflag=1&source=132&_=1628948867523

url中的参数callback以及_删除即可。重要的是参数cursor,第一条url参数cursor是等于0的,第二条url才出现,所以要查找cursor参数是怎么出现的。经过我的观察,cursor参数其实是上一条url的last参数:


实战代码

import requests
import pandas as pd
import time
import random

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
df = pd.DataFrame()
a = 1
# 此处必须设定循环次数,否则会无限重复爬取
# 281为参照数据包中的oritotal,数据包中一共10条数据,循环280次得到2800条数据,但不包括底下回复的评论
# 数据包中的commentnum,是包括回复的评论数据的总数,而数据包都包含10条评论数据和底下的回复的评论数据,所以只需要把2800除以10取整数+1即可!
while a < 281:
    if a == 1:
        url = 'https://video.coral.qq.com/varticle/6655100451/comment/v2?orinum=10&oriorder=o&pageflag=1&cursor=0&scorecursor=0&orirepnum=2&reporder=o&reppageflag=1&source=132'
    else:
        url = f'https://video.coral.qq.com/varticle/6655100451/comment/v2?orinum=10&oriorder=o&pageflag=1&cursor={cursor}&scorecursor=0&orirepnum=2&reporder=o&reppageflag=1&source=132'
    res = requests.get(url, headers=headers).json()
    cursor = res['data']['last']
    for i in res['data']['oriCommList']:
        ids = i['id']
        times = i['time']
        up = i['up']
        content = i['content'].replace('\n', '')
        text = pd.DataFrame({'ids': [ids], 'times': [times], 'up': [up], 'content': [content]})
        df = pd.concat([df, text])
    a += 1
    time.sleep(random.uniform(2, 3))
    df.to_csv('革命者_评论.csv', encoding='utf-8', index=False)

效果展示:


B站

本文以爬取视频《“ 这是我见过最拽的一届中国队奥运冠军”》为例,讲解如何爬取B站视频的弹幕和评论!

网页地址:

https://www.bilibili.com/video/BV1wq4y1Q7dp

弹幕


分析网页

B站视频的弹幕不像腾讯视频那样,播放视频就会触发弹幕数据包,他需要点击网页右侧的弹幕列表行的展开,然后点击查看历史弹幕获得视频弹幕开始日到截至日链接:

链接末尾以oid以及开始日期来构成弹幕日期url:

https://api.bilibili.com/x/v2/dm/history/index?type=1&oid=384801460&month=2021-08

在上面的的基础之上,点击任一有效日期即可获得这一日期的弹幕数据包,里面的内容目前是看不懂的,之所以确定它为弹幕数据包,是因为点击了日期他才加载出来,且链接与前面的链接具有相关性:

得到的url:

https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid=384801460&date=2021-08-08

url中的oid为视频弹幕链接的id值;data参数为刚才的的日期,而获得该视频全部弹幕内容,只需要更改data参数即可。而data参数可以从上面的弹幕日期url获得,也可以自行构造;网页数据格式为json格式


实战代码

import requests
import pandas as pd
import re

def data_resposen(url):
    headers = {
        "cookie": "你的cookie",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36"
    }
    resposen = requests.get(url, headers=headers)
    return resposen

def main(oid, month):
    df = pd.DataFrame()
    url = f'https://api.bilibili.com/x/v2/dm/history/index?type=1&oid={oid}&month={month}'
    list_data = data_resposen(url).json()['data']  # 拿到所有日期
    print(list_data)
    for data in list_data:
        urls = f'https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid={oid}&date={data}'
        text = re.findall(".*?([\u4E00-\u9FA5]+).*?", data_resposen(urls).text)
        for e in text:
            print(e)
            data = pd.DataFrame({'弹幕': [e]})
            df = pd.concat([df, data])
    df.to_csv('弹幕.csv', encoding='utf-8', index=False, mode='a+')

if __name__ == '__main__':
    oid = '384801460'  # 视频弹幕链接的id值
    month = '2021-08'  # 开始日期
    main(oid, month)

结果展示:

评论


分析网页

B站视频的评论内容在网页下方,进入浏览器的开发者工具后,只需要向下拉取即可加载出数据包:

得到真实url:

https://api.bilibili.com/x/v2/reply/main?callback=jQuery1720034332372316460136_1629011550479&jsonp=jsonp&next=0&type=1&oid=589656273&mode=3&plat=1&_=1629012090500
https://api.bilibili.com/x/v2/reply/main?callback=jQuery1720034332372316460136_1629011550483&jsonp=jsonp&next=2&type=1&oid=589656273&mode=3&plat=1&_=1629012513080
https://api.bilibili.com/x/v2/reply/main?callback=jQuery1720034332372316460136_1629011550484&jsonp=jsonp&next=3&type=1&oid=589656273&mode=3&plat=1&_=1629012803039

两条urlnext参数,以及_callback参数。_callback一个是时间戳,一个是干扰参数,删除即可。next参数第一条为0,第二条为2,第三条为3,所以第一条next参数固定为0,第二条开始递增;网页数据格式为json格式。


实战代码

import requests
import pandas as pd

df = pd.DataFrame()
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'}
try:
    a = 1
    while True:
        if a == 1:
         # 删除不必要参数得到的第一条url
            url = f'https://api.bilibili.com/x/v2/reply/main?&jsonp=jsonp&next=0&type=1&oid=589656273&mode=3&plat=1'
        else:
            url = f'https://api.bilibili.com/x/v2/reply/main?&jsonp=jsonp&next={a}&type=1&oid=589656273&mode=3&plat=1'
        print(url)
        html = requests.get(url, headers=headers).json()
        for i in html['data']['replies']:
            uname = i['member']['uname']  # 用户名称
            sex = i['member']['sex']  # 用户性别
            mid = i['mid']  # 用户id
            current_level = i['member']['level_info']['current_level']  # vip等级
            message = i['content']['message'].replace('\n', '')  # 用户评论
            like = i['like']  # 评论点赞次数
            ctime = i['ctime']  # 评论时间
            data = pd.DataFrame({'用户名称': [uname], '用户性别': [sex], '用户id': [mid],
                                 'vip等级': [current_level], '用户评论': [message], '评论点赞次数': [like],
                                 '评论时间': [ctime]})
            df = pd.concat([df, data])
        a += 1
except Exception as e:
    print(e)
df.to_csv('奥运会.csv', encoding='utf-8')
print(df.shape)

结果展示,获取的内容不包括二级评论,如果需要,可自行爬取,操作步骤差不多:


爱奇艺

本文以爬取电影《哥斯拉大战金刚》为例,讲解如何爬爱奇艺视频的弹幕和评论!

网页地址:

https://www.iqiyi.com/v_19rr0m845o.html

弹幕


分析网页

爱奇艺视频的弹幕依然是要进入开发者工具进行抓包,得到一个br压缩文件,点击可以直接下载,里面的内容是二进制数据,视频每播放一分钟,就加载一条数据包:

得到url,两条url差别在于递增的数字,60为视频每60秒更新一次数据包:

https://cmts.iqiyi.com/bullet/64/00/1078946400_60_1_b2105043.br
https://cmts.iqiyi.com/bullet/64/00/1078946400_60_2_b2105043.br

br文件可以用brotli库进行解压,但实际操作起来很难,特别是编码等问题,难以解决;在直接使用utf-8进行解码时,会报以下错误:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x91 in position 52: invalid start byte

在解码中加入ignore,中文不会乱码,但html格式出现乱码,数据提取依然很难:

decode("utf-8", "ignore")

小刀被编码弄到头疼,如果有兴趣的小伙伴可以对上面的内容继续研究,本文就不在进行深入。所以本文采用另一个方法,对得到url进行修改成以下链接而获得.z压缩文件:

https://cmts.iqiyi.com/bullet/64/00/1078946400_300_1.z

之所以如此更改,是因为这是爱奇艺以前的弹幕接口链接,他还未删除或修改,目前还可以使用。该接口链接中1078946400是视频id;300是以前爱奇艺的弹幕每5分钟会加载出新的弹幕数据包,5分钟就是300秒,《哥斯拉大战金刚》时长112.59分钟,除以5向上取整就是23;1是页数;64为id值的第7为和第8为数。


实战代码

import requests
import pandas as pd
from lxml import etree
from zlib import decompress  # 解压

df = pd.DataFrame()
for i in range(1, 23):
    url = f'https://cmts.iqiyi.com/bullet/64/00/1078946400_300_{i}.z'
    bulletold = requests.get(url).content  # 得到二进制数据
    decode = decompress(bulletold).decode('utf-8')  # 解压解码
    with open(f'{i}.html', 'a+', encoding='utf-8') as f:  # 保存为静态的html文件
        f.write(decode)

    html = open(f'./{i}.html', 'rb').read()  # 读取html文件
    html = etree.HTML(html)  # 用xpath语法进行解析网页
    ul = html.xpath('/html/body/danmu/data/entry/list/bulletinfo')
    for i in ul:
        contentid = ''.join(i.xpath('./contentid/text()'))
        content = ''.join(i.xpath('./content/text()'))
        likeCount = ''.join(i.xpath('./likecount/text()'))
        print(contentid, content, likeCount)
        text = pd.DataFrame({'contentid': [contentid], 'content': [content], 'likeCount': [likeCount]})
        df = pd.concat([df, text])
df.to_csv('哥斯拉大战金刚.csv', encoding='utf-8', index=False)

结果展示:

评论


分析网页

爱奇艺视频的评论在网页下方,依然是动态加载的内容,需要进入浏览器的开发者工具进行抓包,当网页下拉取时,会加载一条数据包,里面包含评论数据:

得到的真实url:

https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&authcookie=null&business_type=17&channel_id=1&content_id=1078946400&hot_size=10&last_id=&page=&page_size=10&types=hot,time&callback=jsonp_1629025964363_15405
https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&authcookie=null&business_type=17&channel_id=1&content_id=1078946400&hot_size=0&last_id=7963601726142521&page=&page_size=20&types=time&callback=jsonp_1629026041287_28685
https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&authcookie=null&business_type=17&channel_id=1&content_id=1078946400&hot_size=0&last_id=4933019153543021&page=&page_size=20&types=time&callback=jsonp_1629026394325_81937

第一条url加载的是精彩评论的内容,第二条url开始加载的是全部评论的内容。经过删减不必要参数得到以下url:

https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&business_type=17&content_id=1078946400&last_id=&page_size=10
https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&business_type=17&content_id=1078946400&last_id=7963601726142521&page_size=20
https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&business_type=17&content_id=1078946400&last_id=4933019153543021&page_size=20

区别在于参数last_idpage_size。page_size在第一条url中的值为10,从第二条url开始固定为20。last_id在首条url中值为空,从第二条开始会不断发生变化,经过我的研究,last_id的值就是从前一条url中的最后一条评论内容的用户id(应该是用户id);网页数据格式为json格式。


实战代码

import requests
import pandas as pd
import time
import random


headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
df = pd.DataFrame()
try:
    a = 0
    while True:
        if a == 0:
            url = 'https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&business_type=17&content_id=1078946400&page_size=10'
        else:
            # 从id_list中得到上一条页内容中的最后一个id值
            url = f'https://sns-comment.iqiyi.com/v3/comment/get_comments.action?agent_type=118&agent_version=9.11.5&business_type=17&content_id=1078946400&last_id={id_list[-1]}&page_size=20'
        print(url)
        res = requests.get(url, headers=headers).json()
        id_list = []  # 建立一个列表保存id值
        for i in res['data']['comments']:
            ids = i['id']
            id_list.append(ids)
            uname = i['userInfo']['uname']
            addTime = i['addTime']
            content = i.get('content', '不存在')  # 用get提取是为了防止键值不存在而发生报错,第一个参数为匹配的key值,第二个为缺少时输出
            text = pd.DataFrame({'ids': [ids], 'uname': [uname], 'addTime': [addTime], 'content': [content]})
            df = pd.concat([df, text])
        a += 1
        time.sleep(random.uniform(2, 3))
except Exception as e:
    print(e)
df.to_csv('哥斯拉大战金刚_评论.csv', mode='a+', encoding='utf-8', index=False)

结果展示:


知乎

本文以爬取知乎热点话题《如何看待网传腾讯实习生向腾讯高层提出建议颁布拒绝陪酒相关条令?》为例,讲解如爬取知乎回答!

网页地址:

https://www.zhihu.com/question/478781972


分析网页

经过查看网页源代码等方式,确定该网页回答内容为动态加载的,需要进入浏览器的开发者工具进行抓包。进入Noetwork→XHR,用鼠标在网页向下拉取,得到我们需要的数据包:

得到的真实url:

https://www.zhihu.com/api/v4/questions/478781972/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cattachment%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Cis_labeled%2Cpaid_info%2Cpaid_info_content%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_recognized%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cvip_info%2Cbadge%5B%2A%5D.topics%3Bdata%5B%2A%5D.settings.table_of_content.enabled&limit=5&offset=0&platform=desktop&sort_by=default
https://www.zhihu.com/api/v4/questions/478781972/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cattachment%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Cis_labeled%2Cpaid_info%2Cpaid_info_content%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_recognized%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cvip_info%2Cbadge%5B%2A%5D.topics%3Bdata%5B%2A%5D.settings.table_of_content.enabled&limit=5&offset=5&platform=desktop&sort_by=default

url有很多不必要的参数,大家可以在浏览器中自行删减。两条url的区别在于后面的offset参数,首条url的offset参数为0,第二条为5,offset是以公差为5递增;网页数据格式为json格式。


实战代码

import requests
import pandas as pd
import re
import time
import random

df = pd.DataFrame()
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
}
for page in range(0, 1360, 5):
    url = f'https://www.zhihu.com/api/v4/questions/478781972/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cattachment%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Cis_labeled%2Cpaid_info%2Cpaid_info_content%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_recognized%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cvip_info%2Cbadge%5B%2A%5D.topics%3Bdata%5B%2A%5D.settings.table_of_content.enabled&limit=5&offset={page}&platform=desktop&sort_by=default'
    response = requests.get(url=url, headers=headers).json()
    data = response['data']
    for list_ in data:
        name = list_['author']['name']  # 知乎作者
        id_ = list_['author']['id']  # 作者id
        created_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(list_['created_time'] )) # 回答时间
        voteup_count = list_['voteup_count']  # 赞同数
        comment_count = list_['comment_count']  # 底下评论数
        content = list_['content']  # 回答内容
        content = ''.join(re.findall("[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b\u4e00-\u9fa5]", content))  # 正则表达式提取
        print(name, id_, created_time, comment_count, content, sep='|')
        dataFrame = pd.DataFrame(
            {'知乎作者': [name], '作者id': [id_], '回答时间': [created_time], '赞同数': [voteup_count], '底下评论数': [comment_count],
             '回答内容': [content]})
        df = pd.concat([df, dataFrame])
    time.sleep(random.uniform(2, 3))
df.to_csv('知乎回答.csv', encoding='utf-8', index=False)
print(df.shape)

结果展示:


微博

本文以爬取微博热搜《霍尊手写道歉信》为例,讲解如何爬取微博评论!

网页地址:

https://m.weibo.cn/detail/4669040301182509


分析网页

微博评论是动态加载的,进入浏览器的开发者工具后,在网页上向下拉取会得到我们需要的数据包:

得到真实url:

https://m.weibo.cn/comments/hotflow?id=4669040301182509&mid=4669040301182509&max_id_type=0
https://m.weibo.cn/comments/hotflow?id=4669040301182509&mid=4669040301182509&max_id=3698934781006193&max_id_type=0

两条url区别很明显,首条url是没有参数max_id的,第二条开始max_id才出现,而max_id其实是前一条数据包中的max_id:

但有个需要注意的是参数max_id_type,它其实也是会变化的,所以我们需要从数据包中获取max_id_type:

实战代码import re
import requests
import pandas as pd
import time
import random

df = pd.DataFrame()
try:
a = 1
while True:
header = {
'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'
}
resposen = requests.get('https://m.weibo.cn/detail/4669040301182509', headers=header)
# 微博爬取大概几十页会封账号的,而通过不断的更新cookies,会让爬虫更持久点...
cookie = [cookie.value for cookie in resposen.cookies] # 用列表推导式生成cookies部件
headers = {
# 登录后的cookie, SUB用登录后的
'cookie': f'WEIBOCN_FROM={cookie[3]}; SUB=; _T_WM={cookie[4]}; MLOGIN={cookie[1]}; M_WEIBOCN_PARAMS={cookie[2]}; XSRF-TOKEN={cookie[0]}',
'referer': 'https://m.weibo.cn/detail/4669040301182509',
'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'
}
if a == 1:
url = 'https://m.weibo.cn/comments/hotflow?id=4669040301182509&mid=4669040301182509&max_id_type=0'
else:
url = f'https://m.weibo.cn/comments/hotflow?id=4669040301182509&mid=4669040301182509&max_id={max_id}&max_id_type={max_id_type}'

html = requests.get(url=url, headers=headers).json()
data = html['data']
max_id = data['max_id'] # 获取max_id和max_id_type返回给下一条url
max_id_type = data['max_id_type']
for i in data['data']:
screen_name = i['user']['screen_name']
i_d = i['user']['id']
like_count = i['like_count'] # 点赞数
created_at = i['created_at'] # 时间
text = re.sub(r'<[^>]*>', '', i['text']) # 评论
print(text)
data_json = pd.DataFrame({'screen_name': [screen_name], 'i_d': [i_d], 'like_count': [like_count], 'created_at': [created_at],'text': [text]})
df = pd.concat([df, data_json])
time.sleep(random.uniform(2, 7))
a += 1
except Exception as e:
print(e)

df.to_csv('微博.csv', encoding='utf-8', mode='a+', index=False)
print(df.shape)

结果展示:

以上便是今天的全部内容了,如果你喜欢今天的内容,希望你能在下方点个赞和在看支持我,谢谢!

聊时看了下斗鱼直播,突然灵感乍现,想做出这个弹幕效果,于是有了下文

这里还是要说一下我的前端学习群:594959296,从我一个到现在的都是看我每一篇文章来的,可以说都是我们大前端的学霸啊,不定期分享干货。想学到东西的都可以来,欢迎初学和进阶中的小伙伴

说明:弹幕分两种:

1、像优酷、爱奇艺等,记录用户发送弹幕时该视频播放的位置,其他人播放这个视频时到了这个点就显示弹幕。

2、像斗鱼、熊猫这样的直播网站,用户发送弹幕直接显示在屏幕上,以后不需要再显示了。

我们就来做第二种!!!

实现效果

项目分析

1、获取用户输入信息;

2、在页面中创建一个 <span></span>来放获取到的文本,并添加一些样式(字体大小、颜色等);

3、给这个 span添加一个从右向左移动的动画;

4、动画结束后,移除这个 <span>

思路很清楚了,就来动手实现一下。。

布局

页面中要有一个输入框让用户输入信息,还要一个盒子用来显示弹幕。为了美观,我多加了一些东西。

1、#dm 是显示弹幕的区域,input供用户输入信息(其中 placeholder 属性规定输入框中默认显示内容)。

2、其它元素都是为了美观和布局。

样式

跟以前一样,先给出代码然后再讲解。

1、首先还是格式化浏览器默认的 marginpaddingoverflow属性设置超出屏幕的部分隐藏,这样就不会出现下拉和水平的滚动条了。

2、#dm用来显示弹幕,我们把它的高度设置为 90vhvh 是 CSS3 中新增的长度单位,表示相对于视口的高度。视口高度被均分为100单位的vh,90vh 就表示当前浏览器可视区域高度的 90%。(vw 就是视口宽度)

3、#dm span是每条弹幕的样式。你可以修改为你喜欢的样子,不过一定要设置 position
定位属性。

4、然后就是下面的输入部分了。使用了 flex布局 (也是CSS3 中新增的内容,太方便了)。只需要 align-items:center;justify-content:center; 这两句就可以使其中的子元素在水平和垂直方向居中。所以 #idDom#content都使用了。

5、然后就是 输入框、按钮的样式了,没什么好说的,应该都能看懂。

JS部分

先看看代码吧。。

1、说明:向页面添加弹幕使用 addBarrage() 函数,添加动画用 move()函数。(把一种功能封装为一个函数是一个好习惯)

2、首先思考一下弹幕的触发事件。应该有两个:点击“发射”按钮、按下 Enter按键。所以分别监听点击 和 键盘事件。

3、然后就要思考怎么向页面添加弹幕了?

先得到用户输入的信息

然后在页面中创建一个

<span></span>

把得到的文本放进去

添加到 #dm这个盒子中:

  • 这样就可以了吗?? 当然不是的。。

4、每条弹幕应该有不同的颜色,这样才炫酷。思路就是把预先的颜色放进一个数组,使用的时候用随机的下标,这样就获得了随机颜色。(借鉴斗鱼的 7 中颜色)

这样每条弹幕就有不同的颜色了。

5、每条弹幕在页面上还要有不同的位置(高度),也就是不同的 top 值。我的想法是,虽然要有不同的 top ,但也不能太随意。就像下面这样:(一行是一行的)

我的思路是:先判断页面可以放多少行?

然后计算可以有的 top 值:(加 10 是为了不至于紧挨着屏幕顶部,最后的 -1 是为了不至于太靠下 )

你可以自己理解一下我的这种计算方式。。

然后应用给

<span>

即可:

6、对了,弹幕应该添加到页面的什么位置呢?因为弹幕要从右往左移动,所以应该添加到屏幕的右侧,left 值为浏览器页面的宽度。这时候就知道为什么前面要设置 body 的 overflow: hidden;了吧!!

动画

弹幕(也就是<span>)被添加到页面中了,我们要让他动起来。思路就是写一个函数,减少 <span>

的 left 值。每隔几毫秒执行一次这个函数,我们看起来这个 <span>元素就动起来了。

7、但是有几个问题:

  • 页面中有多少个弹幕(<span>)?因为页面中只有 弹幕 使用的是 <span> 标签,所以这样就可以获取所有 <span>的 nodeList(类似数组但不是数组,可以使用下标索引访问):

怎样记录每条弹幕的 left 值?获取到所有的 <span>后,用一个 for 循环将每一个 <span> 的 left 值放进一个数组:

  • 这样 arr[] 就保存了所有弹幕的 left 值。

8、接下来我们就逐个减少每条弹幕的 left 值:

9、最后判断如果弹幕已经移出了页面的左边,就把这条弹幕删除了吧。

我们可以在开发工具中看一下这个过程:

弹幕的 left 一直减小,移出页面后 #dm中就没有这个 <span>了。

10、最后处理一个小细节吧!当我们点击按钮或按回车后,输入框中的文字会保留,影响我们下次输入,所以只要获取到了用户输入的内容,就把输入框清空吧!

到此,弹幕就实现了。。。

学习javascript也是有门槛的,就是你的html和css至少还比较熟练,您不能连html这东东是干啥的都不知道就开始学javascript了,学乘除前,学好加减法总是有益无害的。

如果想看到更加系统的文章和学习方法经验可以关注我的微信公众号:‘web前端课程’关注后回复‘给我资料’可以领取一套完整的学习视频

着视频网站和直播网站的兴起,弹幕的功能算是一个核心元素,弹幕最初是由日本视频网站Niconico引入的,国内除了在Bilibili和AcFun等弹幕视频网站中使用之外,其他主流视频网站中的视频播放器也支持弹幕,对于视频网站和直播网站来说,弹幕的交互实时性更高,更加受欢迎。

本文重点介绍一下弹幕的基本原理,以及JavaScript和HTML的代码实现方式,最后分享一个弹幕的开源库。

关于视频网站建设,可以参考我们之前的通过Drupal构建自建视频课程平台。

弹幕基础知识

单条弹幕的属性

  1. 模式:滚动弹幕、逆向弹幕、顶部弹幕、底部弹幕。
  2. 基本属性:正文、出现时间、持续时间、字体、字号、颜色等样式。
  3. 容器:块级元素,一般是与视频大小相同的区域。

弹幕的特性:

  1. 空间的独立性:虽然在同一个元素内展现,但空间计算上每种弹幕模式处于不同层,每种模式也可以有多层,每层内的弹幕占位不会重叠。
  2. 渲染的确定性:如果弹幕容器和渲染的列表固定不变,在没有用户交互(比如暂停单条弹幕)的情况下,每次渲染每条弹幕所出现的位置和顺序都是固定的。
  3. 各模式的统一性:每种模式的弹幕的生存时间保持一致。

弹幕的基本模式:

  1. 滚动弹幕:自右向左匀速滚动过屏幕的弹幕,以自上而下的优先度展示。
  2. 逆向弹幕:自左向右匀速滚动过屏幕的弹幕,以自上而下的优先度展示,逆向弹幕是相对于滚动弹幕的相反方向滚动的弹幕展示模式。
  3. 顶部弹幕:自上而下静止居中的弹幕、以自上而下的优先度展示。
  4. 底部弹幕:自下而上静止居中的弹幕、以自下而上的优先度展示。

弹幕的使用场景:

  1. 点播视频互动,把评论搬到弹幕上来
  2. 直播互动,直播视频中,可以直接通过弹幕讨论、互动
  3. 视频内容增强,比如对视频中某些内容进行解释,描述等
  4. 标记视频亮点,对视频中的高亮进行提示等
  5. 网页互动,比如弹幕可以点赞,回复等
  6. 互动上墙,大屏的投票可以通过弹幕来实现交互大墙
  7. 蒙版弹幕,可以对某个地方不想让用户看到,可以通过弹幕掩盖
  8. 非文字弹幕,比如表情、红包等等交互效果

弹幕的实现原理

弹幕本身不是对视频的操作,其实是给视频上面加DIV等DOM元素,也就是给视频上面加层,然后展示我们想要的内容即可,目前弹幕的主要实现有两种方式:

  1. Canvas
  2. HTML+CSS

首先,这种我们自然想到用Canvas,使用Canvas能很方便地绘制动画,并且获得非常不错的性能,目前前端不少动画都是通过Canvas去做。不过对于基于Canvas的动画而言,最大的问题就是“交互性”上面。

如果用HTML+CSS的方式实现,我们可以很简单地通过监听原生DOM事件去知道哪一条弹幕与用户鼠标发生了交互。但是通过Canvas,我们只能通过监听画布的事件,然后做一堆遍历计算坐标的骚操作去确定是哪一条弹幕。从鹅厂的视频网站可以看到,他们的弹幕是可交互的,所以他们使用了HTML+CSS的实现方式;而B站的弹幕是非交互的,它提供Canvas和HTML+CSS可选,默认是前者。

虽然功能性上两者的实现会有点差异,但弹幕的基本原理都是一样。

所以弹幕本身就是对DIV元素的操作,DIV层一般在Video层上面,类似下面的图,不过也有放在Video层下面,弹幕层在底下,详情参考这篇文章的介绍 https://github.com/logcas/a-barrage

多层的弹幕实现

弹幕实现的技术细节

我们先分析一下文档管理系统的基本需求以及功能点,

从上图可以看到,弹幕是很清晰地分成了一行一行,我把它们称为“轨道”。每一个弹幕都只在轨道上从右往左移动,不会越界。那么,要实现弹幕功能,首先我们必须把弹幕分成若干个轨道,然后再在合适的时间把弹幕“塞”进去让它平移。

每一个轨道会有两个属性:

barrages: T[] = []
offset: number = 0

barrages为一个弹幕数组,offset则是已占据的宽度。offset用于滚动弹幕时,弹幕轨道添加弹幕前判断最佳轨道;当弹幕类型时固定时无作用。barrages存放当前轨道上可现实的弹幕实例。

每一个轨道实例管理自己轨道中的数组,主要进行进行增、删、重置以及更新offest的操作。

添加新弹幕

push(...items: T[]) {
this.barrages.push(...items)
}

删除弹幕

remove(index: number) {

if (index < 0 || index >= this.barrages.length) { return }

this.barrages.splice(index, 1)

}

重置轨道

reset() {
this.barrages = [] this.offset = 0
}

更新剩余轨

updateOffset() {
const endBarrage = this.barrages[this.barrages.length - 1]
if (endBarrage && isScrollBarrage(endBarrage)) {
const { speed } = endBarrage this.offset -= speed
} }

拿来即用-开源的方案

在知名的男性交友网站github上,已经有一款很好用的弹幕开源解决方案,并且是国产的,叫abarrage,同时实现了Canvas和HTML+CSS的模式,要用的话直接在github把源代码撸下来,并且有在线的体验地址,效果非常不错。

A-Barrage 同时支持Canvas渲染和HTML+CSS的渲染模式,你可以根据实际情况采用不同的渲染引擎进行弹幕的渲染。其中,Canvas是非交互式渲染,也就是说,采用Canvas渲染的弹幕并不会有任何的交互操作,纯展示性质;HTML+CSS是交互式渲染,如果你的网站允许用户与弹幕之间进行一些交互(如点赞、回复等),那么可以采用HTML+CSS的渲染模式,它会结合浏览器的DOM事件进行响应。

github地址: https://github.com/logcas/a-barrage

测试地址: https://logcas.github.io/a-barrage/example/css3.html

网站首页

发弹幕截图