整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          用Python爬取六大平臺(tái)的彈幕、評(píng)論,看這一篇就夠了

          天講解如何用python爬取芒果TV、騰訊視頻、B站、愛(ài)奇藝、知乎、微博這幾個(gè)常見(jiàn)常用的影視、輿論平臺(tái)的彈幕和評(píng)論,這類爬蟲得到的結(jié)果一般用于娛樂(lè)、輿情分析,如:新出一部火爆的電影,爬取彈幕評(píng)論分析他為什么這么火;微博又出大瓜,爬取底下評(píng)論看看網(wǎng)友怎么說(shuō),等等這娛樂(lè)性分析。

          本文爬取一共六個(gè)平臺(tái),十個(gè)爬蟲案例,如果只對(duì)個(gè)別案例感興趣的可以根據(jù):芒果TV、騰訊視頻、B站、愛(ài)奇藝、知乎、微博這一順序進(jìn)行拉取觀看。完整的實(shí)戰(zhàn)源碼已在文中,我們廢話不多說(shuō),下面開(kāi)始操作!

          芒果TV

          本文以爬取電影《懸崖之上》為例,講解如何爬取芒果TV視頻的彈幕和評(píng)論!

          網(wǎng)頁(yè)地址:

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


          彈幕


          分析網(wǎng)頁(yè)

          彈幕數(shù)據(jù)所在的文件是動(dòng)態(tài)加載的,需要進(jìn)入瀏覽器的開(kāi)發(fā)者工具進(jìn)行抓包,得到彈幕數(shù)據(jù)所在的真實(shí)url。當(dāng)視頻播放一分鐘它就會(huì)更新一個(gè)json數(shù)據(jù)包,里面包含我們需要的彈幕數(shù)據(jù)。

          得到的真實(shí)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
          

          可以發(fā)現(xiàn),每條url的差別在于后面的數(shù)字,首條url為0,后面的逐步遞增。視頻一共120:20分鐘,向上取整,也就是121條數(shù)據(jù)包。


          實(shí)戰(zhàn)代碼

          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}頁(yè)')
              resposen = requests.get(f'https://bullet-ali.hitv.com/bullet/2021/08/3/004902/12281642/{e}.json', headers=headers)
              # 直接用json提取數(shù)據(jù)
              for i in resposen.json()['data']['items']:
                  ids = i['ids']  # 用戶id
                  content = i['content']  # 彈幕內(nèi)容
                  time = i['time']  # 彈幕發(fā)生時(shí)間
                  # 有些文件中不存在點(diǎn)贊數(shù)
                  try:  
                      v2_up_count = i['v2_up_count']
                  except:
                      v2_up_count = ''
                  text = pd.DataFrame({'ids': [ids], '彈幕': [content], '發(fā)生時(shí)間': [time]})
                  df = pd.concat([df, text])
          df.to_csv('懸崖之上.csv', encoding='utf-8', index=False)
          

          結(jié)果展示:

          評(píng)論


          分析網(wǎng)頁(yè)

          芒果TV視頻的評(píng)論需要拉取到網(wǎng)頁(yè)下面進(jìn)行查看。評(píng)論數(shù)據(jù)所在的文件依然是動(dòng)態(tài)加載的,進(jìn)入開(kāi)發(fā)者工具,按下列步驟進(jìn)行抓包:Network→js,最后點(diǎn)擊查看更多評(píng)論。

          加載出來(lái)的依然是js文件,里面包含評(píng)論數(shù)據(jù)。得到的真實(shí)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
          

          其中有差別的參數(shù)有page_,page是頁(yè)數(shù),_是時(shí)間戳;url中的時(shí)間戳刪除后不影響數(shù)據(jù)完整性,但里面的callback參數(shù)會(huì)干擾數(shù)據(jù)解析,所以進(jìn)行刪除。最后得到url:

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

          數(shù)據(jù)包中每頁(yè)包含15條評(píng)論數(shù)據(jù),評(píng)論總數(shù)是2527,得到最大頁(yè)為169。


          實(shí)戰(zhàn)代碼

          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']  # 被點(diǎn)贊數(shù)
                  date = i['date']  # 發(fā)送日期
                  content = i['content']  # 評(píng)論內(nèi)容
                  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)
          

          結(jié)果展示:


          騰訊視頻

          本文以爬取電影《革命者》為例,講解如何爬取騰訊視頻的彈幕和評(píng)論!

          網(wǎng)頁(yè)地址:

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

          彈幕


          分析網(wǎng)頁(yè)

          依然進(jìn)入瀏覽器的開(kāi)發(fā)者工具進(jìn)行抓包,當(dāng)視頻播放30秒它就會(huì)更新一個(gè)json數(shù)據(jù)包,里面包含我們需要的彈幕數(shù)據(jù)。

          得到真實(shí)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
          

          其中有差別的參數(shù)有timestamp_。_是時(shí)間戳。timestamp是頁(yè)數(shù),首條url為15,后面以公差為30遞增,公差是以數(shù)據(jù)包更新時(shí)長(zhǎng)為基準(zhǔn),而最大頁(yè)數(shù)為視頻時(shí)長(zhǎng)7245秒。依然刪除不必要參數(shù),得到url:

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


          實(shí)戰(zhàn)代碼

          import pandas as pd
          import time
          import requests
          
          headers = {
              'User-Agent': 'Googlebot'
          }
          # 初始為15,7245 為視頻秒長(zhǎng),鏈接以三十秒遞增
          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('革命者_(dá)彈幕.csv', encoding='utf-8', index=False)
          

          結(jié)果展示:

          評(píng)論


          分析網(wǎng)頁(yè)

          騰訊視頻評(píng)論數(shù)據(jù)在網(wǎng)頁(yè)底部,依然是動(dòng)態(tài)加載的,需要按下列步驟進(jìn)入開(kāi)發(fā)者工具進(jìn)行抓包:

          點(diǎn)擊查看更多評(píng)論后,得到的數(shù)據(jù)包含有我們需要的評(píng)論數(shù)據(jù),得到的真實(shí)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中的參數(shù)callback以及_刪除即可。重要的是參數(shù)cursor,第一條url參數(shù)cursor是等于0的,第二條url才出現(xiàn),所以要查找cursor參數(shù)是怎么出現(xiàn)的。經(jīng)過(guò)我的觀察,cursor參數(shù)其實(shí)是上一條url的last參數(shù):


          實(shí)戰(zhàn)代碼

          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
          # 此處必須設(shè)定循環(huán)次數(shù),否則會(huì)無(wú)限重復(fù)爬取
          # 281為參照數(shù)據(jù)包中的oritotal,數(shù)據(jù)包中一共10條數(shù)據(jù),循環(huán)280次得到2800條數(shù)據(jù),但不包括底下回復(fù)的評(píng)論
          # 數(shù)據(jù)包中的commentnum,是包括回復(fù)的評(píng)論數(shù)據(jù)的總數(shù),而數(shù)據(jù)包都包含10條評(píng)論數(shù)據(jù)和底下的回復(fù)的評(píng)論數(shù)據(jù),所以只需要把2800除以10取整數(shù)+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('革命者_(dá)評(píng)論.csv', encoding='utf-8', index=False)
          

          效果展示:


          B站

          本文以爬取視頻《“ 這是我見(jiàn)過(guò)最拽的一屆中國(guó)隊(duì)奧運(yùn)冠軍”》為例,講解如何爬取B站視頻的彈幕和評(píng)論!

          網(wǎng)頁(yè)地址:

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

          彈幕


          分析網(wǎng)頁(yè)

          B站視頻的彈幕不像騰訊視頻那樣,播放視頻就會(huì)觸發(fā)彈幕數(shù)據(jù)包,他需要點(diǎn)擊網(wǎng)頁(yè)右側(cè)的彈幕列表行的展開(kāi),然后點(diǎn)擊查看歷史彈幕獲得視頻彈幕開(kāi)始日到截至日鏈接:

          鏈接末尾以oid以及開(kāi)始日期來(lái)構(gòu)成彈幕日期url:

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

          在上面的的基礎(chǔ)之上,點(diǎn)擊任一有效日期即可獲得這一日期的彈幕數(shù)據(jù)包,里面的內(nèi)容目前是看不懂的,之所以確定它為彈幕數(shù)據(jù)包,是因?yàn)辄c(diǎn)擊了日期他才加載出來(lái),且鏈接與前面的鏈接具有相關(guān)性:

          得到的url:

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

          url中的oid為視頻彈幕鏈接的id值;data參數(shù)為剛才的的日期,而獲得該視頻全部彈幕內(nèi)容,只需要更改data參數(shù)即可。而data參數(shù)可以從上面的彈幕日期url獲得,也可以自行構(gòu)造;網(wǎng)頁(yè)數(shù)據(jù)格式為json格式


          實(shí)戰(zhàn)代碼

          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'  # 開(kāi)始日期
              main(oid, month)
          

          結(jié)果展示:

          評(píng)論


          分析網(wǎng)頁(yè)

          B站視頻的評(píng)論內(nèi)容在網(wǎng)頁(yè)下方,進(jìn)入瀏覽器的開(kāi)發(fā)者工具后,只需要向下拉取即可加載出數(shù)據(jù)包:

          得到真實(shí)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參數(shù),以及_callback參數(shù)。_callback一個(gè)是時(shí)間戳,一個(gè)是干擾參數(shù),刪除即可。next參數(shù)第一條為0,第二條為2,第三條為3,所以第一條next參數(shù)固定為0,第二條開(kāi)始遞增;網(wǎng)頁(yè)數(shù)據(jù)格式為json格式。


          實(shí)戰(zhàn)代碼

          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:
                   # 刪除不必要參數(shù)得到的第一條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等級(jí)
                      message = i['content']['message'].replace('\n', '')  # 用戶評(píng)論
                      like = i['like']  # 評(píng)論點(diǎn)贊次數(shù)
                      ctime = i['ctime']  # 評(píng)論時(shí)間
                      data = pd.DataFrame({'用戶名稱': [uname], '用戶性別': [sex], '用戶id': [mid],
                                           'vip等級(jí)': [current_level], '用戶評(píng)論': [message], '評(píng)論點(diǎn)贊次數(shù)': [like],
                                           '評(píng)論時(shí)間': [ctime]})
                      df = pd.concat([df, data])
                  a += 1
          except Exception as e:
              print(e)
          df.to_csv('奧運(yùn)會(huì).csv', encoding='utf-8')
          print(df.shape)
          

          結(jié)果展示,獲取的內(nèi)容不包括二級(jí)評(píng)論,如果需要,可自行爬取,操作步驟差不多:


          愛(ài)奇藝

          本文以爬取電影《哥斯拉大戰(zhàn)金剛》為例,講解如何爬愛(ài)奇藝視頻的彈幕和評(píng)論!

          網(wǎng)頁(yè)地址:

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

          彈幕


          分析網(wǎng)頁(yè)

          愛(ài)奇藝視頻的彈幕依然是要進(jìn)入開(kāi)發(fā)者工具進(jìn)行抓包,得到一個(gè)br壓縮文件,點(diǎn)擊可以直接下載,里面的內(nèi)容是二進(jìn)制數(shù)據(jù),視頻每播放一分鐘,就加載一條數(shù)據(jù)包:

          得到url,兩條url差別在于遞增的數(shù)字,60為視頻每60秒更新一次數(shù)據(jù)包:

          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庫(kù)進(jìn)行解壓,但實(shí)際操作起來(lái)很難,特別是編碼等問(wèn)題,難以解決;在直接使用utf-8進(jìn)行解碼時(shí),會(huì)報(bào)以下錯(cuò)誤:

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

          在解碼中加入ignore,中文不會(huì)亂碼,但html格式出現(xiàn)亂碼,數(shù)據(jù)提取依然很難:

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

          小刀被編碼弄到頭疼,如果有興趣的小伙伴可以對(duì)上面的內(nèi)容繼續(xù)研究,本文就不在進(jìn)行深入。所以本文采用另一個(gè)方法,對(duì)得到url進(jìn)行修改成以下鏈接而獲得.z壓縮文件:

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

          之所以如此更改,是因?yàn)檫@是愛(ài)奇藝以前的彈幕接口鏈接,他還未刪除或修改,目前還可以使用。該接口鏈接中1078946400是視頻id;300是以前愛(ài)奇藝的彈幕每5分鐘會(huì)加載出新的彈幕數(shù)據(jù)包,5分鐘就是300秒,《哥斯拉大戰(zhàn)金剛》時(shí)長(zhǎng)112.59分鐘,除以5向上取整就是23;1是頁(yè)數(shù);64為id值的第7為和第8為數(shù)。


          實(shí)戰(zhàn)代碼

          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  # 得到二進(jìn)制數(shù)據(jù)
              decode = decompress(bulletold).decode('utf-8')  # 解壓解碼
              with open(f'{i}.html', 'a+', encoding='utf-8') as f:  # 保存為靜態(tài)的html文件
                  f.write(decode)
          
              html = open(f'./{i}.html', 'rb').read()  # 讀取html文件
              html = etree.HTML(html)  # 用xpath語(yǔ)法進(jìn)行解析網(wǎng)頁(yè)
              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('哥斯拉大戰(zhàn)金剛.csv', encoding='utf-8', index=False)
          

          結(jié)果展示:

          評(píng)論


          分析網(wǎng)頁(yè)

          愛(ài)奇藝視頻的評(píng)論在網(wǎng)頁(yè)下方,依然是動(dòng)態(tài)加載的內(nèi)容,需要進(jìn)入瀏覽器的開(kāi)發(fā)者工具進(jìn)行抓包,當(dāng)網(wǎng)頁(yè)下拉取時(shí),會(huì)加載一條數(shù)據(jù)包,里面包含評(píng)論數(shù)據(jù):

          得到的真實(shí)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加載的是精彩評(píng)論的內(nèi)容,第二條url開(kāi)始加載的是全部評(píng)論的內(nèi)容。經(jīng)過(guò)刪減不必要參數(shù)得到以下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
          

          區(qū)別在于參數(shù)last_idpage_size。page_size在第一條url中的值為10,從第二條url開(kāi)始固定為20。last_id在首條url中值為空,從第二條開(kāi)始會(huì)不斷發(fā)生變化,經(jīng)過(guò)我的研究,last_id的值就是從前一條url中的最后一條評(píng)論內(nèi)容的用戶id(應(yīng)該是用戶id);網(wǎng)頁(yè)數(shù)據(jù)格式為json格式。


          實(shí)戰(zhàn)代碼

          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中得到上一條頁(yè)內(nèi)容中的最后一個(gè)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 = []  # 建立一個(gè)列表保存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提取是為了防止鍵值不存在而發(fā)生報(bào)錯(cuò),第一個(gè)參數(shù)為匹配的key值,第二個(gè)為缺少時(shí)輸出
                      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('哥斯拉大戰(zhàn)金剛_評(píng)論.csv', mode='a+', encoding='utf-8', index=False)
          

          結(jié)果展示:


          知乎

          本文以爬取知乎熱點(diǎn)話題《如何看待網(wǎng)傳騰訊實(shí)習(xí)生向騰訊高層提出建議頒布拒絕陪酒相關(guān)條令?》為例,講解如爬取知乎回答!

          網(wǎng)頁(yè)地址:

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


          分析網(wǎng)頁(yè)

          經(jīng)過(guò)查看網(wǎng)頁(yè)源代碼等方式,確定該網(wǎng)頁(yè)回答內(nèi)容為動(dòng)態(tài)加載的,需要進(jìn)入瀏覽器的開(kāi)發(fā)者工具進(jìn)行抓包。進(jìn)入Noetwork→XHR,用鼠標(biāo)在網(wǎng)頁(yè)向下拉取,得到我們需要的數(shù)據(jù)包:

          得到的真實(shí)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有很多不必要的參數(shù),大家可以在瀏覽器中自行刪減。兩條url的區(qū)別在于后面的offset參數(shù),首條url的offset參數(shù)為0,第二條為5,offset是以公差為5遞增;網(wǎng)頁(yè)數(shù)據(jù)格式為json格式。


          實(shí)戰(zhàn)代碼

          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'] )) # 回答時(shí)間
                  voteup_count = list_['voteup_count']  # 贊同數(shù)
                  comment_count = list_['comment_count']  # 底下評(píng)論數(shù)
                  content = list_['content']  # 回答內(nèi)容
                  content = ''.join(re.findall("[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b\u4e00-\u9fa5]", content))  # 正則表達(dá)式提取
                  print(name, id_, created_time, comment_count, content, sep='|')
                  dataFrame = pd.DataFrame(
                      {'知乎作者': [name], '作者id': [id_], '回答時(shí)間': [created_time], '贊同數(shù)': [voteup_count], '底下評(píng)論數(shù)': [comment_count],
                       '回答內(nèi)容': [content]})
                  df = pd.concat([df, dataFrame])
              time.sleep(random.uniform(2, 3))
          df.to_csv('知乎回答.csv', encoding='utf-8', index=False)
          print(df.shape)
          

          結(jié)果展示:


          微博

          本文以爬取微博熱搜《霍尊手寫道歉信》為例,講解如何爬取微博評(píng)論!

          網(wǎng)頁(yè)地址:

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


          分析網(wǎng)頁(yè)

          微博評(píng)論是動(dòng)態(tài)加載的,進(jìn)入瀏覽器的開(kāi)發(fā)者工具后,在網(wǎng)頁(yè)上向下拉取會(huì)得到我們需要的數(shù)據(jù)包:

          得到真實(shí)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區(qū)別很明顯,首條url是沒(méi)有參數(shù)max_id的,第二條開(kāi)始max_id才出現(xiàn),而max_id其實(shí)是前一條數(shù)據(jù)包中的max_id:

          但有個(gè)需要注意的是參數(shù)max_id_type,它其實(shí)也是會(huì)變化的,所以我們需要從數(shù)據(jù)包中獲取max_id_type:

          實(shí)戰(zhàn)代碼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)
          # 微博爬取大概幾十頁(yè)會(huì)封賬號(hào)的,而通過(guò)不斷的更新cookies,會(huì)讓爬蟲更持久點(diǎn)...
          cookie = [cookie.value for cookie in resposen.cookies] # 用列表推導(dǎo)式生成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'] # 點(diǎn)贊數(shù)
          created_at = i['created_at'] # 時(shí)間
          text = re.sub(r'<[^>]*>', '', i['text']) # 評(píng)論
          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)

          結(jié)果展示:

          以上便是今天的全部?jī)?nèi)容了,如果你喜歡今天的內(nèi)容,希望你能在下方點(diǎn)個(gè)贊和在看支持我,謝謝!

          聊時(shí)看了下斗魚直播,突然靈感乍現(xiàn),想做出這個(gè)彈幕效果,于是有了下文

          這里還是要說(shuō)一下我的前端學(xué)習(xí)群:594959296,從我一個(gè)到現(xiàn)在的都是看我每一篇文章來(lái)的,可以說(shuō)都是我們大前端的學(xué)霸啊,不定期分享干貨。想學(xué)到東西的都可以來(lái),歡迎初學(xué)和進(jìn)階中的小伙伴

          說(shuō)明:彈幕分兩種:

          1、像優(yōu)酷、愛(ài)奇藝等,記錄用戶發(fā)送彈幕時(shí)該視頻播放的位置,其他人播放這個(gè)視頻時(shí)到了這個(gè)點(diǎn)就顯示彈幕。

          2、像斗魚、熊貓這樣的直播網(wǎng)站,用戶發(fā)送彈幕直接顯示在屏幕上,以后不需要再顯示了。

          我們就來(lái)做第二種!!!

          實(shí)現(xiàn)效果

          項(xiàng)目分析

          1、獲取用戶輸入信息;

          2、在頁(yè)面中創(chuàng)建一個(gè) <span></span>來(lái)放獲取到的文本,并添加一些樣式(字體大小、顏色等);

          3、給這個(gè) span添加一個(gè)從右向左移動(dòng)的動(dòng)畫;

          4、動(dòng)畫結(jié)束后,移除這個(gè) <span>

          思路很清楚了,就來(lái)動(dòng)手實(shí)現(xiàn)一下。。

          布局

          頁(yè)面中要有一個(gè)輸入框讓用戶輸入信息,還要一個(gè)盒子用來(lái)顯示彈幕。為了美觀,我多加了一些東西。

          1、#dm 是顯示彈幕的區(qū)域,input供用戶輸入信息(其中 placeholder 屬性規(guī)定輸入框中默認(rèn)顯示內(nèi)容)。

          2、其它元素都是為了美觀和布局。

          樣式

          跟以前一樣,先給出代碼然后再講解。

          1、首先還是格式化瀏覽器默認(rèn)的 marginpaddingoverflow屬性設(shè)置超出屏幕的部分隱藏,這樣就不會(huì)出現(xiàn)下拉和水平的滾動(dòng)條了。

          2、#dm用來(lái)顯示彈幕,我們把它的高度設(shè)置為 90vhvh 是 CSS3 中新增的長(zhǎng)度單位,表示相對(duì)于視口的高度。視口高度被均分為100單位的vh,90vh 就表示當(dāng)前瀏覽器可視區(qū)域高度的 90%。(vw 就是視口寬度)

          3、#dm span是每條彈幕的樣式。你可以修改為你喜歡的樣子,不過(guò)一定要設(shè)置 position
          定位屬性。

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

          5、然后就是 輸入框、按鈕的樣式了,沒(méi)什么好說(shuō)的,應(yīng)該都能看懂。

          JS部分

          先看看代碼吧。。

          1、說(shuō)明:向頁(yè)面添加彈幕使用 addBarrage() 函數(shù),添加動(dòng)畫用 move()函數(shù)。(把一種功能封裝為一個(gè)函數(shù)是一個(gè)好習(xí)慣)

          2、首先思考一下彈幕的觸發(fā)事件。應(yīng)該有兩個(gè):點(diǎn)擊“發(fā)射”按鈕、按下 Enter按鍵。所以分別監(jiān)聽(tīng)點(diǎn)擊 和 鍵盤事件。

          3、然后就要思考怎么向頁(yè)面添加彈幕了?

          先得到用戶輸入的信息

          然后在頁(yè)面中創(chuàng)建一個(gè)

          <span></span>

          把得到的文本放進(jìn)去

          添加到 #dm這個(gè)盒子中:

          • 這樣就可以了嗎?? 當(dāng)然不是的。。

          4、每條彈幕應(yīng)該有不同的顏色,這樣才炫酷。思路就是把預(yù)先的顏色放進(jìn)一個(gè)數(shù)組,使用的時(shí)候用隨機(jī)的下標(biāo),這樣就獲得了隨機(jī)顏色。(借鑒斗魚的 7 中顏色)

          這樣每條彈幕就有不同的顏色了。

          5、每條彈幕在頁(yè)面上還要有不同的位置(高度),也就是不同的 top 值。我的想法是,雖然要有不同的 top ,但也不能太隨意。就像下面這樣:(一行是一行的)

          我的思路是:先判斷頁(yè)面可以放多少行?

          然后計(jì)算可以有的 top 值:(加 10 是為了不至于緊挨著屏幕頂部,最后的 -1 是為了不至于太靠下 )

          你可以自己理解一下我的這種計(jì)算方式。。

          然后應(yīng)用給

          <span>

          即可:

          6、對(duì)了,彈幕應(yīng)該添加到頁(yè)面的什么位置呢?因?yàn)閺椖灰獜挠彝笠苿?dòng),所以應(yīng)該添加到屏幕的右側(cè),left 值為瀏覽器頁(yè)面的寬度。這時(shí)候就知道為什么前面要設(shè)置 body 的 overflow: hidden;了吧!!

          動(dòng)畫

          彈幕(也就是<span>)被添加到頁(yè)面中了,我們要讓他動(dòng)起來(lái)。思路就是寫一個(gè)函數(shù),減少 <span>

          的 left 值。每隔幾毫秒執(zhí)行一次這個(gè)函數(shù),我們看起來(lái)這個(gè) <span>元素就動(dòng)起來(lái)了。

          7、但是有幾個(gè)問(wèn)題:

          • 頁(yè)面中有多少個(gè)彈幕(<span>)?因?yàn)轫?yè)面中只有 彈幕 使用的是 <span> 標(biāo)簽,所以這樣就可以獲取所有 <span>的 nodeList(類似數(shù)組但不是數(shù)組,可以使用下標(biāo)索引訪問(wèn)):

          怎樣記錄每條彈幕的 left 值?獲取到所有的 <span>后,用一個(gè) for 循環(huán)將每一個(gè) <span> 的 left 值放進(jìn)一個(gè)數(shù)組:

          • 這樣 arr[] 就保存了所有彈幕的 left 值。

          8、接下來(lái)我們就逐個(gè)減少每條彈幕的 left 值:

          9、最后判斷如果彈幕已經(jīng)移出了頁(yè)面的左邊,就把這條彈幕刪除了吧。

          我們可以在開(kāi)發(fā)工具中看一下這個(gè)過(guò)程:

          彈幕的 left 一直減小,移出頁(yè)面后 #dm中就沒(méi)有這個(gè) <span>了。

          10、最后處理一個(gè)小細(xì)節(jié)吧!當(dāng)我們點(diǎn)擊按鈕或按回車后,輸入框中的文字會(huì)保留,影響我們下次輸入,所以只要獲取到了用戶輸入的內(nèi)容,就把輸入框清空吧!

          到此,彈幕就實(shí)現(xiàn)了。。。

          學(xué)習(xí)javascript也是有門檻的,就是你的html和css至少還比較熟練,您不能連html這東東是干啥的都不知道就開(kāi)始學(xué)javascript了,學(xué)乘除前,學(xué)好加減法總是有益無(wú)害的。

          如果想看到更加系統(tǒng)的文章和學(xué)習(xí)方法經(jīng)驗(yàn)可以關(guān)注我的微信公眾號(hào):‘web前端課程’關(guān)注后回復(fù)‘給我資料’可以領(lǐng)取一套完整的學(xué)習(xí)視頻

          著視頻網(wǎng)站和直播網(wǎng)站的興起,彈幕的功能算是一個(gè)核心元素,彈幕最初是由日本視頻網(wǎng)站Niconico引入的,國(guó)內(nèi)除了在Bilibili和AcFun等彈幕視頻網(wǎng)站中使用之外,其他主流視頻網(wǎng)站中的視頻播放器也支持彈幕,對(duì)于視頻網(wǎng)站和直播網(wǎng)站來(lái)說(shuō),彈幕的交互實(shí)時(shí)性更高,更加受歡迎。

          本文重點(diǎn)介紹一下彈幕的基本原理,以及JavaScript和HTML的代碼實(shí)現(xiàn)方式,最后分享一個(gè)彈幕的開(kāi)源庫(kù)。

          關(guān)于視頻網(wǎng)站建設(shè),可以參考我們之前的通過(guò)Drupal構(gòu)建自建視頻課程平臺(tái)。

          彈幕基礎(chǔ)知識(shí)

          單條彈幕的屬性

          1. 模式:滾動(dòng)彈幕、逆向彈幕、頂部彈幕、底部彈幕。
          2. 基本屬性:正文、出現(xiàn)時(shí)間、持續(xù)時(shí)間、字體、字號(hào)、顏色等樣式。
          3. 容器:塊級(jí)元素,一般是與視頻大小相同的區(qū)域。

          彈幕的特性:

          1. 空間的獨(dú)立性:雖然在同一個(gè)元素內(nèi)展現(xiàn),但空間計(jì)算上每種彈幕模式處于不同層,每種模式也可以有多層,每層內(nèi)的彈幕占位不會(huì)重疊。
          2. 渲染的確定性:如果彈幕容器和渲染的列表固定不變,在沒(méi)有用戶交互(比如暫停單條彈幕)的情況下,每次渲染每條彈幕所出現(xiàn)的位置和順序都是固定的。
          3. 各模式的統(tǒng)一性:每種模式的彈幕的生存時(shí)間保持一致。

          彈幕的基本模式:

          1. 滾動(dòng)彈幕:自右向左勻速滾動(dòng)過(guò)屏幕的彈幕,以自上而下的優(yōu)先度展示。
          2. 逆向彈幕:自左向右勻速滾動(dòng)過(guò)屏幕的彈幕,以自上而下的優(yōu)先度展示,逆向彈幕是相對(duì)于滾動(dòng)彈幕的相反方向滾動(dòng)的彈幕展示模式。
          3. 頂部彈幕:自上而下靜止居中的彈幕、以自上而下的優(yōu)先度展示。
          4. 底部彈幕:自下而上靜止居中的彈幕、以自下而上的優(yōu)先度展示。

          彈幕的使用場(chǎng)景:

          1. 點(diǎn)播視頻互動(dòng),把評(píng)論搬到彈幕上來(lái)
          2. 直播互動(dòng),直播視頻中,可以直接通過(guò)彈幕討論、互動(dòng)
          3. 視頻內(nèi)容增強(qiáng),比如對(duì)視頻中某些內(nèi)容進(jìn)行解釋,描述等
          4. 標(biāo)記視頻亮點(diǎn),對(duì)視頻中的高亮進(jìn)行提示等
          5. 網(wǎng)頁(yè)互動(dòng),比如彈幕可以點(diǎn)贊,回復(fù)等
          6. 互動(dòng)上墻,大屏的投票可以通過(guò)彈幕來(lái)實(shí)現(xiàn)交互大墻
          7. 蒙版彈幕,可以對(duì)某個(gè)地方不想讓用戶看到,可以通過(guò)彈幕掩蓋
          8. 非文字彈幕,比如表情、紅包等等交互效果

          彈幕的實(shí)現(xiàn)原理

          彈幕本身不是對(duì)視頻的操作,其實(shí)是給視頻上面加DIV等DOM元素,也就是給視頻上面加層,然后展示我們想要的內(nèi)容即可,目前彈幕的主要實(shí)現(xiàn)有兩種方式:

          1. Canvas
          2. HTML+CSS

          首先,這種我們自然想到用Canvas,使用Canvas能很方便地繪制動(dòng)畫,并且獲得非常不錯(cuò)的性能,目前前端不少動(dòng)畫都是通過(guò)Canvas去做。不過(guò)對(duì)于基于Canvas的動(dòng)畫而言,最大的問(wèn)題就是“交互性”上面。

          如果用HTML+CSS的方式實(shí)現(xiàn),我們可以很簡(jiǎn)單地通過(guò)監(jiān)聽(tīng)原生DOM事件去知道哪一條彈幕與用戶鼠標(biāo)發(fā)生了交互。但是通過(guò)Canvas,我們只能通過(guò)監(jiān)聽(tīng)畫布的事件,然后做一堆遍歷計(jì)算坐標(biāo)的騷操作去確定是哪一條彈幕。從鵝廠的視頻網(wǎng)站可以看到,他們的彈幕是可交互的,所以他們使用了HTML+CSS的實(shí)現(xiàn)方式;而B站的彈幕是非交互的,它提供Canvas和HTML+CSS可選,默認(rèn)是前者。

          雖然功能性上兩者的實(shí)現(xiàn)會(huì)有點(diǎn)差異,但彈幕的基本原理都是一樣。

          所以彈幕本身就是對(duì)DIV元素的操作,DIV層一般在Video層上面,類似下面的圖,不過(guò)也有放在Video層下面,彈幕層在底下,詳情參考這篇文章的介紹 https://github.com/logcas/a-barrage

          多層的彈幕實(shí)現(xiàn)

          彈幕實(shí)現(xiàn)的技術(shù)細(xì)節(jié)

          我們先分析一下文檔管理系統(tǒng)的基本需求以及功能點(diǎn),

          從上圖可以看到,彈幕是很清晰地分成了一行一行,我把它們稱為“軌道”。每一個(gè)彈幕都只在軌道上從右往左移動(dòng),不會(huì)越界。那么,要實(shí)現(xiàn)彈幕功能,首先我們必須把彈幕分成若干個(gè)軌道,然后再在合適的時(shí)間把彈幕“塞”進(jìn)去讓它平移。

          每一個(gè)軌道會(huì)有兩個(gè)屬性:

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

          barrages為一個(gè)彈幕數(shù)組,offset則是已占據(jù)的寬度。offset用于滾動(dòng)彈幕時(shí),彈幕軌道添加彈幕前判斷最佳軌道;當(dāng)彈幕類型時(shí)固定時(shí)無(wú)作用。barrages存放當(dāng)前軌道上可現(xiàn)實(shí)的彈幕實(shí)例。

          每一個(gè)軌道實(shí)例管理自己軌道中的數(shù)組,主要進(jìn)行進(jìn)行增、刪、重置以及更新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
          } }

          拿來(lái)即用-開(kāi)源的方案

          在知名的男性交友網(wǎng)站github上,已經(jīng)有一款很好用的彈幕開(kāi)源解決方案,并且是國(guó)產(chǎn)的,叫abarrage,同時(shí)實(shí)現(xiàn)了Canvas和HTML+CSS的模式,要用的話直接在github把源代碼擼下來(lái),并且有在線的體驗(yàn)地址,效果非常不錯(cuò)。

          A-Barrage 同時(shí)支持Canvas渲染和HTML+CSS的渲染模式,你可以根據(jù)實(shí)際情況采用不同的渲染引擎進(jìn)行彈幕的渲染。其中,Canvas是非交互式渲染,也就是說(shuō),采用Canvas渲染的彈幕并不會(huì)有任何的交互操作,純展示性質(zhì);HTML+CSS是交互式渲染,如果你的網(wǎng)站允許用戶與彈幕之間進(jìn)行一些交互(如點(diǎn)贊、回復(fù)等),那么可以采用HTML+CSS的渲染模式,它會(huì)結(jié)合瀏覽器的DOM事件進(jìn)行響應(yīng)。

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

          測(cè)試地址: https://logcas.github.io/a-barrage/example/css3.html

          網(wǎng)站首頁(yè)

          發(fā)彈幕截圖


          主站蜘蛛池模板: 久久人妻内射无码一区三区 | 精品无码人妻一区二区三区不卡 | 蜜桃臀无码内射一区二区三区 | 成人精品一区二区不卡视频| 无码精品人妻一区二区三区免费| 亚洲欧美日韩中文字幕在线一区| 激情亚洲一区国产精品| 久久久久一区二区三区| 日本亚洲国产一区二区三区| 久久久久女教师免费一区| 中文字幕亚洲一区二区va在线| 国产成人无码一区二区三区在线 | 国产精品一区二区久久国产| 中文字幕一区二区区免| 亚洲AV无码一区二区三区DV| 久久久综合亚洲色一区二区三区| 无码少妇一区二区三区浪潮AV| 中文字幕在线一区二区在线| av无码免费一区二区三区| 无码少妇丰满熟妇一区二区| 国产精品亚洲一区二区三区| 国产一区二区三区视频在线观看| 一区二区不卡久久精品| 国产免费一区二区三区不卡| 久久久91精品国产一区二区| 无码人妻精品一区二区蜜桃网站 | 亚洲人成网站18禁止一区| 在线精品亚洲一区二区小说| 久久一本一区二区三区| 亚洲一区二区三区在线观看蜜桃 | 性色A码一区二区三区天美传媒| 免费播放一区二区三区| 日韩精品免费一区二区三区| 国产美女口爆吞精一区二区| 少妇激情AV一区二区三区| 久久精品成人一区二区三区| 国产精品福利区一区二区三区四区| av无码人妻一区二区三区牛牛| 无码人妻精品一区二区三区蜜桃| 波多野结衣精品一区二区三区| 三上悠亚国产精品一区|