2

[其它源码] 简单的python爬虫工具源码,B站视频爬虫11.13

[复制链接]
654 0
雪儿 2023-11-13 17:57:08 | 显示全部楼层 |阅读模式
享一个我自己写的pythonB站视频爬虫,写的比较粗糙(大佬们可以帮我添加一些其他功能爬虫功能吗,如模拟登录,数据分析等;求求啦(>W<))

当然网上一堆B站视频获取的工具,也不差我这个粗糙的python脚本,就是分享出来大家一起讨论学习,如果大家有什么好的想法和功能我们可以一起聊聊。

这里分享一个我自己用的B站视频下载的工具BBDown,很好用,作者也是在一直更新。

必要工具ffmpeg,建议还是放在你的python项目目录下(我不知道为什么配置的环境变量没有生效)

这个如果想爬取高清视频就把自己的cookie加到api_headers。这里进度条加载有点问题,就是视频太小了进度条可能加载不完全,还有就是视频合成也有点问题,有时视频合成不了(我推测可能是特殊字符没有过滤完全,当然也可能是其他原因)

python源码:
  1. import argparse

  2. import requests, re, sys, os, time
  3. from contextlib import closing
  4. from urllib import parse
  5. from lxml import etree
  6. import subprocess
  7. from tqdm import tqdm

  8. class BiliBili:
  9.     def __init__(self, dirname):

  10.         self.search_headers = {
  11.             'authority': 'search.bilibili.com',
  12.             'Accept': '*/*',
  13.             'Referer': 'https://www.bilibili.com/',
  14.             'Accept-Encoding': 'gzip, deflate, br',
  15.             'Accept-Language': 'zh-CN,zh;q=0.9',
  16.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  17.                           'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61',
  18.         }

  19.         self.video_headers = {
  20.             'authority': 'www.bilibili.com',
  21.             'Referer': 'https://www.bilibili.com/',
  22.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  23.                           'Chrome/118.0.0.0 Safari/537.36'
  24.         }

  25.         self.api_headers = {
  26.             'authority': 'api.bilibili.com',
  27.             'Accept': '*/*',
  28.             'Referer': 'https://www.bilibili.com/',
  29.             'Accept-Encoding': 'gzip, deflate, br',
  30.             'Accept-Language': 'zh-CN,zh;q=0.9',
  31.             # 'cookie':"",
  32.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  33.                           'Chrome/119.0.0.0 Safari/537.36'
  34.         }
  35.         self.sess = requests.Session()

  36.         self.dir = dirname

  37.     def downloader(self, data_url, title):
  38.         """
  39.         数据下载
  40.         Parameters:
  41.             data_url: 数据地址
  42.             title: 标题
  43.         """
  44.         if self.dir not in os.listdir():
  45.             os.mkdir(self.dir)
  46.         size = 0
  47.         with closing(self.sess.get(data_url, headers=self.video_headers, stream=True)) as response:
  48.             chunk_size = 1000
  49.             content_size = int(response.headers['content-length'])
  50.             content_mb = content_size / 1000 / 1000
  51.             if response.status_code == 200:
  52.                 sys.stdout.write('  [开始下载]\n')
  53.                 sys.stdout.write('  [文件大小]: %0.2f MB\n' % content_mb)
  54.                 video_name = os.path.join(self.dir, title)
  55.                 # 保存视频,并输出进度
  56.                 with tqdm(total=content_size, desc='  [下载进度]',leave=False, ncols=100, unit='B',unit_scale=True) as pbar:
  57.                     with open(video_name, 'wb') as file:
  58.                         if content_mb < 3:
  59.                             file.write(response.content)
  60.                             for i in range(5):
  61.                                 pbar.update(content_size/5)
  62.                         else:
  63.                             for data in response.iter_content(chunk_size=chunk_size):
  64.                                 file.write(data)
  65.                                 pbar.update(len(data))
  66.                                 size += len(data)
  67.                                 file.flush()

  68.                         sys.stdout.write('\n')
  69.                         sys.stdout.write('  [下载完成]' + '\r')
  70.                         sys.stdout.flush()
  71.                         if size / content_size == 1:
  72.                             print('\n')
  73.             else:
  74.                 print('~~~链接异常~~~'+'\r')
  75.                 time.sleep(1)

  76.     def search_video(self, keyword, page=1):
  77.         """
  78.         搜索页视频信息
  79.         Parameters:
  80.             keyword: 关键词
  81.             page: 页码
  82.         Returns:
  83.             videos[titles,bvs]
  84.             titles:标题
  85.             bvs: bv号
  86.         """
  87.         url = f'https://search.bilibili.com/all?keyword={parse.quote(keyword)}&page={page}&o=30'
  88.         req = self.sess.get(url=url, headers=self.search_headers)
  89.         html = etree.fromstring(req.text, etree.HTMLParser())
  90.         bvs = html.xpath('//div[@class="bili-video-card__info--right"]/a/@href')[:3]
  91.         titles = html.xpath('//div[@class="bili-video-card__info--right"]/a/h3/@title')[:3]
  92.         videos = []
  93.         for i, j in zip(titles, bvs):
  94.             for c in u'´★☆❤◦\/:*?"<>|':
  95.                 i = i.replace(c, '')
  96.             tmp = [i, j]
  97.             videos.append(tmp)
  98.         # 输出搜索页面视频标题和视频url
  99.         print(videos)
  100.         return videos

  101.     # titles, bvs
  102.     def get_download_url(self, arcurl):
  103.         """
  104.         获取详情页数据信息
  105.         Parameters:
  106.             arcurl: 视频播放地址
  107.         Returns:
  108.             accept_description: 视频清晰度
  109.             video_data: 视频地址
  110.             audio_data: 音频地址
  111.             title: 标题
  112.         """
  113.         xp = 'BV\d.{9}'
  114.         if re.findall(xp, arcurl):
  115.             bv = re.findall(xp, arcurl)[0]
  116.             url = f'https://api.bilibili.com/x/web-interface/view?bvid={bv}'  # avid&cid
  117.         else:
  118.             print('视频BV号解析失败,请检查输入的bv号是否正确')
  119.             exit(0)
  120.         req1 = self.sess.get(url=url, headers=self.video_headers)
  121.         ac_json = req1.json()
  122.         avid = ac_json['data']['aid']
  123.         cid = ac_json['data']['cid']
  124.         url2 = f'https://api.bilibili.com/x/player/wbi/playurl?avid={avid}&cid={cid}&fnval=4048'  # playurl
  125.         title = ac_json['data']['title']
  126.         req2 = self.sess.get(url=url2, headers=self.api_headers)
  127.         playinfo_dict = req2.json()

  128.         accept_description = playinfo_dict["data"]["accept_description"]  # 视频清晰度
  129.         # id = [playinfo_dict["data"]["dash"]["video"][0]["id"]]
  130.         audio_data = [playinfo_dict["data"]["dash"]["audio"][0]["baseUrl"]]  # 音频数据
  131.         video_data = [playinfo_dict["data"]["dash"]["video"][0]["baseUrl"]]
  132.         # print(id)
  133.         if not audio_data and not video_data:
  134.             print('视频解析失败')
  135.             exit(0)
  136.         return [accept_description, video_data, audio_data,title]

  137.     def merge_data(self, dir, video_name):
  138.         """
  139.         视频合成
  140.         Parameters:
  141.             dir: 目录
  142.             video_name: 视频名
  143.         """
  144.         time.sleep(0.1)
  145.         if video_name+'_2' in os.listdir(self.dir):
  146.             print( '合成视频已存在')
  147.             exit(0)
  148.         else:
  149.             print('视频合成开始:', video_name)
  150.             cmd = f"cd {dir} & ffmpeg -y -i {video_name}.mp4 -i {video_name}.mp3 -c:v copy -c:a aac -strict experimental -map 0:0 -map 1:0 {video_name}_2.mp4 && del {video_name}.mp4 {video_name}.mp3"
  151.             subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  152.             print('视频合成结束:', video_name+'\r')

  153.     def search_downloader(self, keyword,page=1):
  154.         """
  155.         批量爬取搜索页视频
  156.         Parameters:
  157.             keyword: 关键词
  158.             page: 页码
  159.         """
  160.         if self.dir not in os.listdir():
  161.             os.mkdir(self.dir)
  162.         for j in range(page):
  163.             s_video = self.search_video(keyword, j+1)
  164.             for i in range(len(s_video)):
  165.                 title = s_video[i][0]
  166.                 arcurl = s_video[i][1]
  167.                 if title not in os.listdir(self.dir):
  168.                     videos_data = self.get_download_url(arcurl)[1]
  169.                     audio_data = self.get_download_url(arcurl)[2]
  170.                     if not videos_data[0] or not audio_data[0]:
  171.                         print('第[ %d ]页:%s视频或音频解析失败,跳过下载:' % (1 + j, title))
  172.                         continue  # Skip video download if video or audio parsing fails

  173.                     fname = title + '.mp4'
  174.                     print('第[ %d ]页:视频[ %s ]下载中:' % (1 + j, fname))  # 打印页码和指定下载视频
  175.                     self.downloader(videos_data[0], fname)
  176.                     print('视频下载完成!')

  177.                     fname = title + '.mp3'
  178.                     print('第[ %d ]页:音频[ %s ]下载中:' % (1 + j, fname))  # 打印页码和指定下载视频
  179.                     self.downloader(audio_data[0], fname)
  180.                     print('音频下载完成!')

  181.                     # 创建临时文本文件用于合并视频音频
  182.                     try:
  183.                         video_name = title
  184.                         dirz = self.dir
  185.                         self.merge_data(dirz, video_name)
  186.                     except:
  187.                         print('请安装FFmpeg,并配置环境变量 http://ffmpeg.org/')

  188.     def a_video_download(self,bv):
  189.         """
  190.         单个视频爬取
  191.         Parameters:
  192.             bv: 关bv号
  193.         """
  194.         video_info = self.get_download_url(bv)
  195.         title = video_info[3]
  196.         fname = "{0}.mp4".format(title)
  197.         print('视频[ %s ]下载中:' % fname)  # 打印页码和指定下载视频
  198.         self.downloader(video_info[1][0], fname)
  199.         print('视频下载完成!')
  200.         fname = '{0}.mp3'.format(title)
  201.         print('音频[ %s ]下载中:' % fname)  # 打印页码和指定下载视频
  202.         self.downloader(video_info[2][0], fname)
  203.         print('音频下载完成!')
  204.         self.merge_data(self.dir,video_info[3])

  205. if __name__ == '__main__':
  206.     if len(sys.argv) == 1:
  207.         sys.argv.append('--help')
  208.     parser = argparse.ArgumentParser()
  209.     parser.add_argument('-d', '--dir', required=True, help='必要,下载路径')
  210.     parser.add_argument('-bv', '--bvid', required=False, help='下载指定bv视频')
  211.     parser.add_argument('-s', '--search', required=False, action='store_true', help='批量下载搜索页视频')
  212.     parser.add_argument('-k', '--keyword', required=False, help='搜索关键词内容')
  213.     parser.add_argument('-p', '--pages', required=False, help='需要下载页码数', type=int)
  214.     args = parser.parse_args()

  215.     B = BiliBili(args.dir)
  216.     if args.search:
  217.         if args.keyword and args.pages is None:
  218.             print('请输入搜索关键词和页码')
  219.             exit(0)
  220.         B.search_downloader(args.keyword, args.pages)
  221.     if args.bvid:
  222.         if args.search or args.keyword or args.pages:
  223.             print('下载单个视频请只输入BV号')
  224.             exit(0)
  225.         B.a_video_download(args.bvid)
  226.     # return [accept_description, video_data, audio_data, title]

  227.     # B = BiliBili('猫')
  228.     # url = 'https://www.bilibili.com/video/BV1Jy4y1K7yp/'
  229.     # a=B.get_download_url(url)
  230.     # B.downloader(a[1][0], a[3])
复制代码

一起来加入破走大家庭!
快捷回复: 换一换
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|破走论坛

    本站资源来自互联网用户收集发布,如有侵权请邮件与我们联系处理:pozou@qq.com

    Copyright © 2018-2024 破走论坛. Powered by 破走论坛!