Last active
November 16, 2021 10:54
-
-
Save prnake/3671b73af3df2982d751369014a8c826 to your computer and use it in GitHub Desktop.
Bilibili漫画下载
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Source: https://github.com/LaMP57/BilibiliMangaDownload | |
# 支持从连续使用多个账号的网页版 SESSDATA,或者使用单个手机抓包得到的 Token,自动购买/解锁章节,验证下载图片完整性连续下载一本漫画的所有章节的功能。 | |
import json | |
import os | |
import re | |
import requests | |
import zipfile | |
from io import BytesIO | |
from PIL import Image | |
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED | |
AutoBuy = False | |
PARAM_ID = "appkey=***&mobi_app=android_comic&version=4.7.0&build=***&channel=xiaomi&platform=android&device=android&buvid=***&machine=Xiaomi%2BM2011K2C&access_key=***&is_teenager=0&ts=***" | |
USE_SESSION = False | |
if not PARAM_ID: | |
PARAM_ID = "device=pc&platform=web" | |
USE_SESSION = True | |
URL_DETAIL = "https://manga.bilibili.com/twirp/comic.v2.Comic/ComicDetail?" + PARAM_ID | |
URL_IMAGE_INDEX = "https://manga.bilibili.com/twirp/comic.v1.Comic/GetImageIndex?" + PARAM_ID | |
URL_MANGA_HOST = "https://manga.hdslb.com" | |
URL_IMAGE_TOKEN = "https://manga.bilibili.com/twirp/comic.v1.Comic/ImageToken?" + PARAM_ID | |
URL_MANGA_BUY_INFO = "https://manga.bilibili.com/twirp/comic.v1.Comic/GetEpisodeBuyInfo?" + PARAM_ID | |
URL_MANGA_BUY = "https://manga.bilibili.com/twirp/comic.v1.Comic/BuyEpisode?" + PARAM_ID | |
def downloadImage(url, path): | |
if not os.path.exists(path): | |
r = requests.get(url, stream = True, cookies = cookies) | |
if r.content.endswith(b'\xff\xd9'): | |
f = open(path, 'wb') | |
for chunk in r.iter_content(chunk_size = 1024): | |
if chunk: | |
f.write(chunk) | |
f.close() | |
else: | |
print("[ERROR]" + path + " 下载失败,重试中...") | |
os.remove(path) | |
downloadImage(url, path) | |
def getMangaInfo(mcNum): | |
data = requests.post(URL_DETAIL, data = {'comic_id': mcNum}).json()['data'] | |
data['ep_list'].reverse() | |
return filterStr(data['title']), data['ep_list'] | |
def getImages(mcNum, chNum): | |
data = requests.post(URL_IMAGE_INDEX, data = {'ep_id': chNum}, cookies = cookies).json()['data'] | |
data = bytearray(requests.get(data['host'] + data['path']).content[9:]) | |
key = [chNum&0xff, chNum>>8&0xff, chNum>>16&0xff, chNum>>24&0xff, mcNum&0xff, mcNum>>8&0xff, mcNum>>16&0xff, mcNum>>24&0xff] | |
for i in range(len(data)): | |
data[i] ^= key[i%8] | |
file = BytesIO(data) | |
zf = zipfile.ZipFile(file) | |
data = json.loads(zf.read('index.dat')) | |
zf.close() | |
file.close() | |
return data['pics'] | |
def getURLwithToken(url): | |
data = requests.post(URL_IMAGE_TOKEN, data = {"urls": "[\""+url+"\"]"}, cookies = cookies).json()["data"][0] | |
return '%s?token=%s' % (data["url"], data["token"]) | |
def getChapterName(chapterList, chapterID): | |
for chapte in chapterList: | |
if chapte['id'] == chapterID: | |
return filterStr(chapte['short_title'] + ' ' + chapte['title']) | |
return None | |
def downloadChapter(mcNum, chapterID, chapterName): | |
if not(os.path.exists('downloads/%s/%s' % (mangaTitle, chapterName))) or len(os.listdir('downloads/%s/%s' % (mangaTitle, chapterName))) == 0: | |
if not(os.path.exists('downloads/%s/%s' % (mangaTitle, chapterName))): | |
os.mkdir('downloads/%s/%s' % (mangaTitle, chapterName)) | |
try: | |
if AutoBuy == True: | |
buy_content = requests.post(URL_MANGA_BUY, data = {'ep_id': chapterID,'buy_method':1}, cookies = cookies).content | |
print(json.loads(buy_content)["msg"].encode().decode()) | |
while json.loads(buy_content)["code"] != 0: | |
buy_info_content = json.loads(requests.post(URL_MANGA_BUY_INFO, data = {'ep_id': chapterID}, cookies = cookies).content) | |
coupon_num = buy_info_content["data"]["remain_coupon"] | |
if int(coupon_num) == 0: | |
print("无剩余优惠卷,请输入新的SESSDATA:") | |
cookies['SESSDATA'] = input().strip() | |
else: | |
print("剩余优惠卷%s张,自动使用" % coupon_num) | |
buy_content = requests.post(URL_MANGA_BUY, data = {'ep_id': chapterID,'buy_method':2,'coupon_id':buy_info_content["data"]["recommend_coupon_id"],'auto_pay_gold_status':1}, cookies = cookies).content | |
print(json.loads(buy_content)["msg"].encode().decode()) | |
print('[INFO]%s开始下载' % chapterName) | |
imagesURLs = getImages(mcNum, chapterID) | |
imagesIndexLength = len(str(len(imagesURLs))) | |
tasks = list() | |
for idx, url in enumerate(imagesURLs, 1): | |
fullURL = getURLwithToken(url) | |
path = 'downloads/%s/%s/%s.jpg' % (mangaTitle, chapterName, str(idx).zfill(imagesIndexLength)) | |
tasks.append(pool.submit(downloadImage, fullURL, path)) | |
wait(tasks, return_when = ALL_COMPLETED) | |
print('[INFO]%s下载完成' % chapterName) | |
except: | |
print('[ERROR]%s下载失败' % chapterName) | |
os.rmdir('downloads/%s/%s' % (mangaTitle, chapterName)) | |
else: | |
print('[INFO]%s已下载' % chapterName) | |
def filterStr(name): | |
return re.sub(r'[\/:*?"<>|]', '', name).strip().rstrip('.') | |
if __name__ == "__main__": | |
pool = ThreadPoolExecutor(max_workers=4) | |
if not(os.path.exists('downloads')): | |
os.mkdir('downloads') | |
print('请输入mc号:') | |
print('mc', end='') | |
mcNum = int(input()) | |
mangaTitle, chapterList = getMangaInfo(mcNum) | |
print('[INFO]', mangaTitle) | |
if not(os.path.exists('downloads/%s' % mangaTitle)): | |
os.mkdir('downloads/%s' % mangaTitle) | |
# print('1.下载单章\n2.下载全本') | |
# downloadAll = input() | |
# if downloadAll == '1': | |
# downloadAll = False | |
# print('请输入要下载的章节号:') | |
# chapterID = int(input()) | |
# else: | |
# downloadAll = True | |
downloadAll = True | |
# print('1.均为免费章节\n2.包含付费章节') | |
# needsLogin = input() | |
needsLogin = "2" | |
cookies = dict() | |
if needsLogin == '2' and USE_SESSION: | |
print('请按说明粘贴SESSDATA:') | |
cookies['SESSDATA'] = input().strip() | |
if downloadAll: | |
for chapter in chapterList: | |
chapterName = filterStr(chapter['short_title'] + ' ' + chapter['title']) | |
downloadChapter(mcNum, chapter['id'], chapterName) | |
else: | |
chapterName = getChapterName(chapterList, chapterID) | |
downloadChapter(mcNum, chapterID, chapterName) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import re | |
import shutil | |
name = "大正少女御伽话" | |
os.mkdir(name+"_out") | |
d = os.listdir(name) | |
d.remove('.DS_Store') | |
d = sorted(d, key=lambda y: int(re.findall(r'\d+', y)[0])) | |
print(d) | |
cnt = 1 | |
for i in d: | |
s = os.listdir(name+"/"+i) | |
s = sorted(s, key=lambda y: int(re.findall(r'\d+', y)[0])) | |
for j in s: | |
shutil.copy(name+"/"+i+"/"+j,name + "_out/" + str(cnt).zfill(5) + ".jpg") | |
cnt += 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment