大妈之家最近又在自审了,虽然社区版客户端还能用,网页端也能用,但是为了防止下一次自审,以及以后的作品被ban,总要找个后备方案。(UWP客户端,确信)python脚本爬取图片,便是其中一种。
0. 环境
本文默认读者已经安装和正确配置如下环境
- python3(愤鸟的版本为3.8.2),需安装requests、lxml、urllib、selenium
- FireFox及其对应selenium WebDriver(Chrom(ium)、Edge(两个版本)、Internet Explorer、Opera 和 Safari也可以,其他浏览器不保证能用)
selenium的安装可参考这篇文章,但愤鸟建议安装好WebDriver以后,将WebDriver所在路径添加至系统环境变量里。
1. 思路
要实现爬取、下载大妈之家的漫画图片,我们需要解析、获取图片链接;为了方便查看,需要将每话的图片单独放到一个文件夹内,最好可以将每个作品使用一个文件夹存放;有余力的话,可以做出简单的防止重复下载功能。
为了实现解析、获取图片链接,我们需要分析观看漫画页面的源代码。经过实践得知,大妈之家的观看漫画页的图片为通过js动态加载,那么我们需要从以下两个方案中选择一种:用python运行js脚本从而获取图片链接;用selenium操作浏览器,使页面加载完成后取得图片链接。愤鸟选择了后一种方案,该方案的优势是:代码好写,易读;劣势是:由于要等待浏览器,因此效率较低,而且会吃内存。
为了实现将每话的图片单独放到一个文件夹内和每个作品使用一个文件夹存放,我们需要在漫画详情页获取各话链接。同时还要检测文件夹是否存在,并适时创建文件夹。
为了实现防止重复下载,我们需要设置一个标记,证明我们下载过这一话。愤鸟选择的方案是在下载完成后放置一个空白文件作为标记。
整个程序的流程为:
graph
A(开始)-->B[获取输入];
B-->C[访问作品页面];
C-->D[获取,处理作品页面源代码];
D-->E{作品存在};
E-->|存在|F((链接A));
E-->|不存在|EOF(结束);
G((链接A))-->H[获取作品信息及各话url];
H-->I[本地确认作品文件夹是否存在];
I-->L{该话已下载};
L-->|否|J[访问该话页面];
L-->|是|Q;
J-->K[页面加载完成后获取页面信息和图片url];
K-->O[下载图片];
O-->P[创建标记]
P-->Q{所有话均下载完成};
Q-->|是|EOF;
Q-->|否|L;
2. 编码
获取输入
有关代码十分简单:
name = input().split()[0]
访问作品页面、获取,处理页面源代码
通过输入,获取对应url后,使用requests访问,使用etree处理,解析xpath结构以便于提取信息。
#处理url,获取作品正确url
base_url = "https://manhua.dmzj.com/"+name;
#访问作品页面,获取源代码并处理
response = requests.get(base_url,headers=Headers)
response.encoding = 'utf-8'
html = response.text
#取得xpath结构
htmls = etree.HTML(html)
判断作品是否存在
愤鸟选用的方法是,判断页面标题,若不是404,则认为作品存在。
if(htmls.xpath('/html/head/title/text()')[0] != '404 Not Found'):
获取作品信息及各话url
通过xpath找到相应元素的信息:
作品名称
各话信息
#获取作品名称
name = htmls.xpath('//span[@class = "anim_title_text"]/a/h1/text()')[0]
#获取各话对应url
amine_urls = html.xpath('//div[@class = "cartoon_online_border"]/ul/li/a//@href')
本地确认作品文件夹是否存在
可以通过os的方法,直接判断路径的存在性。
愤鸟所用的路径为,以脚本所在当前路径为根,检测、创建相应文件夹。
同时判断作品文件夹和各话文件夹是否存在,可以省去下载失败后的大量重试时间,缺点就是会短时间内产生大量空文件夹。
#判断作品文件夹是否存在,若不存在则创建
if not os.path.exists(folder_root_url):
os.mkdir(name)
#获取标题列表
index_list = html.xpath('//div[@class = "cartoon_online_border"]/ul/li/a/text()')
for index_item in index_list:
#判断各话文件夹是否存在,若不存在则创建
if not os.path.exists(folder_root_url+index_item+'/'):
os.mkdir(folder_root_url+index_item+'/')
访问各话页面
由于需要浏览器代劳加载页面,使我们可以获取图片链接,所以这里用到了selenium(的WebDriver)。
ami_url = site_url+each+"#@page=1"
browser = webdriver.Firefox(firefox_binary='C:/Program Files/Mozilla Firefox/firefox.exe')
browser.get(ami_url)
页面加载完成后获取页面信息和图片url
需要通过WebDriver获取。
由于图片加载时间不稳定,且python无法准确识别图片加载完成的时间,因此需要使用WebDriverWait方法。这个方法可以使浏览器等待某元素加载后读取信息。需要注意的是,这个方法需要设置以秒为单位的超时(timeout)。
图片链接的获取也是使用了xpath,与上面不同的是,这里无需etree,可以直接通过WebDriver的find_elements_by_xpath
方法获取。
P.S 现版本的大妈之家web的漫画浏览页的跳转选择中直接存放了图片地址……
#确保页面充分加载后,获取图片url
img = WebDriverWait(browser,timeout=12).until(lambda d : d.find_elements_by_xpath('//select[@id="page_select"]/option'))
检测该话是否已下载
也是使用os.path.exists
进行判断。
下载图片
使用requests下载图片
#下载图片
#传入参数:图片url,图片文件夹uri
def download(url,folder_url):
file_url = 'https:'+url
file_name = url.split('/')[-1]
file_store_url = folder_url + file_name
print("当前图片:"+file_name)
request.urlretrieve(file_url,file_store_url)
完整代码
将实现以上功能的代码整合,再添加亿点细节,即得到脚本的完整代码:
import os
import re
import sys
import requests
from lxml import etree
from urllib import request
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
#吟唱前置魔法
#准备请求头
Headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0','Referer':'https://manhua.dmzj.com/'
}
#准备根url
site_url = 'https://manhua.dmzj.com'
#预处理,获取作品对应页的源代码
#传入参数:作品字符串
def index(name):
#处理url,获取作品正确url
base_url = "https://manhua.dmzj.com/"+name;
#访问作品页面,获取源代码并处理
response = requests.get(base_url,headers=Headers)
response.encoding = 'utf-8'
html = response.text
#取得xpath结构
htmls = etree.HTML(html)
#依据网页标题,检验作品是否存在
if(htmls.xpath('/html/head/title/text()')[0] != '404 Not Found'):
#获取作品名称
name = htmls.xpath('//span[@class = "anim_title_text"]/a/h1/text()')[0]
print("作品为: "+name)
clear(htmls,name)
else:
print("404")
#数据处理,获取各话url,并解析图片url。
#同时维护对应文件夹,并简单判断是否下载过
#传入参数:处理为xpath结构的html,作品名称
def clear(html,name):
cnt=0
#获取各话对应url
amine_urls = html.xpath('//div[@class = "cartoon_online_border"]/ul/li/a//@href')
folder_root_url = './'+name+'/'
#判断作品文件夹是否存在,若不存在则创建
if not os.path.exists(folder_root_url):
os.mkdir(name)
#获取标题列表
index_list = html.xpath('//div[@class = "cartoon_online_border"]/ul/li/a/text()')
for index_item in index_list:
#判断各话文件夹是否存在,若不存在则创建
if not os.path.exists(folder_root_url+index_item+'/'):
os.mkdir(folder_root_url+index_item+'/')
#对每话进行处理
for each in amine_urls:
#获取该话“名称”
index = index_list[cnt]cnt = cnt+1
folder_url = folder_root_url + index + '/'
#简单判断是否下载过,若无则进行下载
print(folder_url+index)
if not os.path.exists(folder_url+index):
#访问各话对应页面
ami_url = site_url+each+"#@page=1"
browser = webdriver.Firefox(firefox_binary='C:/Program Files/Mozilla Firefox/firefox.exe')
browser.get(ami_url)
#确保页面充分加载后,获取图片url
img = WebDriverWait(browser,timeout=12).until(lambda d : d.find_elements_by_xpath('//select[@id="page_select"]/option'))
#下载每张图片
for i in img:
download(i.get_attribute('value'),folder_url)
#创建判断文件
f = open(folder_url+index,'w')
f.close()
#关闭页面
browser.quit()
#下载图片
#传入参数:图片url,图片文件夹uri
def download(url,folder_url):
file_url = 'https:'+url
file_name = url.split('/')[-1]
file_store_url = folder_url + file_name
print("当前图片:"+file_name)
request.urlretrieve(file_url,file_store_url)
#主函数(毫无存在感)
if __name__ == '__main__':
print("dmzj爬虫\ncode by AkiPolaris\n")
print("该脚本将会于脚本当前目录生成文件夹,一部作品唯一对应一个文件夹。\n")
print("请输入作品url末端字符串\n如『向阳一隅』url为https://manhua.dmzj.com/xiangyangyiyu/,\n则输入xiangyangyiyu")
#获取作品字符串,并交给index()函数处理
name = input().split()[0]
index(name)
3. 结束
经过实际测试,脚本达到了可用级别。哪怕是对于话数特别多的漫画,根据分析网页代码可以认为,该脚本也能对其正常下载。
于是一个可靠的备胎(?)诞生了。
本文在此处和我的博客处同步发布,头图来自愤鸟自己。
一条评论
非常不错!!!!