吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3181|回复: 23
收起左侧

[Python 转载] 基于selenium和urllib的漫画爬取思路分享

  [复制链接]
roshinntou 发表于 2021-6-11 12:18
本帖最后由 roshinntou 于 2021-6-11 16:14 编辑

  • 前言
天下无双漂亮的老婆交代下任务,让我把漫画《两不疑》给她下载出来保存到手机里看。

我搜到了可以资源网站,但是快200章,用手下载得到什么时候。只能python抓取了,但是这些漫画网站不会直接把图片地址放到html代码里,而是会进行JS动态加载,而JS很多都进行了复杂的混淆、加密、验证,我本身也不是程序员出身,自学js再进行分析这种活做不来,只能取巧用selenium来帮我获取图片的地址,反正你网站再怎么加密,最后都得展示出来给用户看的嘛。




  • 出现使用的主要技术

    本次抓取涉及到的第三方库,或技术有:


    1、selenium的相关操作
    2、lxml的etree解析并使用xpath获取元素
    3、基础的 urllib.request 库,获取网页html代码
    4、基础 os 库,操作文件
    5、基础 re 库,正则表达式相关
    6、还有可能用到PIL库,对于img图片的操作


  • 目标情况
    目标网站提供漫画在线浏览服务,《两不疑》是长图形式,每页一张单图,可以使用 方向键的“左、右“键快速翻页,也可以使用”z,x“键快速切换章节,图片的上下各有一个标明当前页码、全部页码的P标签标记。

图片上传失败,就不上图了

    但是,图片地址没有直接写进网页html代码里,而是通过js动态加载。
    同时,图片虽然链接最后的后缀是jpg,可是实际上是webp格式。


  • 解题思路

    根据已知条件,我们决定使用selenium进行简单粗暴的爬取,首先,漫画有章节目录页。


    第一步,根据章节目录信息,读取全部章节的信息,然后在for循环里一章一章地单独打开,打开时使用selenium打开每一张的网页,然后根据xpath获取图片的url,每次获取完毕后,翻页获取下一张的图片。
    翻页时,可以根据网页信息直到这一章一共有多少张图片,然后刨除第一张,翻总页数-1张就可以到最后一页。


    翻页可以获取”下一页“的按钮,然后selenium模拟鼠标左键单击。不过网站提供给我们更简单的翻页方式,可以selenium里全局事件控制键盘方向键右键来快速翻页。


    翻页时不需要等待加载图片,翻页后可以立即获取到图片src。直接快速翻页+保存即可。




    第二步,selenium获取一章的全部图片地址数组后,使用request循环下载图片并保存到固定位置,普通的手机漫画软件支持直接rar压缩包播放,只需要保证每一章,和章内的每一页按顺序保存即可。


    第三部,我还没做,下载的是webp格式图片,需要用pil库的image来转成png或者jpg,我使用Anaconda开发,pil库本身不支持打开webp,需要单独下webp包,但是我这边下载失败很多次,索性就不弄了。之后用python3.7的基础ide重写写一个批量webp转png的方法就行了。


  • 缺点与待改进

    1、正如上面说的,下载的webp格式,需要转png,其实pil库只需要3行代码就行了,各位可以自己改动一下。
    2、没有开多线程,线程间同步数据需要额外写代码。我的思路是先保存所有的图片章节和链接信息,然后多线程直接读取这个文件来下载,下载时删除这个信息,下载成功就过了继续,下载失败就把这个信息重新写进文件里。

  • 代码如下

[Python] 纯文本查看 复制代码
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 11 10:13:43 2021

@author: kkrdfai
"""


from lxml import etree
import urllib.request
import os
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import re

mainUrl = 'https://www.gufengmh8.com'
referer = 'https://www.gufengmh8.com/'

"""
get_html_code 获取url对应HTML代码的方法

Args:
    url:
        网页的连接地址

Returns:
    htmlcode:
        网页的html代码,String型数据
