<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      B站視頻批量下載工具

      工具是另一種財富。


      概述

      在 B 站遇到好的視頻總想下載下來。不過很多瀏覽器插件只能每次下一個,批量下載才省力。有了 AI 的輔助,其實編寫小工具已經(jīng)非常方便了。 不過我們還是需要對生成的工具有要求的。

      (1) 命令行選項;(2)可復(fù)用。(3)可組合

      這其實是從 Linux 系統(tǒng)得來的啟示。 命令行,就是可以通過命令行靈活定制行為,可復(fù)用,就是每個工具是小的但是可以復(fù)用的,當(dāng)需要時不必重寫或修改;可組合,是說命令行可以組合起來實現(xiàn)不同的工具。當(dāng)我們把任務(wù)仔細(xì)拆分之后,就能更容易達(dá)到這個目標(biāo)。

      要批量下載視頻,需要三個工具和一個數(shù)據(jù)源:

      (1)數(shù)據(jù)解析

      • JSON PATH 語法
      • HTML select 語法

      (2)數(shù)據(jù)拼接

      • URL 拼接

      (3)下載工具

      • you-get
      • ffmpeg

      (4) 數(shù)據(jù)源

      • HTML 網(wǎng)頁
      • API 返回的 JSON 數(shù)據(jù)。

      本文主要講述如何解析 API 返回的 JSON 數(shù)據(jù),拼接成所需的 URL,然后用下載工具下載。

      工具開發(fā)

      數(shù)據(jù)解析

      比如說,要下載個人收藏的視頻。

      打開B站個人收藏,查看哪個URL 返回了頁面數(shù)據(jù)。

      API 返回的 JSON 數(shù)據(jù)如下:這里面 bvid 就是B站視頻的標(biāo)識,加上前綴即是可訪問的B站視頻地址。

      {
      	"code": 0,
      	"message": "0",
      	"ttl": 1,
      	"data": {
      		"info": {
      			"id": 3133010051,
      			"fid": 31330100,
      			"mid": 183260251,
      			"attr": 22,
      			"title": "文藝",
      			"cover": "http://i2.hdslb.com/bfs/archive/843055b47a4cc46e80047ab9087b4bea06491f47.jpg",
      			"upper": {
      				"mid": 183260251,
      				"name": "琴水玉",
      				"face": "https://i1.hdslb.com/bfs/face/fd135f95e066ea357969cac54468c0273baea6a1.jpg",
      				"followed": false,
      				"vip_type": 2,
      				"vip_statue": 1
      			},
      			"cover_type": 2,
      			"cnt_info": {
      				"collect": 0,
      				"play": 0,
      				"thumb_up": 0,
      				"share": 0
      			},
      			"type": 11,
      			"intro": "",
      			"ctime": 1709329450,
      			"mtime": 1709329450,
      			"state": 0,
      			"fav_state": 0,
      			"like_state": 0,
      			"media_count": 26,
      			"is_top": false
      		},
      		"medias": [{
      				"id": 113892432876371,
      				"type": 2,
      				"title": "中式美學(xué):沙鷗徑去魚兒飽,野鳥相呼柿子紅。",
      				"cover": "http://i2.hdslb.com/bfs/archive/843055b47a4cc46e80047ab9087b4bea06491f47.jpg",
      				"intro": "中式美學(xué):沙鷗徑去魚兒飽,野鳥相呼柿子紅。",
      				"page": 1,
      				"duration": 99,
      				"upper": {
      					"mid": 1580957455,
      					"name": "中式美學(xué)分享",
      					"face": "https://i1.hdslb.com/bfs/face/f8e2fec9c18501d4e577bd7f60030f97c2c8fe54.jpg",
      					"jump_link": ""
      				},
      				"attr": 0,
      				"cnt_info": {
      					"collect": 5961,
      					"play": 104689,
      					"danmaku": 52,
      					"vt": 0,
      					"play_switch": 0,
      					"reply": 0,
      					"view_text_1": "10.5萬"
      				},
      				"link": "bilibili://video/113892432876371",
      				"ctime": 1737861192,
      				"pubtime": 1737861192,
      				"fav_time": 1738047373,
      				"bv_id": "BV1bvFAe2Efz",
      				"bvid": "BV1bvFAe2Efz",
      				"season": null,
      				"ogv": null,
      				"ugc": {
      					"first_cid": 28085587803
      				},
      				"media_list_link": "bilibili://music/playlist/playpage/3316270251?page_type=3\u0026oid=113892432876371\u0026otype=2"
      			}
      		]
      	}
      }
      

      請 AI 寫一個解析 JSON 文件的工具,能夠指定路徑來獲取指定數(shù)據(jù)。

      import json
      import jmespath
      
      def get_value_by_path(data, path):
          """通過路徑獲取JSON中的值
        
          Args:
              data (dict): JSON數(shù)據(jù)
              path (str): JsonPath表達(dá)式,如 'data.result[?result_type=='video'].data[]'
            
          Returns:
              list: 返回所有匹配的值的列表
          """
          try:
              # 解析并執(zhí)行JsonPath表達(dá)式
              print(path)
              result = jmespath.search(path, data)
              return result
        
          except Exception as e:
              print(f"錯誤: 無法從路徑 '{path}' 獲取值: {str(e)}")
              return []
      
      def extract_values(json_file, path):
          """從指定的JSON文件中提取指定路徑的值
        
          Args:
              json_file (str): JSON文件的路徑
              path (str): 以點號分隔的路徑
            
          Returns:
              list: 包含所有匹配值的列表
          """
          try:
              # 讀取JSON文件
              with open(json_file, 'r', encoding='utf-8') as f:
                  data = json.load(f)
                
              # 獲取指定路徑的值
              values = get_value_by_path(data, path)
            
              # 確保返回值是列表
              if not isinstance(values, list):
                  values = [values]
                
              return values
            
          except FileNotFoundError:
              print(f"錯誤: 找不到文件 '{json_file}'")
              return []
          except json.JSONDecodeError:
              print(f"錯誤: '{json_file}' 不是有效的JSON文件")
              return []
      

      import json
      import argparse
      from pytools.tools.dw_video import download_video
      from pytools.common.jsonparse import extract_values
      
      # ------------------------------------------------------------
      # up 主視頻
      # bf -f video.json -p 'data.list.vlist[*].bvid'
      # ------------------------------------------------------------
      
      def genurl(bvids):
          urls = []
          for bvid in bvids:
              urls.append("https://www.bilibili.com/video/" + bvid)
          return urls
      
      def main():
          # 設(shè)置命令行參數(shù)
          parser = argparse.ArgumentParser(description='從JSON文件中提取指定路徑的值')
          parser.add_argument('-f', '--file', required=True, help='JSON文件路徑')
          parser.add_argument('-p', '--path', required=True, help='以點號分隔的JSON路徑,例如:data.list.vlist[*].bvid')
        
          args = parser.parse_args()
        
          # 提取值
          values = extract_values(args.file, args.path)
          print(values)
        
          # 打印結(jié)果
          if values:
              print("\n".join(str(v) for v in values))
              # 如果提取的是bvid,則下載視頻
              urls = genurl(values)
              for url in urls:
                  download_video(url)
                
      
      if __name__ == '__main__':
          main()
      
      

      AI 很快就寫出來了。只要執(zhí)行 python3 bf.py -f video.json -p "data.list.vlist[*].bvid" 就可以生成該頁的所有視頻的 B 站網(wǎng)址。這里用到了 JsonPath 語法。可以去學(xué)習(xí)下:““JsonPath簡明教程”。須知軟件工程師要十八般武藝樣樣會一點。

      為什么寫成這樣呢,因為這個是可以復(fù)用的。

      咱們再來看搜索個人UP主視頻返回什么格式。她的 bvid 藏在 ?data.list.vlist[*].bvid? 里。

      可以使用 python3 bf.py -f video.json -p "data.list.vlist[*].bvid"

      {
      	"code": 0,
      	"message": "0",
      	"ttl": 1,
      	"data": {
      		"list": {
      			"slist": [],
      			"tlist": {
      				"160": {
      					"tid": 160,
      					"count": 2,
      					"name": "生活"
      				},
      				"36": {
      					"tid": 36,
      					"count": 140,
      					"name": "知識"
      				}
      			},
      			"vlist": [{
      					"comment": 4,
      					"typeid": 228,
      					"play": 1688,
      					"pic": "http://i2.hdslb.com/bfs/archive/2bf4a4f7ed75aa137c2d999998db6d0123481ad2.jpg",
      					"subtitle": "",
      					"description": "中式美學(xué):正浪吟、不覺回橈,水花風(fēng)葉兩悠悠。",
      					"copyright": "1",
      					"title": "中式美學(xué):正浪吟、不覺回橈,水花風(fēng)葉兩悠悠。",
      					"review": 0,
      					"author": "中式美學(xué)分享",
      					"mid": 1580957455,
      					"created": 1747611930,
      					"length": "02:10",
      					"video_review": 1,
      					"aid": 114531426636976,
      					"bvid": "BV1kAESzuEXc",
      					"hide_click": false,
      					"is_pay": 0,
      					"is_union_video": 0,
      					"is_steins_gate": 0,
      					"is_live_playback": 0,
      					"is_lesson_video": 0,
      					"is_lesson_finished": 0,
      					"lesson_update_info": "",
      					"jump_url": "",
      					"meta": null,
      					"is_avoided": 0,
      					"season_id": 0,
      					"attribute": 8405120,
      					"is_charging_arc": false,
      					"elec_arc_type": 0,
      					"elec_arc_badge": "",
      					"vt": 0,
      					"enable_vt": 0,
      					"vt_display": "",
      					"playback_position": 0,
      					"is_self_view": false
      				},
      				{
      					"comment": 1,
      					"typeid": 228,
      					"play": 2122,
      					"pic": "http://i2.hdslb.com/bfs/archive/b5da93822dd42959ef4103b944ad089593cbc928.jpg",
      					"subtitle": "",
      					"description": "中式美學(xué):落日熔金,暮云合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。",
      					"copyright": "1",
      					"title": "中式美學(xué):落日熔金,暮云合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。",
      					"review": 0,
      					"author": "中式美學(xué)分享",
      					"mid": 1580957455,
      					"created": 1747434610,
      					"length": "01:40",
      					"video_review": 3,
      					"aid": 114519833580541,
      					"bvid": "BV1YSE8zKE4Q",
      					"hide_click": false,
      					"is_pay": 0,
      					"is_union_video": 0,
      					"is_steins_gate": 0,
      					"is_live_playback": 0,
      					"is_lesson_video": 0,
      					"is_lesson_finished": 0,
      					"lesson_update_info": "",
      					"jump_url": "",
      					"meta": null,
      					"is_avoided": 0,
      					"season_id": 0,
      					"attribute": 8405120,
      					"is_charging_arc": false,
      					"elec_arc_type": 0,
      					"elec_arc_badge": "",
      					"vt": 0,
      					"enable_vt": 0,
      					"vt_display": "",
      					"playback_position": 0,
      					"is_self_view": false
      				}
      			}
      		}
      

      這里可以對 python3 bf.py 做了一個 alias ,在 ~/.zshrc 里添加

      ?alias bf="python3 /Users/qinshu/tools/pytools/pytools/tools/bf.py"?

      添加到Path里,然后 source ~/.zshrc

      就可以直接使用 bf -f video.json -p "data.list.vlist[*].bvid",這就是可復(fù)用的威力,不需要改代碼,就可以適應(yīng)不同的變化。

      讀者還可以去看看根據(jù)關(guān)鍵字搜索B站視頻的JSON返回數(shù)據(jù),可以用 bss -f video.json -p 'data.result[?result_type=="video"].data[]'?來實現(xiàn)。可謂一個程序能解決三種場景。

      視頻下載

      
      #!/usr/bin/env python3
      
      import subprocess
      import shlex
      from pathlib import Path
      from typing import Optional, Union
      import time
      
      def download_video(
          video_url: str,
          output_dir: Union[str, Path] = Path.cwd(),
          timeout: int = 3600,  # 1小時超時
          retries: int = 3,
          verbose: bool = True
      ) -> Optional[Path]:
          """
          使用 y 命令下載視頻
          
          參數(shù):
              video_url: 視頻URL (e.g. "https://www.bilibili.com/video/BV1xx411x7xx")
              output_dir: 輸出目錄 (默認(rèn)當(dāng)前目錄)
              timeout: 超時時間(秒)
              retries: 重試次數(shù)
              verbose: 顯示下載進(jìn)度
          
          返回:
              成功時返回下載的視頻路徑,失敗返回None
          """
          output_dir = Path(output_dir)
          output_dir.mkdir(parents=True, exist_ok=True)
          
          cmd = f"y {shlex.quote(video_url)}"
          if verbose:
              print(f"開始下載: {video_url}")
              print(f"保存到: {output_dir.resolve()}")
              print(f"執(zhí)行命令: {cmd}")
          
          for attempt in range(1, retries + 1):
              try:
                  start_time = time.time()
                  
                  # 使用Popen實現(xiàn)實時輸出
                  process = subprocess.Popen(
                      cmd,
                      shell=True,
                      cwd=str(output_dir),
                      stdout=subprocess.PIPE,
                      stderr=subprocess.STDOUT,
                      universal_newlines=True,
                      bufsize=1
                  )
                  
                  # 實時打印輸出
                  while True:
                      output = process.stdout.readline()
                      if output == '' and process.poll() is not None:
                          break
                      if output and verbose:
                          print(output.strip())
                      
                      # 檢查超時
                      if time.time() - start_time > timeout:
                          process.terminate()
                          raise subprocess.TimeoutExpired(cmd, timeout)
                  
                  # 檢查返回碼
                  if process.returncode == 0:
                      if verbose:
                          print(f"下載成功 (嘗試 {attempt}/{retries})")
                      return _find_downloaded_file(output_dir, video_url)
                  else:
                      raise subprocess.CalledProcessError(process.returncode, cmd)
                      
              except (subprocess.TimeoutExpired, subprocess.CalledProcessError) as e:
                  if attempt < retries:
                      wait_time = min(attempt * 10, 60)  # 指數(shù)退避
                      if verbose:
                          print(f"嘗試 {attempt}/{retries} 失敗,{wait_time}秒后重試...")
                          print(f"錯誤: {str(e)}")
                      time.sleep(wait_time)
                  else:
                      if verbose:
                          print(f"下載失敗: {str(e)}")
                      return None
      
      def _find_downloaded_file(directory: Path, video_url: str) -> Optional[Path]:
          """嘗試自動查找下載的文件"""
          # 這里可以根據(jù)實際y命令的輸出文件名模式進(jìn)行調(diào)整
          # 示例:查找最近修改的視頻文件
          video_files = sorted(
              directory.glob("*.mp4"),
              key=lambda f: f.stat().st_mtime,
              reverse=True
          )
          return video_files[0] if video_files else None
      
      

      這里采用的是直接調(diào)用命令行,命令行 y 是如下文件。 加上 chmod +x y

      --cookies 里的文件,是firefox 里用 Cookie-Editor 導(dǎo)出的數(shù)據(jù)復(fù)制進(jìn)去的。如果你有其它的下載工具,也可以替換為自己的下載工具。這里咱們就不單獨開發(fā)下載工具了。

      link=$1
      you-get $link --cookies "/Users/qinshu/privateqin/cookies.txt"  -f -o /Users/qinshu/joy/dance/bili
      

      獲取網(wǎng)絡(luò)數(shù)據(jù)

      其實還有一步,獲取網(wǎng)絡(luò)數(shù)據(jù)源,可以通過 python requests 庫實現(xiàn)。不過B站加了風(fēng)控校驗,導(dǎo)致我之前的方法失效了。樂趣減少了很多。只能算做了一半工作。后續(xù)還要看看怎么突破網(wǎng)站防線拉取數(shù)據(jù)。


      ?

      小結(jié)

      工程師不比程序員,他需要的是十八般武藝樣樣都會一點,這樣會獲得更大的自由度。雖然學(xué)藝不精,那有什么關(guān)系呢?什么問題用什么工具最適合解決。對于手里沒錢的碼農(nóng),多寫一點工具讓自己的生活更輕松,未嘗不是一種選擇呢。

      posted @ 2025-06-15 21:22  琴水玉  閱讀(444)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 91精品国产午夜福利| 国产裸体永久免费无遮挡| 国产精品国产三级国快看| 国产精品免费第一区二区| 亚洲另类激情专区小说婷婷久| 国产伦码精品一区二区| 熟女国产精品一区二区三| 国产永久免费高清在线| 又黄又爽又色的免费网站| 91老肥熟女九色老女人| 五月丁香啪啪| 天堂√最新版中文在线地址| 久久精品无码一区二区三区| 日本一区二区三区免费播放视频站| 欧美性xxxxx极品| 日本公与熄乱理在线播放| 欧美日韩不卡视频合集| 一本久久a久久精品综合| 国产精品∧v在线观看| 乱码精品一区二区亚洲区| 一本av高清一区二区三区| 香蕉av777xxx色综合一区| 91亚洲精品一区二区三区| 动漫AV纯肉无码AV电影网| 靖西县| 国产精品人伦一区二区三| 深夜精品免费在线观看| 少妇放荡的呻吟干柴烈火动漫| 国内精品自在拍精选| 日本一区二区不卡精品| 国产精品一区二区 尿失禁| 疯狂添女人下部视频免费| 国产欧美日韩亚洲一区二区三区| 欧美激情在线播放| 国产熟女高潮一区二区三区| 国产精品中文第一字幕| 国产95在线 | 欧美| 无码专区人妻系列日韩精品少妇 | 久久日韩在线观看视频| 久久天天躁狠狠躁夜夜躁2o2o| 视频一区二区不中文字幕|