卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章1829本站已运行4109

Python 某轻小说下载器

由于网站提供的下载只有 UMD、JAR 和 全本 下载并不能分卷、章下载,并且插图也没有提供一键下载
于是我就写了一个简单的爬虫提供根据卷、章下载小说,并且可以自动爬取所有插图的脚本


食用方法:

先打开该轻小说网站,由于搜索功能需要登录,所以还请麻烦登录/注册一下


网站关闭通知我认为是个摆设,因为这个网站依然在正常运作,并且我在看的连载小说也在更新中!
大家可以无视该通知,直接注册、登录!

然后通过搜索等方法打开某一个小说详情页
此时你的链接将会是这样:
https://www.wenku8.net/book/[小说ID].htm
但这还不够,由于我多次测试发现目录链接中有一个参数并不固定
所以还请进一步获取该参数,才能继续下载

点击 “小说目录” 链接,
你将会跳转到小说目录页,此时就可以将链接里:
https://www.wenku8.net/novel/[这一部分]/index.htm
中的那一部分填入 Python 脚本最开始要求输入的内容了

下载使用多线程,不太清楚这个特性在 Python 几出来的,但我测试使用的是 Python 3.10 版本
已知 Python 3.12 版本有个不明为何的特性导致多线程创建失败,所以请使用低于 Python 3.12 版本的 Python

程序运行结束即为小说下载完成,可以翻看一下输出日志有没有报错,如果有无法解决的错误欢迎反馈!

只是一个简单的爬虫程序,有哪里写的不好的欢迎大家指正,谢谢!
import os
import time
from threading import Thread

import requests
from bs4 import BeautifulSoup

print("""轻小说文库下载器 (www.wenku8.net) -By NekoW
  仅供爬虫原理学习如有侵权请联系删除。""")
book = input("请输入小说 ID: ")

retry_time = 5

# 替换内容,可填入三个参数:原内容,替换内容(默认为空),替换次数(默认-1)
# 例如: ['测试1', '测试2'] 就是将所有 '测试1' 替换为 '测试2'
# 如果只替换一次则为: ['测试1', '测试2', 1]
# 如果想要替换为空,则可以省略第二个参数,
# 例如只替换一次 '测试1' 为空: ['测试1', 1]
replaces = [
    [u'\xa0'],
    ['本文来自 轻小说文库(http://www.wenku8.com)\r\n'],
    ['本文来自 轻小说文库(http://www.wenku8.com)'],
    ['最新最全的日本动漫轻小说 轻小说文库(http://www.wenku8.com) 为你一网打尽!'],
    ['\n\r\n', '\n\n'],
    ['\n', 1],
    ['\r\n\n', 1],
    ['\n\n\n', 1]
]
invalid_filename = ['\\', '/', ':', '*', '?', '"', '<', '>', '|']

novel_url = "https://www.wenku8.net/novel/%s/" % book
url = '%s/index.htm' % novel_url
headers = {
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept': 'text/html,application/xhtml+xml,application',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'
}

request = requests.get(url, headers=headers)
text = request.text.encode(request.encoding, 'ignore')
soup = BeautifulSoup(text, 'html.parser')
novel_title = soup.find('div', attrs={'id': 'title'}).text.strip()
try:
    os.mkdir(path=novel_title)
except FileExistsError:
    pass
table = soup.find('table', attrs={'class': 'css'})
trs = table.find_all('tr')
group = None
novels = {}
for tr in trs:
    tds = tr.find_all('td')
    if len(tds) == 1:
        group = tds[0].text.strip()
        novels[group] = []
        continue
    for td in tds:
        if td.find('a') is None:
            continue
        link = td.find('a')['href']
        title = td.text.strip()
        novels[group].append({'title': title, 'link': link})


def download_image(_image, _group, _chapter):
    def helper():
        filename = _image['src'].split('/')[-1]
        print('正在保存图片 %s 到 %s/%s' % (filename, _group, _chapter['title']))
        try:
            _request = requests.get(_image['src'], headers=headers)
            with open('%s/%s/%s/%s' % (novel_title, _group, _chapter['title'], filename), 'x+b') as f:
                f.write(_request.content)
                print('文件已保存至 %s/%s/%s/%s!' % (novel_title, _group, _chapter['title'], filename))
        except Exception as e:
            if type(e) is FileExistsError:
                return
            print('保存图片 %s 时发生错误: %s, 正在重试...' % (filename, str(e)))
            time.sleep(retry_time)
            helper()
    helper()


def download_novel(_group, _chapter):
    def helper():
        try:
            print('正在下载 %s 的 %s...' % (_group, _chapter['title']))
            _request = requests.get(novel_url + _chapter['link'], headers=headers)
            _text = _request.text.encode(_request.encoding, 'ignore')
            _soup = BeautifulSoup(_text, 'html.parser')
            content = _soup.find('div', attrs={'id': 'content'})
            images = content.find_all('img')
            if len(images) > 0:
                print('在章节 %s 中找到 %d 个图片' % (_chapter['title'], len(images)))
                try:
                    os.makedirs('%s/%s/%s' % (novel_title, _group, _chapter['title']))
                except FileExistsError:
                    pass
                for image in images:
                    image_thread = Thread(target=download_image, args=(image, _group, _chapter))
                    image_thread.start()
            _text = content.text
            for replace in replaces:
                arguments = [replace[0]]
                if len(replace) > 1:
                    if type(replace[1]) is int:
                        arguments.append('')
                    arguments.append(replace[1])
                else:
                    arguments.append('')
                if len(replace) > 2:
                    arguments.append(replace[2])
                else:
                    arguments.append(-1)
                _text = _text.replace(arguments[0], arguments[1], arguments[2])
            try:
                os.makedirs('%s/%s' % (novel_title, _group))
            except FileExistsError:
                pass
            if _text == '':
                return
            cp_title = _chapter['title']
            for s in invalid_filename:
                cp_title = cp_title.replace(s, '')
            filename = f"{_group}/{cp_title}.txt"
            with open(f'{novel_title}/{filename}', 'x+', encoding='utf-8') as f:
                f.write(_text)
                print('文件已保存至 %s/%s!' % (novel_title, filename))
        except Exception as e:
            if type(e) is FileExistsError:
                return
            print("下载 %s %s 时发生错误: %s, 正在重试..." % (_group, _chapter['title'], str(e)))
            time.sleep(retry_time)
            helper()
    helper()


for group, novel_list in novels.items():
    for chapter in novel_list:
        Thread(target=download_novel, args=(group, chapter)).start()
 
卓越飞翔博客
上一篇: C&C++ HOOK_API获取对应的寄存器值
下一篇: nginx 怎么设置只能app使用
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