"""
def get_html_code(url):
    max_count = 6
    htmlcode = ''
    for i in range(max_count):
        print('No.'+str(i)+'   start load:' + url)
        try:
            req_one = urllib.request.Request(url)
            req_one.add_header('User-Agent', 'Mozilla/6.0')
            res_one = urllib.request.urlopen(req_one, timeout=60)
            htmlcode = res_one.read().decode('utf-8')
            res_one.close()
            break
        except:
            if i < max_count:
                continue
            else:
                print('多次重复读取网页信息失败')
    time.sleep(0.1)
    return htmlcode

"""
makedir 创建文件目录的方法

Args:
    num:
        目录名,章节名,可以传入int或者string
"""
def makedir(num):
    if os.path.exists(str(num)):
        print('检测到:' + str(num) + '目录已建立,跳过该任务。')
        pass
    else:
        print('创建目录:' + str(num))
        os.mkdir(str(num))

"""
downLoadImg 下载图片的方法

Args:
    url:
        网页的连接地址
    picdir:
        目录名
    index:
        图片编号,也是图片的文件名
"""
def downLoadImg(url, picdir,index):
    header = {'Referer': referer, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',}

    req = urllib.request.Request(url, headers=header)

    Max_Num = 6
    for i in range(Max_Num):
        try:
            req = urllib.request.urlopen(req, timeout=60)
            
            
            #虽然网页图片内的连接最后的后缀是jpg,但是根据二进制码明显发现不是jpg,右键保存发现是webp格式。所以下载的时候直接保存为webp格式
            
            with open(picdir + '/' + str(index)+'.webp', 'wb') as f:
                f.write(req.read())
                print('图片:' + picdir + '/' + str(index) + '.webp保存完毕')
            time.sleep(1)
            break
        except:
            if i < Max_Num - 1:
                continue
            else:
                print('URLError: <urlopen error timed out> All times is failed ')
                
                
"""
get_chapters_list 下载的主要逻辑方法

Args:
    url:
        网页的连接地址,这里需要传入漫画目录的url
"""
def get_chapters_list(url):
    #获取目录网页的全部html代码
    htmlcode = get_html_code(url)
    #etree解析html代码
    html = etree.HTML(htmlcode)
    #根据xpath获取全部的章节
    chapters = html.xpath('//*[@id="chapter-list-54"]/li/a')
        
    #开始循环,不断的通过每一章的url来调用selenium读取漫画图片的地址
    for i in chapters:
        #刚刚的xpath获取的是<a>标签,所以直接可以获取<a>标签的href属性,现在获得的就是这一章的URL
        url = i.get('href')
        # i 是父级<a>标签, 章节的地址可以直接获得,但是章节名称是a标签的span子节点的text内容,需要xpath获取下面的span标签,进而获取章节名称
        title = i.xpath('./span')[0].text
        
        print('开始读取:'+title+"  地址:"+url)
        
        #调用使用selenium的方法来获取这一个章节里的全部url,然后返回一个图片url的list
        #注意传入参数,之前<a>标签获取的地址是相对路径,需要加上主域名才能正常访问
        piclist = get_picurl_by_browser(mainUrl+url)
        
        #设定初始页码
        index = 1
        
        #进一步循环,通过刚刚获得的全部图片连接,一张一张下载图片并根据index页码保存文件
        for j in piclist:
            print('开始下载:'+j)
            #创建章节文件夹
            makedir(title)
            #下载图片
            downLoadImg(j, title,index)
            #页面增加
            index+=1
    



"""
get_picurl_by_browser 通过章节地址来使用selenium自动读取这一章所有图片地址的方法

Args:
    ch_url:
        章节首页的URL

Returns:
    urllist:
        获取到的全部图片地址,数据类型为数组
