孤云 发表于 2020-10-5 12:40

Python爬虫实现漫画下载

本帖最后由 孤云 于 2020-10-6 11:50 编辑

## 使用教程
1. 源码方式
    1). 打开Main.py,运行程序。
    2). 按照提示输入存储路径和漫画名称。路径用"/","\","\\" 做分隔符都行,代码中都将"\"替换成"/"。
      
    ```
      path = input("请输入存储路径:\n").replace("\\","/")
    ```
    3). 选择要下载的漫画,然后等待下载就行。
      

    4). 下载结果显示,程序是从最新章节开始下载的,下载完成程序会自动关闭。
      
2. 可执行文件方式
    1). 打开Main.exe,按照提示操作。
    2). 如果出现程序突然退出可以尝试使用CMD方式运行程序,这样可以看到错误信息。

   
## 注意事项

1.本项目仅用于学习与交流,请勿用于其他用途。
2.会出现单个章节下载不全的情况,这是下载源的问题。
3.可执行文件在win7 x64和win10 x64上测试可以正常运行。
4.漫画名称不能输错,exe文件输错名字可能会闪退,看不到错误信息。
5.可执行文件运行会经常出现退出情况,这可能是因为我在程序里手动抛出了异常导致的退出。用pyinstaller打包后没有输出错误信息就退出了。可以用CMD窗口运行exe文件,这样可以看到错误信息。
## 下载地址
1.源码下载地址
    1). gitee: https://gitee.com/zy888888/spider
    2). 文末
2.可执行文件下载地址
    1). 百度云链接:https://pan.baidu.com/s/1rqTcuPrWem__Io0_pstjbQ提取码:mp0u
    2). 在线下载: https://zhouyun.site/Main.exe

import requests
import lxml.html
import os
import re

class Api():
    # 搜索漫画
    # keywords: 漫画名字
    SEARCH_URL = "https://m.wuxiamh.com/search/?keywords={0}"

    # 下载路径
    FILE_PATH = "{0}/{1}/{2}"

class Book():
    def __init__(self, book_name, author, url):
      self._book_name = book_name
      self._author = author
      self._url = url

    @property
    def book_name(self):
      return self._book_name

    @book_name.setter
    def book_name(self, book_name):
      self._book_name = book_name

    @property
    def author(self):
      return self._author

    @author.setter
    def author(self, author):
      self._author = author

    @property
    def url(self):
      return self._url

    @url.setter
    def url(self, url):
      self._url = url

    @property
    def type(self):
      return self._type

    @type.setter
    def type(self, type):
      self._type = type

    @property
    def date(self):
      return self._date

    @date.setter
    def date(self, date):
      self._date = date

    @property
    def description(self):
      return self._description

    @description.setter
    def description(self, description):
      self._description = description

    def __str__(self) -> str:
      return "{书名: %s , 作者: %s , 链接: %s,类型: %s, 日期: %s, 简介: %s}" % (
      self.book_name, self.author, self.url, self.type, self.date, self.description)

