用Python爬E站本
程序员文章站
2024-01-10 20:03:46
用Python爬E站本 一、前言 参考并改进自 OverJerry 大佬的 "教你怎么用Python爬取E站的本子_OverJerry" 。 本文为技术学习记录,不提供访问无存在网站的任何方法,也不包含不和谐内容。 环境: Python版本为从Win10应用商店安装的 Python3.7.5 ,大概 ......
用python爬e站本
一、前言
参考并改进自 overjerry 大佬的 教你怎么用python爬取e站的本子_overjerry。
本文为技术学习记录,不提供访问无存在网站的任何方法,也不包含不和谐内容。
环境:
- python版本为从win10应用商店安装的python3.7.5,大概若无已安装版本,cmd输入python就会自动打开商店页面吧。不用设置path,但无法使用
py
命令。安装的位置在c:\users\<用户名>\appdata\local\microsoft\windowsapps\
,pip安装的模块位置大概在c:\users\<用户名>\appdata\local\packages\
- 编辑器为vscode,使用推荐的python插件
- 语法检查工具flake8:
python -m pip install flake8
- 格式化工具autopep8:
python -m pip install autopep8
依赖:
- beautifulsoup4:
python -m pip install beautifulsoup4
- requests:
python -m pip install requests
- lxml:
pip install lxml
二、改进内容
- 支持分页下载;
- 允许一次输入多条链接,方便批量执行;
- 文件名使用id+序号的方式,方便排序;
- 允许对同名文件跳过;
- 对于某些图片不稳定导致卡死问题,做了请求超时处理,允许设置超时时长和最大重新请求次数,可以超时时间短但重发次数多,或者时间长但次数少;
- 对于用本名创建文件夹可能存在的名称有不合法字符问题,允许检查并替换字符;
- 对于站点某些本的内容不和谐提示:在cookie中添加nw=1,避免重定向导致错误;
- 那啥代理池没有用,原先以为卡住是被反爬虫了,原来只是单纯下载卡住了,网上扒来的方法似乎也只会报错。
- 想到但没做的,添加传入参数,方便批处理。
三、最终代码
# -*- coding: utf-8 -*- # ehentai本子爬取,学习from:https://blog.csdn.net/weixin_41732074/article/details/87287726 import requests import os import re import time from bs4 import beautifulsoup # import random # import multiprocessing # 默认请求头 headers = {'user-agent': 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/78.0.3904.108 safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'cookie': 'nw=1', # 处理是否查看不宜内容的检查,需要写入cookie,不能用cookies直接写。 'upgrade-insecure-requests': '1', # 用于从http到https转换允许通知给服务器 'dnt': '1'} # 禁止追踪 rootdir = 'e:/mygallery/comic/' overwrite = false # 当文件名存在时是否覆盖重写 replacechar = '_' # 用于替换不当文件名的字符 conndelay = 5 # 连接服务器最大秒数 readdelay = 30 # 读取最大秒数 maxretry = 2 # 下载单图失败时重试次数 ip_list = [] # 代理ip池 # def get_ip_list(url, headers): # 从匿名ip提供网站获取ip列表 # web_data = requests.get(url, headers=headers) # soup = beautifulsoup(web_data.text, 'lxml') # ips = soup.find_all('tr') # ip_list = [] # for i in range(1, len(ips)): # ip_info = ips[i] # tds = ip_info.find_all('td') # ip_list.append(tds[1].text + ':' + tds[2].text) # return ip_list # def get_random_ip(ip_list): # 生成随机ip加端口号 # proxy_list = [] # for ip in ip_list: # proxy_list.append('http://' + ip) # proxy_ip = random.choice(proxy_list) # proxies = {'http': proxy_ip} # return proxies # def init_proxies(): # 初始化随机代理 # url = 'http://www.xicidaili.com/nn/' # headers = { # 'user-agent': 'mozilla/5.0 (windows nt 6.1; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/53.0.2785.143 safari/537.36' # } # global ip_list # ip_list = get_ip_list(url, headers=headers) # # proxies = get_random_ip(ip_list) # # print(proxies) def savefile(url, path): # 保存文件 # print('目标链接: ' + url) # 代理, 超时 , proxies=get_random_ip(ip_list), timeout=(180, 3000) response = requests.get(url, headers=headers, timeout=(conndelay, readdelay)) with open(path, 'wb') as f: # 只写二进制文件,存在则重写,不存在则创建 f.write(response.content) f.flush() def getpicurl(url): # 获取图片源 site_2 = requests.get(url, headers=headers) content_2 = site_2.text soup_2 = beautifulsoup(content_2, 'lxml') imgs = soup_2.find_all(id="img") # 图片的id正是img for img in imgs: picsrc = img['src'] return picsrc def getpiclist(url): # 获取图片分页 site = requests.get(url, headers=headers) content = site.text soup = beautifulsoup(content, 'lxml') # 获取当前分页所有gdtm类,gdtm是eh的默认小缩略图类,gdtl是eh的大缩略图类;find_all()返回一个包含元素的列表 divs = soup.find_all(class_='gdtm') imgcount = 0 # 图片计数器 for div in divs: imgcount = imgcount + 1 print('||共 %d 张图,开始下载...' % (imgcount)) title = re.sub(r'[\\/:*?"<>|\r\n]', replacechar, soup.h1.get_text()) imgnum = 0 i = 0 for div in divs: picurl = div.a.get('href') picalt = div.a.img.get('alt') # 获取链接最右边一段,形如<漫画id-图片序号>,因图片序号前确少0可能导致排序问题,使用alt拼接 picname = picurl.rpartition('/')[2].rpartition('-')[0] + '-' + picalt imgnum = imgnum + 1 print('>> saving:' + picname + '.jpg') picpath = '%s%s/%s.jpg' % (rootdir, title, picname) try: # 非覆写模式下,判断文件是否存在 if not overwrite and os.path.exists(picpath) and os.path.isfile(picpath): print('already exists <<') else: savefile(getpicurl(picurl), picpath) # except requests.exceptions.connectionerror: # print('链接失败') # print('failed <<') # time.sleep(1) # except requests.exceptions.connecttimeout: # print('链接超时') # print('failed <<') # time.sleep(1) # except requests.exceptions.readtimeout: # print('返回数据超时') # print('failed <<') # time.sleep(1) except exception as e: print(e) if(maxretry < 1): print('failed <<') time.sleep(1) for ri in range(0, maxretry): # 重获链接尝试下载 try: print('>> retry times ' + str(ri + 1) + ':') savefile(getpicurl(picurl), picpath) except exception as e2: print(e2) if(ri == maxretry - 1): print('failed <<') time.sleep(1) else: # 下载成功,结束循环 print('succeed <<') i = i + 1 break else: print('succeed <<') i = i + 1 print('||本页共下载 %d 个文件,其中 %d 个成功。' % (imgnum, i)) return [imgnum, i] def getgallery(url): # 主页,输入url if (url.find('https://e-hentai.org/g/') != -1): url = url.partition('?p')[0] # 从参数出现的第一个位置起,将字符串分成包含前中后三个元素的元组 print('== 正在获取内容...==') try: site = requests.get(url, headers=headers) # print(str(site.cookies)) # print(str(site.headers)) content = site.text # 推荐使用lxml解析器解析而不是默认的html解析器,更快,更强 soup = beautifulsoup(content, 'lxml') # 获取分页数,ptds是当前页的class,不是最后一页的;ptt是头部页码table的类,ptd是底部页码table类名 pages = soup.find(class_='ptt').find_all('a') # for link in pages: # print(link.get_text()) # 获取列表倒数第二个项,对应页码最大数值 pagecount = int(pages[len(pages) - 2].get_text()) # 获取标题,gn是大标题,gj是日文标题 title = str(soup.h1.get_text()) title2 = str(soup.find(id="gj").get_text()) print('||[漫画名] 《%s》\n||[日文名] 《%s》\n||共 %d 页' % (title, title2, pagecount)) title = re.sub(r'[\\/:*?"<>|\r\n]', replacechar, title) # 处理windows不支持的文件名 if not os.path.exists(rootdir + title): # 创建目标文件夹 os.mkdir(rootdir + title) except exception as e: print(e) print('== 未知错误!已停止解析。==') else: totalfile = 0 succeedfile = 0 for pagenum in range(0, pagecount): # range是从参数1到参数2前一个的范围,且参数2须大于参数1 print('||当前第 %d 页' % (pagenum + 1)) targeturl = url if pagenum != 0: # 不是第一页,需加上页码get参数 targeturl = url + '?p=' + str(pagenum) returnargs = getpiclist(targeturl) totalfile += returnargs[0] succeedfile += returnargs[1] print('== 《%s》下载完成!共 %d 个文件,其中 %d 个成功!==' % (title, totalfile, succeedfile)) else: print('<错误:"' + url + '" 不是一个有效的eh漫画目录页面的地址。>\n') def main(): # init_proxies() # 初始化ip池 # print(str(ip_list)) urls = [] # 允许批量处理,方便睡觉时下载 url = input('<请输入链接(输入空白内容结束):>\n') while url != "": urls.append(url) url = input('== 已输入链接列表 ==\n' + str(urls) + '\n<请输入链接(输入空白内容结束):>\n') print('== 输入结束 ==') if(len(urls) > 0): for item in urls: getgallery(item) main() else: print('== 结束运行 ==') main()
四、效果图
五、参考来源
上一篇: Flask的基本使用