前言
每次看本都要进入某网站,这实在是太麻烦了。正好学过点爬虫,不勤加练习恐荒废知识。分析了网页,编写好程序。运行一看,一些漫画居然有防爬,即将图片切割成若干部分并逆序拼接,且每张图片切割数量不尽相同。正当要放弃之时,看到一篇文章,使我醍醐灌顶。
正文
网页分析
直接进入某漫画主页,F12打开开发者工具,找到图片位置html,如下图
由此可以发现图片链接都是放在img标签中,我们从这便可获取图片链接
代码编写
1.库介绍
首先介绍需要用到的python库,如下
import requests
import re
from io import BytesIO
from PIL import Image
import os
import shutil
import hashlib
requests库用于网页源码,re用于正则表达式获取网页源码中的图片链接,io和PIL用于处理图片,
os和shutil用于文件夹操作
2.开始编写
先从获取图片链接开始,代码如下
#获取所有图片链接,漫画名称和id
def get_photo_url(url: str):
r = requests.get(url)
results = re.findall(r'data-original="(https://.*)"', r.text)
name = re.search(r'<div class="pull-left">(.*)</div>', r.text).group(1).replace("!", "").replace("?", "")
id = re.search(r".*/(.*)", url).group(1)
print(name)
return results, name, id
使用get获取网页源码,然后用正则表达式获取图片链接(列表存放),漫画名称,漫画id
之后通过图片链接将图片保存至本地,代码如下
#保存
def save(results: list, path: str, is_slice: bool, id: str):
for result in results:
filename = re.search(r".*/(.*)\.", result).group(1)#获取图片页码作为文件名
print(filename)
resp = requests.get(result)
byte_stream = BytesIO(resp.content)
im = Image.open(byte_stream)
# im.show()
if im.mode == "RGBA": #这段是cv的不知道干嘛用的
im.load() # required for png.split()
background = Image.new("RGB", im.size, (255, 255, 255))
background.paste(im, mask=im.split()[3])
if is_slice: #如果图片是切割过的,则处理一下
im = handle_img(filename, id, im)
im.save(f'{path}/{filename}.jpg', 'JPEG')
如果图片是切割过的,则调用handle_img方法,如下
#处理切割图片
def handle_img(filename, id, im):
width, height = im.size
n = get_num(id, filename)
new_im = Image.new("RGB", im.size)
im_parts = []
for i in range(n):
if i == n - 1:
part = im.crop((0, i * int(height / n), width, height))
else:
part = im.crop((0, i * int(height / n), width, (i + 1) * int(height / n)))
im_parts.append(part)
last_height = 0
for i in im_parts[::-1]:
new_im.paste(i, (0, last_height))
last_height += i.height
return new_im
#计算图片切割次数,参数为id和页码(例如第一页,00001)
def get_num(id: str, page_num: str):
a = 10
if int(id) >= 268850:
n = str(id) + page_num
n = hashlib.md5(str(n).encode()).hexdigest()
n = n[-1]
n = ord(n) % 10
if n < 10:
a = (n + 1) * 2
return a
其中get_num方法用于获取图片的切割次数,因为每张图片的切割次数不尽相同,其中id和页码均为字符串。思路参考文章
此程序有个不足,暂未找到区分漫画是否切割的方法,只能自行判断,is_slice为标志,true即为该漫画切割过
3.完整代码
仅供参考
import requests
import re
from io import BytesIO
from PIL import Image
import os
import shutil
import hashlib
#创建文件夹
def mkdir(name) -> str:
path = f"./manga/{name}"
folder = os.path.exists(path)
if folder: # 判断是否存在文件夹如果存在则删除文件夹
shutil.rmtree(path)
os.makedirs(path) # makedirs 创建文件夹
return path
#获取所有图片链接,漫画名称和id
def get_photo_url(url: str):
r = requests.get(url)
results = re.findall(r'data-original="(https://.*)"', r.text)
name = re.search(r'<div class="pull-left">(.*)</div>', r.text).group(1).replace("!", "").replace("?", "!")
id = re.search(r".*/(.*)", url).group(1)
print(name)
return results, name, id
#计算图片切割次数,参数为id和页码(例如第一页,00001)
def get_num(id: str, page_num: str):
a = 10
if int(id) >= 268850:
n = str(id) + page_num
n = hashlib.md5(str(n).encode()).hexdigest()
n = n[-1]
n = ord(n) % 10
if n < 10:
a = (n + 1) * 2
return a
#保存
def save(results: list, path: str, is_slice: bool, id: str):
for result in results:
filename = re.search(r".*/(.*)\.", result).group(1)
print(filename)
resp = requests.get(result)
byte_stream = BytesIO(resp.content)
im = Image.open(byte_stream)
# im.show()
if im.mode == "RGBA": #这段是cv的不知道干嘛用的
im.load() # required for png.split()
background = Image.new("RGB", im.size, (255, 255, 255))
background.paste(im, mask=im.split()[3])
if is_slice: #如果图片是切割过的,则处理一下
im = handle_img(filename, id, im)
im.save(f'{path}/{filename}.jpg', 'JPEG')
#处理切割图片
def handle_img(filename, id, im):
width, height = im.size
n = get_num(id, filename)
new_im = Image.new("RGB", im.size)
im_parts = []
for i in range(n):
if i == n - 1:
part = im.crop((0, i * int(height / n), width, height))
else:
part = im.crop((0, i * int(height / n), width, (i + 1) * int(height / n)))
im_parts.append(part)
last_height = 0
for i in im_parts[::-1]:
new_im.paste(i, (0, last_height))
last_height += i.height
return new_im
url = "" #漫画主页面链接
results, name, id = get_photo_url(url)
path = mkdir(name)
save(results, path, is_slice=True, id=id)