B站視頻下載(VideoHelper)

?

繼續上次的知乎爬蟲, 這次開始了嗶哩嗶哩的爬蟲實踐;

?

首先介紹下如何下載吧: VideoHelper 里面有三種方式下載b站視頻。

?

同樣的流程, 還是先抓包,分析參數,尋找參數(包括之前的請求包和頁面源碼),找出視頻真實地址, 然后在模擬。

?

抓包是注意幾個參數:

aid:每個視頻都會有對應的 aid, 包括ep類型的;

cid:彈幕的id, 通過相關api可由cid找到對應的資源列表

ep_id: 就是地址欄上顯示的ep類型的id了

?

這里詳細的流程我就不介紹了(其實我是來宣傳VideoHelper 的,目前還支持知乎等網站視頻, 歡迎star。滑稽‘(*>﹏<*))

?

其中需要注意的是模擬發包是有些請求頭是不能掉的, user-agent我就不說了, 不如Referer;

?

另外我發現網上目前僅存的b站的視頻爬蟲好像大多不支持ep類型的, 不過我那個最近測試是支持了的, 但是vip專屬的也是會直接報錯;

?

另外注明:該項目參考了you-get的部分api

?

?

下面老規矩貼上主要源碼:

package website;import bean.BilibiliBean;
import bean.VideoBean;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import util.DownloadUtil;
import util.HttpUtil;
import util.MD5Encoder;import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;import static util.PrintUtil.println;/*** 嗶哩嗶哩: https://www.bilibili.com/** @author Asche* @date 2018-10-20 18:02:29* @github https://github.com/asche910*/
public class Bilibili extends BaseSite {// from aid to cidsprivate String ApiGetList = "https://www.bilibili.com/widget/getPageList?aid=";private String AvApi = "http://interface.bilibili.com/v2/playurl?";private String EpApi = "http://bangumi.bilibili.com/player/web_api/playurl?";private String SEC_1 = "94aba54af9065f71de72f5508f1cd42e";private String SEC_2 = "9b288147e5474dd2aa67085f716c560d";// qualityprivate final int RESOLUTION_1080 = 112;private final int RESOLUTION_720 = 64;private final int RESOLUTION_480 = 32;private final int RESOLUTION_360 = 15;private int quality = RESOLUTION_1080;//    private List<String> urls = new ArrayList<>();private String playUrl;private String fileName;private int timeLength;private int fileSize = 0;private int aid;private int cid;// 視頻類型private final int AV_VIDEO = 1;private final int EP_VIDEO = 2;private final int SS_VIDEO = 3;private int type = AV_VIDEO;private boolean isSupported;// ep的關聯系列private List<BilibiliBean> serialList = new ArrayList<>();// 是否已經解析private boolean isResolved;public Bilibili() {}/*** 先獲取信息再決定是否下載* @param playUrl* @param outputDir*/public Bilibili(String playUrl, String outputDir) {if (!isResolved) {this.playUrl = playUrl;String[] strs = playUrl.split("/");for (String str : strs) {if (str.matches("av\\d{4,}")) {aid = Integer.parseInt(str.substring(2));isSupported = true;break;} else if(str.matches("ep\\d{4,}")){type = EP_VIDEO;isSupported = true;break;} else if(str.matches("ss\\d{4,}")){type = SS_VIDEO;isSupported = true;break;}}try {switch (type) {case SS_VIDEO:case EP_VIDEO:initEp();String epApi = generateEpApi(EpApi, cid, quality);println(epApi);parseEpApiResponse(epApi);break;case AV_VIDEO:initAv();String avApi = generateAvApi(AvApi, cid, quality);println(avApi);parseAvApiResponse(avApi);break;}} catch (Exception e) {e.printStackTrace();}isResolved = true;}}@Overridepublic void downloadByUrl(String playUrl, String outputDir) {println("Bilibili start: ");this.playUrl = playUrl;String[] strs = playUrl.split("/");for (String str : strs) {if (str.matches("av\\d{4,}")) {aid = Integer.parseInt(str.substring(2));isSupported = true;break;} else if(str.matches("ep\\d{4,}")){type = EP_VIDEO;isSupported = true;break;} else if(str.matches("ss\\d{4,}")){type = SS_VIDEO;isSupported = true;break;}}try {if (!isResolved) {switch (type) {case SS_VIDEO:case EP_VIDEO:initEp();String epApi = generateEpApi(EpApi, cid, quality);println(epApi);parseEpApiResponse(epApi);break;case AV_VIDEO:initAv();String avApi = generateAvApi(AvApi, cid, quality);println(avApi);parseAvApiResponse(avApi);break;}isResolved = true;}println("# Title: " + fileName);println("     -TimeLength: " + timeLength / 1000 / 60 + ":" + String.format("%02d", timeLength / 1000 % 60));println("     -File Size: " + fileSize / 1024 / 1024 + " M");download(urls, outputDir);} catch (Exception e) {e.printStackTrace();}}/*** 內部下載入口** @param videoSrcs* @param outputDir*/@Overridepublic void download(List<String> videoSrcs, String outputDir) throws IOException {Map<String, List<String>> headerMap = new HashMap<>();// 缺失Referer會導致453錯誤headerMap.put("Referer", Collections.singletonList("http://interface.bilibili.com/v2/playurl?appkey=84956560bc028eb7&cid=59389212&otype=json&qn=3&quality=3&type=&sign=4c841d687bb7e479e3111428c6a4d3b8"));int index = 0;for (String src : videoSrcs) {println("Download: " + ++index + "/" + videoSrcs.size());String fileDir;if (videoSrcs.size() == 1) {fileDir = outputDir + File.separatorChar + fileName.replaceAll("[/|\\\\]", "") + ".flv";} else {fileDir = outputDir + File.separatorChar + fileName.replaceAll("[/|\\\\]", "") + "【" + index + "】.flv";}DownloadUtil.downloadVideo(src, fileDir, headerMap);}println("Download: All Done!");}@Overridepublic VideoBean getInfo() {VideoBean bean = new VideoBean();bean.setTitle(fileName);bean.setTimeLength(timeLength / 1000 / 60 + ":" + String.format("%02d", timeLength / 1000 % 60));bean.setSize(fileSize / 1024 / 1024);return bean;}public List<BilibiliBean> getSerialList(){return serialList;}/*** cid, fileName** @throws IOException*/private void initAv() throws IOException {String result = HttpUtil.getResponseContent(ApiGetList + aid);JSONObject jb = (JSONObject) new JSONArray(result).get(0);cid = jb.getInt("cid");Document doc = Jsoup.connect(playUrl).get();Element ele = doc.selectFirst("div[id=viewbox_report]").selectFirst("h1");if (ele.hasAttr("title"))fileName = ele.attr("title");}/*** cid, fileName and related eps** @throws IOException*/private void initEp() throws IOException {Document doc = Jsoup.connect(playUrl).get();Element ele = doc.body().child(2);String preResult = ele.toString();// println(preResult);
String result = preResult.substring(preResult.indexOf("__=") + 3, preResult.indexOf(";(function()"));// println(result);
JSONObject object = new JSONObject(result);JSONObject curEpInfo = object.getJSONObject("epInfo");fileName = object.getJSONObject("mediaInfo").getString("title");cid = curEpInfo.getInt("cid");JSONArray ja = object.getJSONArray("epList");for (Object obj : ja) {JSONObject epObject = (JSONObject) obj;int aid = epObject.getInt("aid");int cid = epObject.getInt("cid");int duration = epObject.getInt("duration");int epId = epObject.getInt("ep_id");String index = epObject.getString("index");String indexTitle = epObject.getString("index_title");BilibiliBean bean = new BilibiliBean(aid, cid, duration, epId, index, indexTitle);serialList.add(bean);println(bean.toString());}}/*** timeLength, fileSize, urls** @param avReqApi* @throws IOException*/private void parseAvApiResponse(String avReqApi) throws IOException {String result = HttpUtil.getResponseContent(avReqApi);// println(result);
JSONObject jsonObject = new JSONObject(result);timeLength = jsonObject.getInt("timelength");JSONArray ja = jsonObject.getJSONArray("durl");Iterator<Object> iterator = ja.iterator();while (iterator.hasNext()) {JSONObject jb = (JSONObject) iterator.next();String videoSrc = jb.getString("url");urls.add(videoSrc);fileSize += jb.getInt("size");}}/*** timeLength, fileSize, urls** @param epReqApi* @throws IOException* @throws DocumentException*/private void parseEpApiResponse(String epReqApi) throws IOException, DocumentException {String response = HttpUtil.getResponseContent(epReqApi);SAXReader reader = new SAXReader();org.dom4j.Element rootElement = reader.read(new ByteArrayInputStream(response.getBytes("utf-8"))).getRootElement();timeLength = Integer.parseInt(rootElement.element("timelength").getText().trim());List<org.dom4j.Element> elements = rootElement.elements("durl");for (org.dom4j.Element ele : elements) {int curSize = Integer.parseInt(ele.element("size").getText());fileSize += curSize;String url = ele.element("url").getText();urls.add(url);}println(fileName + ": " + fileSize / 1024 / 1024 + "M");}/*** 生成av類型視頻下載信息的api請求鏈接** @param url* @param cid* @param quality* @return*/private String generateAvApi(String url, int cid, int quality) {String paramStr = String.format("appkey=84956560bc028eb7&cid=%d&otype=json&qn=%d&quality=%d&type=", cid, quality, quality);try {String checkSum = MD5Encoder.md5(paramStr + SEC_1).toLowerCase();return url + paramStr + "&sign=" + checkSum;} catch (Exception e) {e.printStackTrace();}return null;}/*** 生成ep類型視頻下載信息的api請求鏈接** @param url* @param cid* @param quality* @return*/private String generateEpApi(String url, int cid, int quality) {String paramStr = String.format("cid=%d&module=bangumi&player=1&quality=%d&ts=%s",cid, quality, System.currentTimeMillis() / 1000 + "");try {String checkSum = MD5Encoder.md5(paramStr + SEC_2).toLowerCase();return url + paramStr + "&sign=" + checkSum;} catch (Exception e) {e.printStackTrace();}return null;}
}

