成品下载地址:https://www.pan38.com/yun/share.php?code=JCnzE 提取密码:1135
这个B站自动发布工具包含视频上传、动态发布和评论功能,使用Selenium实现登录和视频上传,requests处理API请求。使用时需要安装selenium和requests库,并配置ChromeDriver。
源码部分:
import os
import time
import json
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from datetime import datetime
class BilibiliAutoUploader:
def __init__(self, config_file='config.json'):
self.config = self._load_config(config_file)
self.driver = None
self.cookies = None
self.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',
'Referer': 'https://www.bilibili.com/'
}
def _load_config(self, config_file):
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
def login(self):
options = webdriver.ChromeOptions()
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
self.driver = webdriver.Chrome(options=options)
self.driver.get('https://passport.bilibili.com/login')
# 等待登录页面加载
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.ID, 'login-username'))
)
# 输入用户名密码
username = self.driver.find_element(By.ID, 'login-username')
password = self.driver.find_element(By.ID, 'login-passwd')
username.send_keys(self.config['username'])
password.send_keys(self.config['password'])
# 点击登录按钮
login_button = self.driver.find_element(By.CLASS_NAME, 'btn-login')
login_button.click()
# 等待登录完成
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.CLASS_NAME, 'header-avatar'))
)
# 获取cookies
self.cookies = {cookie['name']: cookie['value'] for cookie in self.driver.get_cookies()}
def upload_video(self, video_path, title, desc, tags, cover_path=None):
if not os.path.exists(video_path):
raise FileNotFoundError(f"视频文件 {video_path} 不存在")
if cover_path and not os.path.exists(cover_path):
raise FileNotFoundError(f"封面文件 {cover_path} 不存在")
self.driver.get('https://member.bilibili.com/platform/upload/video/frame')
# 等待上传页面加载
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.CLASS_NAME, 'upload-btn'))
)
# 上传视频文件
upload_input = self.driver.find_element(By.CLASS_NAME, 'upload-btn').find_element(By.TAG_NAME, 'input')
upload_input.send_keys(os.path.abspath(video_path))
# 等待视频上传完成
WebDriverWait(self.driver, 600).until(
EC.presence_of_element_located((By.CLASS_NAME, 'video-title'))
)
# 填写视频信息
title_input = self.driver.find_element(By.CLASS_NAME, 'video-title')
title_input.clear()
title_input.send_keys(title)
desc_input = self.driver.find_element(By.CLASS_NAME, 'video-desc')
desc_input.clear()
desc_input.send_keys(desc)
# 上传封面
if cover_path:
cover_input = self.driver.find_element(By.CLASS_NAME, 'cover-upload-btn').find_element(By.TAG_NAME, 'input')
cover_input.send_keys(os.path.abspath(cover_path))
time.sleep(3) # 等待封面处理完成
# 添加标签
tag_input = self.driver.find_element(By.CLASS_NAME, 'tag-input')
for tag in tags:
tag_input.send_keys(tag)
tag_input.send_keys(Keys.ENTER)
time.sleep(0.5)
# 选择分区
self._select_category()
# 提交视频
submit_button = self.driver.find_element(By.CLASS_NAME, 'submit-btn')
submit_button.click()
# 等待提交完成
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.CLASS_NAME, 'success-info'))
)
# 获取视频aid
success_info = self.driver.find_element(By.CLASS_NAME, 'success-info').text
aid = success_info.split('av')[1].split(')')[0]
return aid
def _select_category(self):
# 点击分区选择按钮
category_btn = self.driver.find_element(By.CLASS_NAME, 'category-btn')
category_btn.click()
# 等待分区选择框出现
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'category-select'))
)
# 选择分区 (这里选择"生活"分区)
first_level = self.driver.find_elements(By.CLASS_NAME, 'first-level')[3] # 生活分区
first_level.click()
second_level = first_level.find_elements(By.CLASS_NAME, 'second-level')[0] # 日常分区
second_level.click()
# 确认选择
confirm_btn = self.driver.find_element(By.CLASS_NAME, 'confirm-btn')
confirm_btn.click()
def post_dynamic(self, content, pictures=None):
if not self.cookies:
raise Exception("请先登录")
url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create'
data = {
'dynamic_id': 0,
'type': 4,
'content': content,
'csrf': self.cookies.get('bili_jct')
}
# 处理图片
if pictures:
pic_urls = []
for pic in pictures:
upload_url = 'https://api.vc.bilibili.com/api/v1/drawImage/upload'
files = {'file_up': open(pic, 'rb')}
headers = {
'cookie': '; '.join([f"{k}={v}" for k, v in self.cookies.items()]),
**self.headers
}
resp = requests.post(upload_url, files=files, headers=headers)
if resp.status_code == 200:
pic_urls.append(resp.json()['data']['image_url'])
if pic_urls:
data['pictures'] = json.dumps(pic_urls)
data['type'] = 2 # 带图片的动态
headers = {
'cookie': '; '.join([f"{k}={v}" for k, v in self.cookies.items()]),
**self.headers
}
resp = requests.post(url, data=data, headers=headers)
if resp.status_code == 200 and resp.json()['code'] == 0:
return resp.json()['data']['dynamic_id']
else:
raise Exception(f"发布动态失败: {resp.text}")
def comment_video(self, aid, content):
if not self.cookies:
raise Exception("请先登录")
url = 'https://api.bilibili.com/x/v2/reply/add'
data = {
'oid': aid,
'type': 1,
'message': content,
'csrf': self.cookies.get('bili_jct')
}
headers = {
'cookie': '; '.join([f"{k}={v}" for k, v in self.cookies.items()]),
**self.headers
}
resp = requests.post(url, data=data, headers=headers)
if resp.status_code == 200 and resp.json()['code'] == 0:
return resp.json()['data']['rpid']
else:
raise Exception(f"评论失败: {resp.text}")
def close(self):
if self.driver:
self.driver.quit()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
from bilibili_auto_uploader import BilibiliAutoUploader
import time
def main():
with BilibiliAutoUploader() as uploader:
# 登录
uploader.login()
# 上传视频
video_path = 'my_video.mp4'
title = '我的日常vlog - ' + time.strftime("%Y年%m月%d日")
desc = '这是我的日常vlog,记录生活中的点点滴滴'
tags = ['vlog', '日常', '生活记录']
cover_path = 'cover.jpg'
aid = uploader.upload_video(video_path, title, desc, tags, cover_path)
print(f"视频上传成功,aid: {aid}")
# 发布动态
dynamic_content = f"新视频发布啦!{title} #vlog #日常"
dynamic_pics = ['screenshot1.jpg', 'screenshot2.jpg']
dynamic_id = uploader.post_dynamic(dynamic_content, dynamic_pics)
print(f"动态发布成功,dynamic_id: {dynamic_id}")
# 评论视频
comment_content = "欢迎大家观看我的视频,喜欢的话请一键三连哦~"
rpid = uploader.comment_video(aid, comment_content)
print(f"评论成功,rpid: {rpid}")
if __name__ == '__main__':
main()
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。