爬取b站一个视频
罗翔老师某一个视频很刑
单个完整代码:
安装依赖库
pip install lxml requests
import osimport requests
import re
from lxml import etree
import json
# 格式化展开输出
from pprint import pprint
# 导入进程模块
import subprocess
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36','cookie': '自己的cookid'
}
url = "https://www.bilibili.com/video/BV1N8qnYeEi3/"
resp = requests.get(url, headers=headers)
html = resp.text
# 提取数据
tree = etree.HTML(html)
title = tree.xpath("/html/body/div[2]/div[2]/div[1]/div[1]/div[1]/div/h1/text()")[0].replace(" ","")
# 视频信息
video_info = re.findall('<script>window.__playinfo__=(.*?)</script>',html)
# 转成json字典
json_data = json.loads(video_info[0])
pprint(json_data)
# 音频链接地址
audio_url = json_data['data']['dash']['audio'][0]['baseUrl']
# print(audio_url)
# 视频链接地址
video_url = json_data['data']['dash']['video'][0]['baseUrl']
# print(video_url)
# 开始下载
os.makedirs("temp",exist_ok=True)
audio_content = requests.get(audio_url,headers).content
video_content = requests.get(video_url,headers).content
with open("temp\\"+title+".mp3",'wb') as audio:audio.write(audio_content)
print("音频下载完成")
with open("temp\\"+title+".mp4",'wb') as video:video.write(video_content)
print("视频下载完成")
# 合并
os.makedirs("data",exist_ok=True) # 合并后的视频存放目录ffmpeg_path = r"D:\env\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin\ffmpeg.exe" # 修改为自己ffmpeg.exe目录
cmd = f'"{ffmpeg_path}" -i temp\\{title}.mp4 -i temp\\{title}.mp3 -c:v copy -c:a aac -strict experimental data\\{title}.mp4'
subprocess.run(cmd, shell=True)
获取思路:
打开network,看到与众不同的图标,音频和视频就在里面
这个与众不同的请求头和我们访问的url是一样的
查看页面的源代码,发现
所以请求url,获取页面,采集数据的链接,下一步下载到本地,然后把音频和视频合并成一个。
提取打印出video_info信息
pprint格式化输出video_info
with open写出到本地:
合并
下载ffmpeg
ffmpeg官网
不要用谷歌浏览器下载,很慢的,换其他浏览器
配置一下path,把bin目录粘贴过来就可以
测试安装成功cmd命令:ffmpeg -version
PotPlayer:不支持S/W HEVC(H265)解码
下载下来:默认安装即可(一直下一步)
PotPlayer:不支持S/W HEVC(H265)解码 的解决办法来源自:大佬
https://github.com/Nevcairiel/LAVFilters/releases
再重新打开data目录下的视频可以了
爬取多个视频
思路:
需要获取到对应的bvid,(bvid在search请求中获取)
发起请求:url = f"https://www.bilibili.com/video/变量bvid/"
循环每一项,就是每个视频的下载
接下来需要为这个search请求构建请求参数,请求头,cookie
baseUrl = "https://space.bilibili.com/3493110839511225/video" # 获取那个up主空间的视频
headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","cookie": "自己的cookie","referer": baseUrl #防盗链来源
}
link = "https://api.bilibili.com/x/space/wbi/arc/search" # search请求
接下来需要请求参数,全部都要
PyCharm批量替换的快捷键 ctrrl+r
使用正则批量添加参数的引号
(.*?): (.*)
'$1': '$2',
如果需要获取其他up主视频
修改完整源码的:baseUrl,headers的cookie值,params参数
多个视频完整源码:
# 优化,标题重复了,导致文件重复
import os
import timeimport requests
import re
from lxml import etree
import json
from pprint import pprint
import subprocess
from tqdm import tqdm
from datetime import datetimebaseUrl = "https://space.bilibili.com/3493110839511225/video"
headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36","cookie": "自己cookie","referer": baseUrl
}
link = "https://api.bilibili.com/x/space/wbi/arc/search"
params = {
'mid': '1789878166',
'ps': '30',
'tid': '0',
'pn': '1',
'keyword':'',
'order': 'pubdate',
'platform': 'web',
'web_location': '1550101',
'order_avoided': 'true',
'dm_img_list': '[]',
'dm_img_str': 'V2ViR0wgMS4wIChPcGVuR0wgRVMgMi4wIENocm9taXVtKQ',
'dm_cover_img_str': 'QU5HTEUgKEludGVsLCBJbnRlbChSKSBJcmlzKFIpIFhlIEdyYXBoaWNzICgweDAwMDBBN0EwKSBEaXJlY3QzRDExIHZzXzVfMCBwc181XzAsIEQzRDExKUdvb2dsZSBJbmMuIChJbnRlbC',
'dm_img_inter': '{"ds":[{"t":2,"c":"Y2xlYXJmaXggZy1zZWFyY2ggc2VhcmNoLWNvbnRhaW5lcg","p":[1782,44,589],"s":[437,899,1290]},{"t":2,"c":"d3JhcHBlcg","p":[795,35,1350],"s":[227,4206,3506]}],"wh":[4506,3752,112],"of":[301,602,301]}',
'w_webid': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzcG1faWQiOiIwLjAiLCJidXZpZCI6IkMwRTlFOEZGLTExN0ItNkIxOS03MEExLUI3OTQ5NkY4NDQ1NDc0NTA0aW5mb2MiLCJ1c2VyX2FnZW50IjoiTW96aWxsYS81LjAgKFdpbmRvd3MgTlQgMTAuMDsgV2luNjQ7IHg2NCkgQXBwbGVXZWJLaXQvNTM3LjM2IChLSFRNTCwgbGlrZSBHZWNrbykgQ2hyb21lLzEzMS4wLjAuMCBTYWZhcmkvNTM3LjM2IiwiYnV2aWRfZnAiOiJDMEU5RThGRi0xMTdCLTZCMTktNzBBMS1CNzk0OTZGODQ0NTQ3NDUwNGluZm9jIiwiYmlsaV90aWNrZXQiOiJmNDM3MDQ2Y2Y4ODkwNzNhMmQyODIyODkxMWVmODI0OCIsImNyZWF0ZWRfYXQiOjE3MzQxNjA3ODEsInR0bCI6ODY0MDAsInVybCI6Ii8xNzg5ODc4MTY2L3ZpZGVvIiwicmVzdWx0Ijoibm9ybWFsIiwiaXNzIjoiZ2FpYSIsImlhdCI6MTczNDE2MDc4MX0.X9iXzZ0ajsvrAqrs53IbCZ4kE9-uQ20qdI1rxRo9ylGqHrCp7tjwq4aHma1eZ2VMBGo7fKGEyK_4I0bYYprFhstC5H2vEitrBiZ3WxCv43p-dDUiK_wiOflwKIW2rMDw5C0Hd1fd0cq3tfiHLALanKLgsr8EpYC8hpeIw_KmimZJQa3c4sb2Ic8aIzxEC_kzVWFFeDriU8VF8OYBDjspLAHwwKkucPteE10IOMZ8ONnmxWKzyTTf_hwf5dtFbAUY1oeozUWKAFdsTiYpTlD2vn_7zBeFUQCmfXUoB17z9BbGdtegQYONSDD1awDHZ7cIm0ZoH5FP8R1VzGBMteSEmg',
'w_rid': '5ebd3bc3f9edb242cb19bc1618b36538',
'wts': '1734160784',
}jsonData = requests.get(url=link, params=params, headers=headers).json()
v_list = jsonData['data']['list']['vlist']
# pprint(v_list)page = 0
for v in v_list:page += 1bvid = v['bvid']url = f"https://www.bilibili.com/video/{bvid}/"resp = requests.get(url, headers=headers, timeout=120)html = resp.texttree = etree.HTML(html)title = tree.xpath("/html/body/div[2]/div[2]/div[1]/div[1]/div[1]/div/h1/text()")[0].replace(" ", "")# 获取当前时间戳timestamp = datetime.now().strftime("%Y%m%d%H%M%S")unique_title = f"{title}_{timestamp}"video_info = re.findall('<script>window.__playinfo__=(.*?)</script>', html)json_data = json.loads(video_info[0])audio_url = json_data['data']['dash']['audio'][0]['baseUrl']video_url = json_data['data']['dash']['video'][0]['baseUrl']os.makedirs("temp1", exist_ok=True)def download_file(url, filename):response = requests.get(url, headers=headers, stream=True, timeout=120)total_size = int(response.headers.get('content-length', 0))with open(filename, 'wb') as file, tqdm(desc=filename,total=total_size,unit='B',unit_scale=True,unit_divisor=1024,) as bar:for data in response.iter_content(chunk_size=1024):size = file.write(data)bar.update(size)try:audio_filename = f"temp1/{unique_title}.mp3"download_file(audio_url, audio_filename)print("音频下载完成")video_filename = f"temp1/{unique_title}.mp4"download_file(video_url, video_filename)print("视频下载完成")print(title)os.makedirs("data1", exist_ok=True)ffmpeg_path = r"D:\env\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin\ffmpeg.exe" #修改为自己的ffmpeg.exe目录cmd = f'"{ffmpeg_path}" -i {video_filename} -i {audio_filename} -c:v copy -c:a aac -strict experimental data1/{unique_title}.mp4'subprocess.run(cmd, shell=True, check=True)print(f"第{page}个视频下载成功")print(f"{unique_title}.mp4")except Exception as e:print(f"下载或合并失败: {e}")time.sleep(10)
完整代码感觉就这个下载进度条函数不太好理解,所以这里用AI解释了一下代码,以供参考学习!看得懂可以忽略不计。
def download_file(url, filename):response = requests.get(url, headers=headers, stream=True, timeout=120)total_size = int(response.headers.get('content-length', 0))with open(filename, 'wb') as file, tqdm(desc=filename,total=total_size,unit='B',unit_scale=True,unit_divisor=1024,) as bar:for data in response.iter_content(chunk_size=1024):size = file.write(data)bar.update(size)
response = requests.get(url, headers=headers, stream=True, timeout=120)
功能: 使用 requests.get 方法发送 HTTP GET 请求以获取文件内容。
参数:
url: 目标文件的 URL。
headers: 请求头信息,通常用于模拟浏览器访问或其他身份验证。
stream=True: 设置为 True 表示不立即下载整个文件,而是以流的形式逐步下载。
timeout=120: 设置请求超时时间为 120 秒。
total_size = int(response.headers.get(‘content-length’, 0))
功能: 从响应头中获取文件的总大小(以字节为单位)。
细节:
response.headers.get(‘content-length’, 0): 尝试从响应头中获取 Content-Length 字段的值,如果不存在则返回默认值 0。
int(…): 将获取到的字符串形式的大小转换为整数。
with open(filename, ‘wb’) as file, tqdm(
desc=filename,
total=total_size,
unit=‘B’,
unit_scale=True,
unit_divisor=1024,
) as bar:
功能: 打开一个文件用于写入二进制数据,并创建一个 tqdm 进度条来显示下载进度。
参数:
open(filename, ‘wb’): 以二进制写模式打开文件。
tqdm(…): 创建一个进度条对象。
desc=filename: 设置进度条的描述信息为文件名。
total=total_size: 设置进度条的总长度为文件的总大小。
unit=‘B’: 设置进度条的单位为字节。
unit_scale=True: 允许进度条自动缩放单位(例如,从字节到千字节、兆字节等)。
unit_divisor=1024: 设置单位缩放的基数为 1024。
for data in response.iter_content(chunk_size=1024):size = file.write(data)bar.update(size)
功能: 逐块下载文件内容并写入本地文件,同时更新进度条。
细节:
response.iter_content(chunk_size=1024): 以 1024 字节(即 1 KB)为一块逐步读取响应内容。
size = file.write(data): 将读取到的数据块写入文件,并返回实际写入的字节数。
bar.update(size): 更新进度条,显示已下载的字节数。
总结
这个函数的主要目的是从指定的 URL 下载文件,并使用 tqdm 库在控制台中显示下载进度。通过这种方式,用户可以直观地看到文件下载的进度,从而更好地了解下载状态。
运行效果
这个代码还是有所欠缺的。只能爬取up主一个一个的视频,如果是合集里面的,默认获取合集第一个视频