?

?

?完整代碼位于:

?https://github.com/asche910/VideoHelper?

?

轉載于:https://www.cnblogs.com/asche/p/9863870.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537602.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537602.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537602.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在職場遇到一個總是說話帶刺的同事怎么相處?

網友&#xff1a; 帶刺的人如果總是撲空&#xff0c;或者總是戳進一團棉花&#xff0c;你說他&#xff0c;她還能堅持多久。職場重心是工作&#xff0c;是做事&#xff0c;不要為這些無聊的人事太費心&#xff0c;刺猬帶著刺到處轉&#xff0c;最后結果不會好的。 網友&#xf…

postgresql 怎么讀_大數據采集和抽取怎么做?這篇文章終于說明白了!

本文來源于公眾號【胖滾豬學編程】&#xff0c;轉載請注明出處&#xff01; 關于數據中臺的概念和架構&#xff0c;我們在大白話 六問數據中臺和數據中臺全景架構及模塊解析&#xff01;一文入門中臺架構師&#xff01;兩篇文章中都說明白了。從這一篇文章開始分享中臺落地實戰…

ZooKeeper應用——解決分布式系統單點故障

1.單點故障問題什么是分布式系統中的單點故障&#xff1a;通常分布式系統采用主從模式&#xff0c;就是一個主控機連接多個處理節點。主節點負責分發任務&#xff0c;從節點負責處理任務&#xff0c;當我們的主節點發生故障時&#xff0c;那么整個系統就都癱瘓了&#xff0c;那…

