import tkinter as tk from tkinter import filedialog, scrolledtext import urllib.request import urllib.error import re import os from io import BytesIO import gzip import zlib import json import logging import brotli # 添加这行来导入 brotli class BilibiliUploader: def __init__(self, master): self.master = master master.title("Bilibili图片上传器") # 文件选择 self.select_button = tk.Button(master, text="选择图片", command=self.select_file) self.select_button.pack() self.file_label = tk.Label(master, text="未选择文件") self.file_label.pack() # 上传按钮 self.upload_button = tk.Button(master, text="上传到Bilibili", command=self.upload_file) self.upload_button.pack() # 日志显示 self.log_text = scrolledtext.ScrolledText(master, height=20) self.log_text.pack() self.file_path = None def select_file(self): self.file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png *.gif")]) if self.file_path: self.file_label.config(text=f"已选择: {self.file_path}") else: self.file_label.config(text="未选择文件") def upload_file(self): if not self.file_path: self.log("请先选择一个图片文件") return self.log(f"开始上传图片到Bilibili: {self.file_path}") self.make_requests(self.file_path) self.log("上传过程完成") def log(self, message): self.log_text.insert(tk.END, message + "n") self.log_text.see(tk.END) def make_requests(self, image_path): response = [None] responseText = None if self.request_api_bilibili_com(response, image_path): self.log("请求成功发送") try: responseText = self.read_response(response[0]) self.log("响应内容:") self.log(responseText) json_response = json.loads(responseText) if json_response.get('code') == 0: self.log("上传成功!") image_url = json_response.get('data', {}).get('image_url', '') if image_url: self.log(f"图片URL: {image_url}") else: self.log(f"上传失败。错误信息: {json_response.get('message', '未知错误')}") except json.JSONDecodeError: self.log("无法解析JSON响应") except Exception as e: self.log(f"处理响应时发生错误: {str(e)}") finally: response[0].close() else: self.log("请求失败") def write_multipart_body_to_request(self, req, body): multiparts = re.compile("<!>").split(body) with BytesIO() as bytes_io: for part in multiparts: if os.path.exists(part): with open(part, "rb") as file: bytes_io.write(file.read()) else: bytes_io.write(part.replace("rn", "n").replace("r", "n").replace("n", "rn").encode("utf-8")) body = bytes_io.getvalue() req.add_header("Content-Length", str(len(body))) req.data = body def read_response(self, response): content = response.read() content_encoding = response.info().get('Content-Encoding', '').lower() if content_encoding == 'gzip': buf = BytesIO(content) with gzip.GzipFile(fileobj=buf) as gf: content = gf.read() elif content_encoding == 'deflate': content = zlib.decompress(content, -zlib.MAX_WBITS) elif content_encoding == 'br': content = brotli.decompress(content) try: return content.decode('utf-8') except UnicodeDecodeError: return content.decode('utf-8', errors='ignore') def request_api_bilibili_com(self, response, image_path): response[0] = None try: req = urllib.request.Request("https://api.bilibili.com/x/dynamic/feed/draw/upload_bfs") headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0", "Accept": "*/*", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate, br, zstd", "Referer": "https://t.bilibili.com/", "Origin": "https://t.bilibili.com", "Connection": "keep-alive", "Cookie": "buvid3=DEEE7B07-FFBF-0ACA-1A8B-BA9B3B98D6F234696infoc; b_nut=1697693535; _uuid=6AB48D3E-CAFB-4210D-ACE3-92C4B2102F46538381infoc; buvid4=A1F0A409-3626-8333-8964-2B9F32D92EC840243-023101913-MnLxL6Vqo8JzkbhjsXOcgQ%3D%3D; buvid_fp=5e9a8088e2093db52ac4432fb2bb81c2; enable_web_push=DISABLE; header_theme_version=CLOSE; home_feed_column=5; browser_resolution=1536-703; fingerprint=5e9a8088e2093db23432fb2bb81c2; buvid_fp_plain=undefined; CURRENT_FNVAL=4048; rpdid=|(J~J|Rm|RuJ0J'uYm~~)kmY|; b_lsid=C342BDD2_192541C1C36; bsource=search_baidu; SESSDATA=2c853358%2C1743540007%2C1c395%2Aa1CjBoQPqw1p54Ut1L3sUZ-iKwDSkFrIWeDJfsmBhzhgNoqGgTCiG-SoohDFKKH0WS5PUSVjRZY0FnQWREVTZQbDFGS0J5d2FiTU9XeVNFU0huOEZES3B1c19UaEFiT1NwYTdBSlhTZWJmczQ5bFJNaWx4YTVkcEljdS1FaVhoVnY5Ql9JNTFJbkl3IIEC; bili_jct=f4a19205ce0e5fdfd26441438b9fd811; DedeUserID=15621741; DedeUserID__ckMd5=a7b49f848348c797; sid=h6yrhg31; hit-dyn-v2=1; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgyNDcyMTQsImlhdCI6MTcyNzk4Nzk1NCwicGx0IjotMX0._g00SN27Nu3SJHAshhrK1WSCVryiM5PsYy2j1VNhSsQ; bili_ticket_expires=1728247154", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site" } for key, value in headers.items(): req.add_header(key, value) boundary = "---------------------------76447336634624058473024722700" req.add_header("Content-Type", f"multipart/form-data; boundary={boundary}") body = f"""--{boundary} Content-Disposition: form-data; name="file_up"; filename="{os.path.basename(image_path)}" Content-Type: image/jpeg <!>{image_path}<!> --{boundary} Content-Disposition: form-data; name="biz" new_dyn --{boundary} Content-Disposition: form-data; name="category" daily --{boundary} Content-Disposition: form-data; name="csrf" f4a19205ce0e5fdfd26441438b9fd811 --{boundary}-- """ self.write_multipart_body_to_request(req, body) self.log("正在发送请求...") response[0] = urllib.request.urlopen(req) self.log("请求已发送,等待响应...") except urllib.error.URLError as e: self.log(f"发生URLError: {e}") if not hasattr(e, "code"): return False response[0] = e except Exception as e: self.log(f"发生异常: {e}") return False return True def main(): root = tk.Tk() app = BilibiliUploader(root) root.mainloop() if __name__ == "__main__": main()