"""
def get_picurl_by_browser(ch_url):
    #创建空数组
    urllist = []
    #webdriver调用谷歌chrome
    browser = webdriver.Chrome()
    
    #打开漫画章节首页
    browser.get(ch_url)
    
    #等待1秒钟,这里等待并不是为了等待图片加载,selenium会在网页加载完成后开始执行之后的代码,这里的等待是为了防止偶尔出现加载错误
    time.sleep(1)
    
    #通过xpath获取图片元素
    img = browser.find_element_by_xpath('//*[@id="images"]/img')
    
    #获得元素后,读取图片元素的地址,这次获取是获取首页的图片
    imgurl = img.get_attribute('src')
    #降首页图片的地址加入数组
    urllist.append(imgurl)
    
    #这里是获取这一章节一共有多少页漫画,获取到的是类似 (1/20)这样的网页内页数提示,下面这个是获取提示的p标签元素
    pageElement = browser.find_element_by_xpath('//*[@id="images"]/p')
    #从<p>标签里获取文本信息
    pageString = pageElement.get_attribute('innerText')
    
    #使用正则表达式获取上面信息中心的全部数字
    pattern = re.compile(r'\d+')
    pageNum = pattern.findall(pageString)
    
    #第一个数字永远是1,我们需要的总页码是第二个数字,也就是下标为1的元素。同时,第一页已经看到了,只需要翻总页数-1 页就可以看到最后一页了
    pagecount = int(pageNum[1])-1
    
    #循环开始翻页获取每一张图片的地址
    for i in  range(pagecount):
    
        time.sleep(1)
        
        #网站支持方向键翻页,可以使用selenium调用方向键右键翻页。
        #这里注意,不要获取图片元素,然后在图片上send_keys,我们翻页是全局时间翻页的。
        ActionChains(browser).send_keys(Keys.RIGHT).perform()
        
        #翻页后获取图片地址
        img = browser.find_element_by_xpath('//*[@id="images"]/img')
        imgurl = img.get_attribute('src')
        #继续保存进数组
        urllist.append(imgurl)
    
    #本章节的全部图片都获取完毕后,关闭窗口
    browser.close()
    return urllist


if __name__ == '__main__':
    urlch = "https://www.gufengmh8.com/manhua/liangbuyi/#chapters"
    get_chapters_list(urlch)
    





经老铁提醒,发现虽然img标签没有直接写在html代码里,但是那个网站直接在html写下了章节内所有图片的地址。明文写下的,可以直接获取到。


好吧,等于直接不用selenium也可以直接下载了。


我改了一下代码:


[Python] 纯文本查看 复制代码
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 11 10:13:43 2021

@author: kkrdfai
"""


from lxml import etree
import urllib.request
import os
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import re

mainUrl = 'https://www.gufengmh8.com'
referer = 'https://www.gufengmh8.com/'

"""
get_html_code 获取url对应HTML代码的方法

Args:
    url:
        网页的连接地址

Returns:
    htmlcode:
        网页的html代码,String型数据
"""
def get_html_code(url):
    max_count = 6
    htmlcode = ''
    for i in range(max_count):
        print('No.'+str(i)+'   start load:' + url)
        try:
            req_one = urllib.request.Request(url)
            req_one.add_header('User-Agent', 'Mozilla/6.0')
            res_one = urllib.request.urlopen(req_one, timeout=60)
            htmlcode = res_one.read().decode('utf-8')
            res_one.close()
            break
        except:
            if i < max_count:
                continue
            else:
                print('多次重复读取网页信息失败')
    time.sleep(0.1)
    return htmlcode