老板思維:有支出必須有對應的收入

項目經理 要時刻關注“有支出必須有對應的收入”。 當手頭的項目產生支出時&#xff0c;必須要問自己&#xff0c;從哪里收入來填補這個支出&#xff0c;如果沒有可收入的&#xff0c;那就是公司虧錢了。如果一定要虧錢&#xff0c;那可否產生收入之外的效益。 甲方提了新需求…

生活之難:生活到底難在哪里

生活之難&#xff1a;生活到底難在哪里 一、總結 一句話總結&#xff1a;難在天性&#xff0c;難在競爭&#xff0c;難在積累&#xff0c;難在追求&#xff0c;難在自己 難在天性 人的天性就是好吃懶做好玩不動腦的&#xff0c;但是生存的壓力&#xff08;食物&#xff0c;房子…

解決org.springframework.web.multipart.MaxUploadSizeExceededException報錯問題

在springboot中接收到上傳的文件時候&#xff0c;出現了這種錯誤 org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.Fil…

python中元祖 字典 列表的區別_Python中元祖,列表,字典的區別

原博文 2016-08-16 15:25 ? Python中有3種內建的數據結構&#xff1a;列表、元祖和字典&#xff1a; 1.列表 list是處理一組有序項目的數據結構&#xff0c;即你可以在一個列表中存儲一個序列的項目。 列表中的項目應該包括在方括號中&#xff0c;這樣Python就知道你是指明一個…

react 16.6 懶加載 Lazy 嘗鮮

react 16.6 發布了新的功能 lazy &#xff0c;和一個組件 Suspense 下面我們看一下他的用法 首先我們先創建兩個組件 LazyTest.1 和 LazyTest.2&#xff0c;內容相同 import React, { Component } from reactexport default class LazyTest extends Component{render(){return …

Intellij IDEA展示類中的方法樹形結構