class Cartoon():
    def get_html(self, url):
      """
      获取html源码
      :param url: 需要获取的url
      :return:   html源码
      """
      if url != None and url != "":
            response = requests.get(url)
            if response.status_code == 200:
                html = response.text
            return html
      else:
            raise Exception("请输入正确的链接")

    def search(self, keywords):
      """
      搜索漫画
      :param keywords: 漫画名称
      :return:    html页面
      """
      url = Api.SEARCH_URL.format(keywords)
      html = self.get_html(url)
      return html

    def search_result(self, html):
      """
      搜索结果处理
      :param html: html页面
      :return: book对象
      """
      selector = lxml.html.fromstring(html)
      info = selector.xpath("//div[@class='Sub_H2 classify']/span/text()")
      result_num = int(re.findall("搜索结果,共(\\d*)条", info, flags=0))
      print("搜索结果,共{0}条".format(result_num))
      if result_num == 0:
            raise Exception("搜索结果为0,请重新搜索")

      # 搜索结果url
      update_list = selector.xpath("//div[@id='update_list']/div/div/div[@class='itemTxt']")
      book_list = []
      for itemTxt in update_list:
            # 搜索结果
            book_name = itemTxt.xpath("a/text()")
            author = itemTxt.xpath("p/text()")
            url = itemTxt.xpath("a/@href")
            type = itemTxt.xpath("p/span/text()")
            date = itemTxt.xpath("p/span/text()")

            book = Book(book_name, author, url)
            book.type = type
            book.date = date
            book.description = ""
            book_list.append(book)
      i = 0
      while i < result_num:
            print("结果" , i + 1, ": ", book_list)
            i += 1
      # 10次输入机会,10次都错退出程序
      for i in range(10):
            result = int(input("请选择结果:\n"))
            # 越界检查
            if result > 0 and result <= result_num:
                # 返回选择的book对象

                return book_list
            else:
                print("请输入正确的结果")

    def chapeter(self, book):
      """
      获取所有章节
      :param book: book对象
      :return: 章节列表
      """
      html = self.get_html(book.url)
      selector = lxml.html.fromstring(html)
      book.description = selector.xpath("//div[@class='comic-view clearfix']/p/text()")
      # 打印书名
      print(book)
      li = selector.xpath("//div[@class='list']/ul/li")
      chapter_dict = {}
      for i in li:
            # 章节名称
            links = "https://m.wuxiamh.com" + i.xpath("a/@href")
            chapter_name = i.xpath("a/span/text()")
            chapter_dict = links
      new_chapter_dict = self.filter(book.book_name ,chapter_dict)
      return new_chapter_dict

    def filter(self, book_name, chapter_dict):
      """
      过滤无效链接
      :param book_name: 名称
      :param chapter_dict: 章节字典
      :return: 过滤后的新章节字典
      """
      new_chapter_dict = {}
      for chapter in chapter_dict.keys():
            html = self.get_html(chapter_dict)
            selector = lxml.html.fromstring(html)
            mip_img = selector.xpath("//div[@class='UnderPage']/div[@class='UnderPage']/mip-link/mip-img/@src")
            if len(mip_img) > 0:
                webp = mip_img
                response = requests.get(webp)
                if response.status_code == 200:
                  new_chapter_dict = webp
                else:
                  print("异常链接: " + webp)
            else:
                print("尊敬的各位喜爱{0}漫画的用户,本站应《{0}》版权方要求现已屏蔽删除本漫画所有章节链接,只保留作品文字信息简介以及章节目录,请喜欢{0}的漫友购买杂志或到官网付费欣赏。为此给各位漫友带来的不便,敬请谅解!".format(book_name))
                raise Exception("没有版权,无法下载")
      return new_chapter_dict

    def download(self, book_name, chapter_dict):
      """
      下载
      :param book_name: 书名
      :param chapter_dict: 章节字典
      :return:
      """
      for chapter in chapter_dict.keys():
            file_name = Api.FILE_PATH.format(book_name, chapter)
            # 目录不存在就创建
            if os.path.exists(file_name) == False:
                os.makedirs(file_name)
            url = chapter_dict
            i = 0
            while True:
                print("download url: " + url + "{0}.webp".format(i))
                response = requests.get(url + "{0}.webp".format(i))
                status_code = response.status_code
                if status_code == 200:
                  with open(file_name + "/{0}.jpg".format(i), "wb") as f:
                        f.write(response.content)
                elif status_code == 404:
                  break
                i += 1



if __name__ == "__main__":
    cartoon = Cartoon()
    path = input("请输入存储路径:\n").replace("\\","/")
    Api.FILE_PATH = path + "/{0}/{1}"
    book_name = input("请输入要搜索的漫画名称:\n")
    try:
      html = cartoon.search(book_name)
      book = cartoon.search_result(html)
      chapter_dict = cartoon.chapeter(book)
      cartoon.download(book.book_name, chapter_dict)
    except Exception as e:
      print(e)

孤云 发表于 2020-10-5 14:42

本帖最后由 孤云 于 2020-10-5 14:54 编辑

lovice1998 发表于 2020-10-5 13:59
感觉还不错,配置了下运行,搜了海贼意思一下关闭代码。感谢分享,菜鸡体验结束。
你是用的可执行文件执行吧。如果是exe文件执行输错漫画名字会退出程序,并且看不到错误信息。源码运行就可以看到错误信息了。具体原因是我在代码里判断了如果搜索结果为0条数据就抛出异常。
      if result_num == 0:
            raise Exception("搜索结果为0,请重新搜索")

lovice1998 发表于 2020-10-8 17:57

孤云 发表于 2020-10-5 14:42
你是用的可执行文件执行吧。如果是exe文件执行输错漫画名字会退出程序,并且看不到错误信息。源码运行就 ...

从GitHub把楼主代码copy下来,我用的idea运行,有到输入结果那里,也给了输出。结果显示如下,当时没细想异常连接的事,想着要是下载下来还得删了那么多话也算麻烦,毕竟还是喜欢追动漫,跑题了。。。

今天又试着搜索别的(星裂变),都有这个异常连接还是没下载东西下来。
是配置出问题了吗?不是很会用idea,靠直觉与百度运行出来的。

莫名堂 发表于 2020-10-5 12:57

打破零回复,谢谢分享

xiaohushen 发表于 2020-10-5 13:19

好厉害,我才刚开始学。

lovice1998 发表于 2020-10-5 13:59

感觉还不错,配置了下运行,搜了海贼意思一下关闭代码。感谢分享,菜鸡体验结束。

騇钚得离去 发表于 2020-10-5 14:13

谢谢,努力向大佬们看齐

hao1917 发表于 2020-10-5 14:17

无法输入中文,粘贴中文书名回车直接退出了。。。

Sc118421 发表于 2020-10-5 14:39

厉害了,学习徐诶

孤云 发表于 2020-10-5 14:45

hao1917 发表于 2020-10-5 14:17
无法输入中文,粘贴中文书名回车直接退出了。。。

你是用的哪种方式运行呢?

吾爱丶加油 发表于 2020-10-5 15:46

付费漫画可以下载吗?{:1_918:}
页: [1] 2 3 4 5
查看完整版本: Python爬虫实现漫画下载