本知识库目录
Python3 行展开代码收起代码
1 2 3
import io, hashlib, requests
from PIL import Image
from math import floor
用到的模块
Python25 行展开代码收起代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
def get_num(album_id:str, index:str):
normalCutNum = 10 # 默认切割数
aIndex = album_id + index
album_id = int(album_id)
aIndex = ord(hashlib.md5(aIndex.encode()).hexdigest()[-1]) # md5编码后 取最后一位 获取所在unicode码表位置
if album_id >= 268850 and album_id <= 421925:
aIndex %= 10
elif album_id >= 421926:
aIndex %= 8
switcher = {
0: 2,
1: 4,
2: 6,
3: 8,
4: 10,
5: 12,
6: 14,
7: 16,
8: 18,
9: 20
}
normalCutNum = switcher.get(aIndex, normalCutNum)
return normalCutNum
输入本子id以及图片编号(如:00001),返回的值为分割数。
Python32 行展开代码收起代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
def hb(img_data, cut_num):
# 打开图片
bin_stream = io.BytesIO(img_data)
img = Image.open(bin_stream)
img_width, img_height = img.size
# 计算切割数量和偏移量
offset = img_height % cut_num # 最后一张图片较高需要偏移高度
# 创建新的空白图像用于拼接
decoded_img = Image.new('RGB', (img_width, img_height))
# 分割并重新拼接
for i in range(cut_num):
cut_height = floor(img_height / cut_num)
y_coordinate = cut_height * i
end_coordinate = img_height - cut_height * (i + 1) - offset
if i == 0:
cut_height += offset
else:
y_coordinate += offset
# 裁剪和粘贴
crop_img = img.crop((0, end_coordinate, img_width, end_coordinate + cut_height))
decoded_img.paste(crop_img, (0, y_coordinate))
# 将处理后的图片保存为字节数据
with io.BytesIO() as buffer:
decoded_img.save(buffer, format="PNG")
data = buffer.getvalue()
return data
输入本子的图片和图片的字节数据,返回拼接好后的字节数据。
封装到一起的话为:
Python83 行展开代码收起代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
import io
import hashlib
from PIL import Image
from math import floor
def encode_JM_single_image(album_id, img_data: bytes, index: (str | int)):
"""
对单张图片进行处理,将图片按照指定的切割数分割并重新拼接。
:param album_id: 专辑 ID,用于算法计算。
:param img_data: 图像数据,字节形式。
:param index: 图像索引,作为分割计算的一部分。
:return: 处理后的图片数据,字节形式。
"""
def get_num(album_id: str, index: str):
"""
计算切割数量的逻辑,基于 album_id 和 index。
"""
normalCutNum = 10 # 默认切割数
aIndex = album_id + index
album_id = int(album_id)
aIndex = ord(hashlib.md5(aIndex.encode()).hexdigest()[-1]) # md5编码后取最后一位
if album_id >= 268850 and album_id <= 421925:
aIndex %= 10
elif album_id >= 421926:
aIndex %= 8
switcher = {
0: 2,
1: 4,
2: 6,
3: 8,
4: 10,
5: 12,
6: 14,
7: 16,
8: 18,
9: 20
}
normalCutNum = switcher.get(aIndex, normalCutNum)
return normalCutNum
# 确保 index 是 5 位数的字符串
if isinstance(index, int):
index = str(index).zfill(5)
else:
index = index.zfill(5)
# 打开图片
bin_stream = io.BytesIO(img_data)
img = Image.open(bin_stream)
img_width, img_height = img.size
# 计算切割数量和偏移量
cut_num = get_num(album_id, index)
offset = img_height % cut_num # 偏移高度
# 创建新的空白图像用于拼接
decoded_img = Image.new('RGB', (img_width, img_height))
# 分割并重新拼接
for i in range(cut_num):
cut_height = floor(img_height / cut_num)
y_coordinate = cut_height * i
end_coordinate = img_height - cut_height * (i + 1) - offset
if i == 0:
cut_height += offset
else:
y_coordinate += offset
# 裁剪和粘贴
crop_img = img.crop((0, end_coordinate, img_width, end_coordinate + cut_height))
decoded_img.paste(crop_img, (0, y_coordinate))
# 将处理后的图片保存为字节数据
with io.BytesIO() as buffer:
decoded_img.save(buffer, format="PNG")
data = buffer.getvalue()
return data
HTML, XML7 行展开代码收起代码
1 2 3 4 5 6 7
未加密的本子123456
<div class="center scramble-page spnotice_chk" id="00001.webp">
<img src="https://cdn-msp.18comic.vip/media/photos/123456/00001.webp" data-original="https://cdn-msp.18comic.vip/media/photos/123456/00001.webp" id="album_photo_00001.webp" class="lazy_img img-responsive-mw lazy-loaded" data-page="0"></div>
加密过的本子954211
<div class="center scramble-page spnotice_chk" id="00001.webp">
<img src="https://cdn-msp2.18comic.vip/media/photos/954211/00001.webp" data-original="https://cdn-msp2.18comic.vip/media/photos/954211/00001.webp" id="album_photo_00001.webp" class="lazy_img img-responsive-mw lazy-loaded hide" data-page="0">
<canvas width="960" height="796" style="width: 960px; display: block;" data="eyJ1cmwiOiJodHRwczovL2Nkbi1tc3AyLjE4Y29taWMudmlwL21lZGlhL3Bob3Rvcy85NTQyMTEvMDAwMDEud2VicCIsImFyZ3MiOltbMCw3MjYsOTYwLDcwLDAsMCw5NjAsNzBdLFswLDY2MCw5NjAsNjYsMCw3MCw5NjAsNjZdLFswLDU5NCw5NjAsNjYsMCwxMzYsOTYwLDY2XSxbMCw1MjgsOTYwLDY2LDAsMjAyLDk2MCw2Nl0sWzAsNDYyLDk2MCw2NiwwLDI2OCw5NjAsNjZdLFswLDM5Niw5NjAsNjYsMCwzMzQsOTYwLDY2XSxbMCwzMzAsOTYwLDY2LDAsNDAwLDk2MCw2Nl0sWzAsMjY0LDk2MCw2NiwwLDQ2Niw5NjAsNjZdLFswLDE5OCw5NjAsNjYsMCw1MzIsOTYwLDY2XSxbMCwxMzIsOTYwLDY2LDAsNTk4LDk2MCw2Nl0sWzAsNjYsOTYwLDY2LDAsNjY0LDk2MCw2Nl0sWzAsMCw5NjAsNjYsMCw3MzAsOTYwLDY2XV19"></canvas></div>
可以看到jm的加密是在网页中实现的,不过由于我不懂html和css,js所以没找到jm的加密css。 data是由base64加密过的图片链接和分割坐标,通过模拟浏览器应该可以获得,我没有去尝试。 可以看到是否加密的区别在于class中是否有hide,有了这一点再结合上面的分割算法即可完成判断以及还原,不过class标签只有在photo中才有。