当前位置: 移动技术网 > IT编程>脚本编程>Python > 用Python爬E站本

用Python爬E站本

2019年12月11日  | 移动技术网IT编程  | 我要评论

1.99神龙皓月,弹珠台秘技,贫乏姊妹物语

用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插件
  • 语法检查工具flake8python -m pip install flake8
  • 格式化工具autopep8python -m pip install autopep8

依赖:

  • beautifulsoup4python -m pip install beautifulsoup4
  • requestspython -m pip install requests
  • lxmlpip install lxml

二、改进内容

  1. 支持分页下载;
  2. 允许一次输入多条链接,方便批量执行;
  3. 文件名使用id+序号的方式,方便排序;
  4. 允许对同名文件跳过;
  5. 对于某些图片不稳定导致卡死问题,做了请求超时处理,允许设置超时时长和最大重新请求次数,可以超时时间短但重发次数多,或者时间长但次数少;
  6. 对于用本名创建文件夹可能存在的名称有不合法字符问题,允许检查并替换字符;
  7. 对于站点某些本的内容不和谐提示:在cookie中添加nw=1,避免重定向导致错误;
  8. 那啥代理池没有用,原先以为卡住是被反爬虫了,原来只是单纯下载卡住了,网上扒来的方法似乎也只会报错。
  9. 想到但没做的,添加传入参数,方便批处理。

三、最终代码

# -*- 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()

四、效果图

运行效果

五、参考来源

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网