"""
downLoadImg 下载图片的方法

Args:
    url:
        网页的连接地址
    picdir:
        目录名
    index:
        图片编号,也是图片的文件名
"""
def downLoadImg(url, picdir,index):
    header = {'Referer': referer, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',}

    req = urllib.request.Request(url, headers=header)

    Max_Num = 6
    for i in range(Max_Num):
        try:
            req = urllib.request.urlopen(req, timeout=60)
            
            
            #虽然网页图片内的连接最后的后缀是jpg,但是根据二进制码明显发现不是jpg,右键保存发现是webp格式。所以下载的时候直接保存为webp格式
            
            with open(picdir + '/' + str(index)+'.webp', 'wb') as f:
                f.write(req.read())
                print('图片:' + picdir + '/' + str(index) + '.webp保存完毕')
            time.sleep(1)
            break
        except:
            if i < Max_Num - 1:
                continue
            else:
                print('URLError: <urlopen error timed out> All times is failed ')
                
                
"""
get_chapters_list 下载的主要逻辑方法

Args:
    url:
        网页的连接地址,这里需要传入漫画目录的url
"""
def get_chapters_list(url):
    #获取目录网页的全部html代码
    htmlcode = get_html_code(url)
    #etree解析html代码
    html = etree.HTML(htmlcode)
    #根据xpath获取全部的章节
    chapters = html.xpath('//*[@id="chapter-list-54"]/li/a')
        
    #开始循环,不断的通过每一章的url来调用selenium读取漫画图片的地址
    for i in chapters:
        #刚刚的xpath获取的是<a>标签,所以直接可以获取<a>标签的href属性,现在获得的就是这一章的URL
        url = i.get('href')
        # i 是父级<a>标签, 章节的地址可以直接获得,但是章节名称是a标签的span子节点的text内容,需要xpath获取下面的span标签,进而获取章节名称
        title = i.xpath('./span')[0].text
        
        print('开始读取:'+title+"  地址:"+url)
        if os.path.exists(title):
            print('检测到:' + title + '目录已建立,跳过该任务。')
            continue
        else:
            print('创建目录:' + title)
            os.mkdir(title)
        #调用使用selenium的方法来获取这一个章节里的全部url,然后返回一个图片url的list
        #注意传入参数,之前<a>标签获取的地址是相对路径,需要加上主域名才能正常访问
        piclist = get_picurl(mainUrl+url)
        
        #设定初始页码
        index = 1
        
        #进一步循环,通过刚刚获得的全部图片连接,一张一张下载图片并根据index页码保存文件
        for j in piclist:
            print('开始下载:'+j)
            #下载图片
            downLoadImg(j, title,index)
            #页面增加
            index+=1
    



"""
get_picurl 通过章节地址来读取这一章所有图片地址的方法

Args:
    ch_url:
        章节首页的URL

Returns:
    urllist:
        获取到的全部图片地址,数据类型为数组
"""
def get_picurl(ch_url):
    #创建空数组
    
    urllist = []
    mainUrl = "https://res.xiaoqinre.com/images/comic/729/"
    code = get_html_code(ch_url)
    pattern = re.compile(r'\d{2,10}')
    pageNum = pattern.findall(ch_url)[0]
    
    
    ben = code.index("siteName")
    ben = code.index('[',ben,len(code))+1
    end = code.index(']',ben,len(code))
    
    piclist = code[ben:end].replace('"','').split(",")
    
    urllist = []
    for i in piclist:
        urllist.append(mainUrl+str(pageNum)+'/'+i)
    
    return urllist


if __name__ == '__main__':
    urlch = "https://www.gufengmh8.com/manhua/liangbuyi/#chapters"
    get_chapters_list(urlch)
    



 

免费评分

参与人数 7吾爱币 +12 热心值 +7 收起 理由
人二 + 1 + 1 用心讨论,共获提升!
cckkopit + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
王星星 + 2 + 1 热心回复!
小竹子爱吃龙虾 + 1 + 1 用心讨论,共获提升!
yjn866y + 1 + 1 我很赞同!
Ljh666 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

kof21411 发表于 2021-6-11 13:33
其实可以不用selenium,所有图片名都在下js代码里,只要用re提取一下就可以,再用requests可以直接下载了
[JavaScript] 纯文本查看 复制代码
<script>;var siteName = "";var siteUrl = "https://www.gufengmh8.com";;var chapterImages = ["1579492621O6sdBlYR7sYX5FFt.jpg","1579492620ZdS4s5F-4O8rs2dP.jpg","1579492619fNbV3SrmHCwN5Oao.jpg","1579492618SO_Yrj6diSxw0gwa.jpg","1579492617NoFOgJ4YNxNktd5T.jpg","1579492616KuSc4Uyk8yvKdQWc.jpg","1579492615rSec3wC_L7ss0OrI.jpg","1579492615T99ze75K1ybeIxLJ.jpg","1579492614qyFNTPzi1p9jIml5.jpg","1579492613F-ZCvK9q_14bGWV0.jpg","1579492612h-uIDcIAFPXSncNc.jpg","1579492611JLAb79fn_lFCXRAR.jpg","1579492610q6KJydc2ic4nb5Gy.jpg","1579492609Yjeh6G-YezB3i2a2.jpg","1579492608bi_OnhpiPgwOFBCu.jpg","15794926070GPmpFRDFbcU_rkP.jpg","1579492606qP1FYNpMhGEZlOHR.jpg","1579492605AjFY9CgSEAudn1Xd.jpg","1579492604wuj9HB7UFH9QKQio.jpg","1579492604v8sK1QlTAvTufOGv.jpg","1579492603Qg89tbHR1iuvMaX3.jpg","1579492602VG06N49U15ZklQNL.jpg","1579492601bw8z2r4tZSjiw0To.jpg","1579492600tisk7Nfb9MzkuWTj.jpg"];var chapterPath = "images/comic/598/1194223/";var pageTitle = "斗罗大陆1在线观看";var comicUrl = "https://www.gufengmh8.com/manhua/douluodalu/";var pageUrl = "https://www.gufengmh8.com/manhua/douluodalu/";var pageImage = "https://res.xiaoqinre.com/images/cover/201807/1530537387O-NvKf3ZohGRjv6E.jpg";var pageDomain = "https://www.gufengmh8.com";var pageId = "comic.287";var prevChapterData = {"id":null,"comic_id":null,"comic_name":null,"status":null,"vip":null,"is_end":null,"name":null,"type":null,"rtl":null,"image_mode":null,"category":null,"link":null,"link_name":null,"image_type":null,"count":null,"sort":null,"price":null,"created_at":null,"updated_at":null};var nextChapterData = {"id":1194224,"comic_id":287,"comic_name":"斗罗大陆","status":1,"vip":0,"is_end":0,"name":"2","type":0,"rtl":0,"image_mode":0,"category":54,"link":"","link_name":"","image_type":0,"count":24,"sort":200,"price":0,"created_at":1579491753,"updated_at":1579492644};</script>

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
roshinntou + 1 + 1 噗噗噗噗噗... ... ... ... 我仔细一看,确实,直接request获取的代码里就.
三滑稽甲苯 + 1 + 1 我很赞同!

查看全部评分

 楼主| roshinntou 发表于 2021-6-11 12:21
这个是py文件的压缩包,没有密码

gufengmh.zip

3.1 KB, 下载次数: 7, 下载积分: 吾爱币 -1 CB

 楼主| roshinntou 发表于 2021-6-11 12:25
本帖最后由 roshinntou 于 2021-6-11 12:26 编辑

还有一个小问题,写的匆忙,有一点没有加,就是如果下载失败断了,目前的代码就得从头重新开始。

可以在读取每一章网址时,用章节名称找一下文件夹里有没有这个文件,如果有就跳过,没有再下载,这样如果中途断了,可以删除最后一个文件夹重新执行,就能接上了
yoyoma211 发表于 2021-6-11 12:44
楼主是技术达人哈,进来学习下,给力
zheshiweihe 发表于 2021-6-11 13:17
技术非常厉害,赞一个
FIzz001 发表于 2021-6-11 13:33
可以,先收藏
三滑稽甲苯 发表于 2021-6-11 13:46
kof21411 发表于 2021-6-11 13:33
其实可以不用selenium,所有图片名都在下js代码里,只要用re提取一下就可以,再用requests可以直接下载了
...

支持
有其他方法就不要使用selenium,它效率较低
yjn866y 发表于 2021-6-11 14:09
谢谢分享
guoguo12343 发表于 2021-6-11 14:16
额,既然都是图片,加载出来页面,直接截图不是最方便吗?直接定位两个大概的x y值,把图片都截屏进去。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-16 23:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表