zoukankan      html  css  js  c++  java
  • 用Python爬E站本

    用Python爬E站本

    一、前言

    参考并改进自 OverJerry 大佬的 教你怎么用Python爬取E站的本子_OverJerry

    本文为技术学习记录,不提供访问无存在网站的任何方法,也不包含不和谐内容。

    环境:

    • Python版本为从Win10应用商店安装的Python3.7.5,大概若无已安装版本,cmd输入python就会自动打开商店页面吧。不用设置PATH,但无法使用 py 命令。安装的位置在 C:Users<用户名>AppDataLocalMicrosoftWindowsApps,pip安装的模块位置大概在 C:users<用户名>appdatalocalpackages
    • 编辑器为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'[\/:*?"<>|
    ]', 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》
    ||[日文名] 《%s》
    ||共 %d 页' %
                      (title, title2, pagecount))
                title = re.sub(r'[\/:*?"<>|
    ]', 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漫画目录页面的地址。>
    ')
    
    
    def main():
        # init_proxies()  # 初始化ip池
        # print(str(ip_list))
        urls = []  # 允许批量处理,方便睡觉时下载
        url = input('<请输入链接(输入空白内容结束):>
    ')
        while url != "":
            urls.append(url)
            url = input('== 已输入链接列表 ==
    ' + str(urls) + '
    <请输入链接(输入空白内容结束):>
    ')
        print('== 输入结束 ==')
        if(len(urls) > 0):
            for item in urls:
                getGallery(item)
            main()
        else:
            print('== 结束运行 ==')
    
    
    main()
    
    

    四、效果图

    运行效果

    五、参考来源

          签名

    我是 Albertiy

    一只永远的萌新

    当白日的余温散去

    我的灯未熄

    我的心未倦

    我的梦想正在升起

    欢迎与我互动,在下方评论区留言

    一起进步

  • 相关阅读:
    Python_反射
    Python_面向对象_类2
    Python_面向对象_类1
    Python_logging模块
    Python_子进程管理subprocess模块
    Python_python内置加密模块
    Python_configparser模块
    Python_xml
    Python_shelve模块
    Python_shutil模块
  • 原文地址:https://www.cnblogs.com/Albertiy/p/12022303.html
Copyright © 2011-2022 走看看