在intellij Idea中叫Structure&#xff08;結構體&#xff09;&#xff0c;如下圖&#xff1b; 也可以直接AltF7快捷鍵&#xff0c;這樣默認會把Structure顯示在屏幕下方&#xff0c;如下圖操作就可以移動到右側。 效果如下&#xff1a;

時間計算題100道_2019四校及分校自招開放日情況匯總(含時間安排、考試內容難度、到場人數等)...

點擊上方“上海初升高”&#xff0c;選擇“星標公眾號”回復“加群”就能加入上萬家長信賴的升學群受到疫情的影響&#xff0c;今年各市重點的自招開放日報名遲遲沒有提上日程。但不管怎樣&#xff0c;自招應該是不會取消的&#xff0c;以下是去年四校及分校自招開放日情況匯總…

linux I/O 棧 預習(上)

二、預習 在我們進去device mapper的dm dedup學習之前&#xff0c;我們先要預習一下&#xff0c;什么是device mapper&#xff0c;和為什么device mapper能夠做塊重刪。 1、device mapper照舊&#xff0c;我們先看一下維基百科對它的介紹。The device mapper is a framework pr…

java.util.concurrent.RejectedExecutionException

報錯日志 java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask12e2cb93 rejected from java.util.concurrent.ThreadPoolExecutor6ecd396b[Running, pool size 10, active threads 10, queued tasks 200, completed tasks 0] 原因&am…

springboot controller 訪問 404

兩種解決方式&#xff1a; 1、因為SpringBoot的項目啟動類&#xff0c;會只掃描該包下的文件或者改包下所有子包內的文件&#xff0c;只要你把該文件移動到啟動類的相同目錄報下就可以。 2、就是在該類上面加者在啟動類上添加注解 ComponentScan(basePackages {"com.boota…

fegin需要實現類_【第24條】靜態成員類優于非靜態成員類

第24條靜態成員類優于非靜態成員類嵌套類(nested class)是指定義在另一個類的內部的類。嵌套類存在的目的應該只是為它的外圍類(enclosing class)提供服務。如果嵌套類將來可能會用于其他的某個環境中&#xff0c;它就應該是頂層類(top-level class)。嵌套類有四種&#xff1a;…

.h與.cpp

本質上沒什么區別。 cpp:c plus plus,就表示為c原文件。 .h文件實現的功能是聲明.cpp文件中需要使用的變量、函數及宏定義等。 .h文件就像是一個接口&#xff0c;具體的實現可以在.cpp中&#xff0c;也可以在.h中。轉載于:https://www.cnblogs.com/keguniang/p/9877581.html

Springboot 多線程的使用

直接上代碼 線程配置類 package zengmg.nbpi.com.thread;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework…

vlookup函數練習_為什么職場要學excel函數?看這個案例演示:自動計算快遞價格...

在上一篇文章里面&#xff0c;我們講了如何整理完成一個規范化的表格&#xff0c;以便于下一步的函數計算。最初的信息內容如圖所示。經過整理&#xff0c;我們得到了表2這樣的規范化表格。現在&#xff0c;我們就通過表2來實現快遞費用自動計算&#xff0c;最終實現圖中這樣的…

%@ taglib prefix=c uri=http://java.sun.com/jsp/jstl/core %會報錯

有些時候&#xff0c;<% taglib prefix"c" uri"http://java.sun.com/jsp/jstl/core" %>會報錯&#xff0c;錯誤提示為&#xff1a; Can not find the tag library descriptor for "http://java.sun.com/jsp/jstl/core" 主要原因是缺少 jst…

藍牙芯片排行_7月TWS 全球品牌出貨量排行榜出爐

數據鑄造影響力撰文 / 旭日大數據編輯 / 柏序旭日大數據公布了2020年7月全球TWS品牌銷量排行榜&#xff0c;與上期數據相比&#xff0c;全球品牌七排名TOP20汰換率為15%&#xff0c;其中DOSS&#xff0c;萬魔、BOSE跌出前20&#xff0c;廣州由我&#xff0c;Tzumi登榜&…

project 打印的時候上面的表格和下面的圖例中間有個很大的空白,這塊東西怎么能去掉呢?

“打印預覽”的“頁面設置”里面&#xff0c;“頁面”選項卡里的“縮放”項設為1頁寬&#xff0c;1頁高就可以了&#xff0c; 當然如果你的任務項比較少的話&#xff0c;怎么調也不容易去掉下面的空白 操作如下圖&#xff08;下圖的任務太少&#xff0c;去不掉空白的&#xf…