使用python3开发趴小说的小工具

最近制作了一个从笔趣阁下载小说的小工具,写篇博客记录下这个工具的制作方法:
工具的功能:从笔趣阁网站上下载一个小说到电脑中,方面像我这样的书虫简单愉快的看小说(无黄页、无广告、一次下载、本地阅读~~)。

一、开发环境:

我的项目是在win10、python3.6开发的。当然并不要求完全和我的开发环境一致,python3应该是都可用的;

二、编程实现:

废话少说,直接开干!

2.1、进入笔趣阁主页,点击进入任何一本小说。比如在“最新入库小说”下随便找一本小说《飞天神皇》,进入其主页(https://www.biquge5200.cc/96_96609/),按F12查看调试信息。

2.2、创建类BQG:
创建类,并创建几个变量:
self.bookName ——– 用于保存小说名
self.file ——– 用于创建小说的保存文件
self.agentIP ——– 用于保存代理IP(防止被笔趣阁禁封IP)
selt.tool ——– 用于替代趴取到的页面中的一些符号

class BQG:
    def __init__(self):
        self.agentIP = AgentIp()#''  # 代理IP网址
        self.file = None    # 用于保存下载的小说--txt格式
        self.bookName = None   # 小说名

2.3、创建一个通过代理趴网站的函数:
创建函数getPage_proxy()并调用它,将小说主页(https://www.biquge5200.cc/96_96609/)上的内容全部趴下来,通过pageCode输出

def getPage_proxy(self, url):
        proxy = {'http':self.agentIP} #self.agentIP中保存的是代理ip
        proxy_support = urllib.request.ProxyHandler(proxy)
        opener = urllib.request.build_opener(proxy_support)
        opener.addheaders = [('User-agent','Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36')] 
        urllib.request.install_opener(opener)
        response = urllib.request.urlopen(url)

        #需要添加‘ignore’参数,因为笔趣阁有的小说主页中的编码既有gbk又有utf-8,而笔趣阁header中charset=gbk,所以选择gbk并忽略utf-8
        pageCode = response.read().decode("gbk", 'ignore')         
        return pageCode

2.4、获取小说名:
调试窗口搜索小说名“飞天神皇”,能找到条目”飞天神皇”,此条及小说的名,通过如下代码获取小说名:(此函数中的pageCode即上一条中趴取的内容)

    def getBookName(self, pageCode):
        pattern = re.compile('<h1>(.*?)</h1>', re.S)
        bookName = re.search(pattern, pageCode) #匹配小说名,bookName[1]即小说名
        if bookName:
            print("\n\n准备下载小说:\t%s\n"%(bookName[1]))
            self.bookName = bookName[1]

2.5、获取所有章节的网站链接
观察各章节内容的规律,创建正则表达式获取章节名即网站链接,具体代码如下:

    def getChapters(self, pageCode):
        pattern = re.compile('<dd><a href="(.*?)">(.*?)</a></dd>', re.S)
        chapters = re.findall(pattern, pageCode)
        if chapters:
            filename = self.bookName + ".txt"
            print("创建小说文件:\t%s"%filename) # 创建小说文件 飞天神皇.txt
            file = open(filename, 'r+') #打开此文件(文件不存在则创建)
            if file:
                print("\n=================== 依次下载各章节内容 ===================\n")
                for chapter in chapters: # 依次获取各章节内容
                    print("获取章节:\t%s"%(chapter[1]))#chapter[0]--章节url链接 chapter[1]--章节名
                    file.write(chapter[1])
                    text = self.getOneChapterContent(chapter[0]) # 获取该章节内容
                    file.write(text) # 写入章节内容到文件中
                file.close()
            else:
                print("文件创建or打开失败")

2.6、获取章节内容:
依次打开上一节中的各章网站链接,用F12查看调试信息,根据内容格式写正则表达式。趴取网页中所有内容,再通过正则表达式获取并该章小说内容。代码如下:

    def getOneChapterContent(self, chapterurl): # chapterurl即某章网站链接
        pageCode = self.getPage_proxy(chapterurl.strip()) # 通过代理ip趴取网站的内容
        if pageCode == None:
            return None
        pattern = re.compile('<div id="content".*?>(.*?)</div>', re.S) #创建正则表达式
        content = re.findall(pattern, pageCode) # 获取小说该章内容
        if content:
            pp = re.compile('<p>|</p>')
            text = re.sub(pp, "\n", content[0]) # 替换掉其中换行符
            return text #text中保存的即是该章节的内容
        return None

2.7、制作一个弹出框,用于输入网址链接
为了让软件能方便重用,制作一个输入框让程序每次运行起来的时候先弹出一个输入框,用户可用在此输入框中输入小说的主页网址,点击“确定”即可一键获取小说具体代码如下:

def pop_up_box():       #弹出并获取输入框
    def getBookUrl(): # 创建一个按键消息处理函数,按下确定安静,及获取输入框中的内容
        nonlocal book_url
        try:
            book_url = str(var.get().strip())
            root.destroy()  #输入后直接关闭窗口
        except:
            book_url = 'Not a valid url.'

    book_url = None
    root = tkinter.Tk(className='提示:请输入小说主页的网址')  # 弹出框框名
    root.geometry('400x80')     # 设置弹出框的大小 w x h
    root.resizable(width=False, height=False) # 固定弹出框的大小
    label = tkinter.Label(root,text='yxrsh声明:\n本工具完全基于兴趣制作,完全免费,且禁止任何形式的商业用途\nQQ:1744058358') # 制作一个简单的声明标签(其实是小广告~~)
    label.pack(side = 'bottom')   # 将输入框放到弹出框靠左侧位置

    var = tkinter.StringVar()   # 输入框中的内容
    edit = tkinter.Entry(root, textvariable=var, width = '50')  # 设置"文本变量"为var
    edit.pack(side = 'left')   # 将输入框放到弹出框靠左侧位置

    bt = tkinter.Button(root, text='确定', command=getBookUrl)     # 按下此按钮(Input), 触发inputint函数
    bt.pack(side='right')   # 将输入按键框放到弹出框靠右侧位置

    root.mainloop() # 弹出弹出框
    return book_url # 返回获取到的小说主页链接

2.8、调用类BQG
整个工程到此基本完成了所有的小块,接下来就是组合起来,让程序跑起来了。上述程序有一些依赖库,需要导入到工程中

import urllib.request #趴网站需要
import re   #正则表达式需要
import tkinter    # 输入框需要

调用以下代码即可跑起程序了。。。

url = pop_up_box()  #获取小说主页网址(章节目录页)
if url:
    spider = BQG()
    spider.start(url)
else:
    print("请输入合理的网址")

2.9 改进
百度发现笔趣阁居然有新(https://www.biquge5200.cc/#)、旧(http://www.biquge.com.tw/)之分,前期调试软件都是基于新笔趣阁测试的,本工具无法获取旧笔趣阁中的小说。没办法,改呗!

a) 用当前工具获取旧笔趣阁小说时,根本获取不到网站,提示错误:
“urllib.error.URLError: urlopen error [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。”
也就是说使用代理还连不上网站了,那我试试不使用代理,结果尼玛还挺OK,好吧,不软原因怎么样,先取消代理吧。新建一个不用代理的获取网站信息的函数,将程序中调用的getPage_proxy()函数都换成getPage()函数。

    # 不使用代理的话,将程序中getPage_proxy()函数替代成getPage()函数
    def getPage(self, url):
        user_agent='Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
        header = {'User-Agent': user_agent}
        request = urllib.request.Request(url, headers = header)
        response = urllib.request.urlopen(request)
        pageCode = response.read().decode("gbk", "ignore")
        return pageCode

b) 完成上一步后能获取到各章节地址了,但是调用第一章就失败了,查看下获取到的第一章网站链接(/18_18698/8526643.html),居然和新笔趣阁不一样,不是一个完整的网址链接。从网站上点击进去其第一章网页(https://www.biquge.com.tw/18_18698/8526643.html),对比小说主页(https://www.biquge.com.tw/18_18698/),发现了还是有一定联系的:章节真实网址=主页+获取到的链接地址 - 连接处相同内容。ok,那我们创建一个这样的函数

def urlcat(head, tail):
    cnt = len(head) # 获取头部长度
    index = 0
    flag = True
    while flag:
        if head.rfind(tail[0:index]) != -1: #查看相同部分的大小
            index += 1
        else:
            flag = False
        if index >= cnt:
            flag = False

    #返回链接后的地址       
    if index > 0:
        return (head + tail[index-1::])
    else:
        return head + tail

然后修改getChapters()函数,


                file.write(chapter[1])
                    real_url = chapter[0].strip() # 获取到的地址
                    if real_url[0] == '/': # 如果地址以'/'开头,则将其与主页地址进行拼接
                        real_url = urlcat(self.mainurl, real_url) 
                    text = self.getOneChapterContent(real_url)

c) 修改后,现在可以正常获取旧笔趣阁的小说啦,测试下最新的代码能否获取新笔趣阁小说,发现居然也很正常。好吧,我前期多此一举加了代理ip。不过还有点小缺陷,调试时发现,有时复制网页时没有复制进来”http://”部分,而是”www”开头的网址,这样urllib.request.Request()是没法识别的,刚好前面制作了一个网页链接的小函数,这里也可用用上

if url: # 从输入框获取到小说主页网址后,判断其是否为“www”开头,是的话给其添加头部"http://"
    if url[0:3] == "www":
        url = urlcat("https://", url)

三、制作exe工具

找到python安装路径,在子子目录scripts下有pip.exe工具(我用的是pip3.6.exe),将其拖入到cmd窗口,后面添加相应的按键命令(如:C:\Users\test\AppData\Local\Programs\Python\Python36-32\Scripts\pip3.6 install pywin32),回车安装软件
1、下载 pywin32:    pip install pywin32
2、下载 pyinstaller: pip install pyinstaller。
3、使用pyinstaller工具打包: pyinstaller -F -w -c  app.py,即可生成exe文件;
生成的exe文件路径在命令行中可以看到(如下图红框所示),之后就可以将此exe文件共享给基友使用啦~~
![这里写图片描述](https://img-blog.csdn.net/2018051515544767?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l4cnNobGps/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

四、跑起来看看

进入笔趣阁网站,搜索一本经典的小说----紫川,进入紫川小说主页,复制其地址(https://www.biquge5200.cc/59_59557/);
双击"bqg.exe",在弹出的窗口中输入复制的地址,点击“确定”,接下来就耐心等候小说下载了,下载完成后会关闭窗口。然后你会发现,在当前路径下已经躺好了一本"紫川.txt"静静等候你的临幸。

这里写图片描述

附录:以下为完整的源程序bqg.py:


# -*- coding: utf-8 -*-

import urllib
import urllib.request
import re
import tkinter    # 输入输出框



def pop_up_box():       #弹出并获取输入框
    def getBookUrl():
        nonlocal book_url
        try:
            book_url = str(var.get().strip())
            root.destroy()  #输入后直接关闭窗口
        except:
            book_url = 'Not a valid url.'


    book_url = None
    root = tkinter.Tk(className='提示:请输入小说主页的网址')  # 弹出框框名
    root.geometry('400x80')     # 设置弹出框的大小 w x h
    root.resizable(width=False, height=False) #宽不可变, 高可变,默认为True

    label = tkinter.Label(root,text='yxrsh声明:\n本工具完全基于兴趣制作,完全免费,且禁止任何形式的商业用途\nQQ:1744058358') #生成标签
    label.pack(side = 'bottom')   # 将输入框放到弹出框靠左侧位置

    var = tkinter.StringVar()   # 输入框中的内容
    edit = tkinter.Entry(root, textvariable=var, width = '50')  # 设置"文本变量"为var
    edit.pack(side = 'left')   # 将输入框放到弹出框靠左侧位置

    bt = tkinter.Button(root, text='确定', command=getBookUrl)     # 按下此按钮(Input), 触发inputint函数
    bt.pack(side='right')   # 将输入按键框放到弹出框靠右侧位置

    root.mainloop() # 弹出弹出框
    return book_url

def AgentIp():
    agent_url = 'http://www.xicidaili.com/'   #在此网站中可查找许多代理网站
    user_agent='Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
    header = {'User-Agent': user_agent}
    request = urllib.request.Request(agent_url, headers = header)
    response = urllib.request.urlopen(request)
    pageCode = response.read().decode('utf-8')
    pattern = re.compile('<td class="country"><img.*?/></td>.*?<td>(.*?)</td>.*?>高匿</td>', re.S)
    ip = re.search(pattern, pageCode)   #只获取一个匿名IP做代理
    if ip:
        print(ip[0])
        return ip[1]
    return None


def urlcat(head, tail):
    cnt = len(head) # 获取头部长度
    index = 0
    flag = True
    while flag:
        if head.rfind(tail[0:index]) != -1: #查看相同部分的大小
            index += 1
        else:
            flag = False
        if index >= cnt:
            flag = False

    #返回链接后的地址       
    if index > 0:
        return (head + tail[index-1::])
    else:
        return head + tail



class BQG:
    def __init__(self):
        self.agentIP = "113.118.94.140"#AgentIp()#''  # 代理IP网址
        self.file = None    # 用于保存下载的小说--txt格式
        self.bookName = 'test'   # 小说名
        self.mainurl = ''


    def openFile(self, filename):
        file = open(filename, 'r+')
        if file:
            return file
        return None

    # 不使用代理的话,将程序中getPage_proxy()函数替代成getPage()函数
    def getPage(self, url):
        user_agent='Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
        header = {'User-Agent': user_agent}
        request = urllib.request.Request(url, headers = header)
        response = urllib.request.urlopen(request)
        pageCode = response.read().decode("gbk", "ignore")
        return pageCode


    def getPage_proxy(self, url):
        proxy = {'http':self.agentIP}
        proxy_support = urllib.request.ProxyHandler(proxy)
        opener = urllib.request.build_opener(proxy_support)
        opener.addheaders = [('User-agent','Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36')]
        urllib.request.install_opener(opener)
        response = urllib.request.urlopen(url)
        pageCode = response.read().decode("gbk", 'ignore')
        return pageCode


    def getBookName(self, pageCode):
        pattern = re.compile('<h1>(.*?)</h1>', re.S)
        bookName = re.search(pattern, pageCode)
        if bookName:
            print("\n\n准备下载小说:\t%s\n"%(bookName[1].strip()))
            self.bookName = bookName[1].strip()


    def getOneChapterContent(self, chapterurl):
        pageCode = self.getPage(chapterurl.strip()) # getPage_proxy(chapterurl.strip())
        if pageCode == None:
            return None
        pattern = re.compile('<div id="content".*?>(.*?)</div>', re.S)
        content = re.findall(pattern, pageCode)
        if content:
            pp = re.compile('<p>|</p>')
            text = re.sub(pp, "\n", content[0])
            return text
        print("none")
        return ''


    def getChapters(self, pageCode):
        pattern = re.compile('<dd><a href="(.*?)">(.*?)</a></dd>', re.S)
        chapters = re.findall(pattern, pageCode)
        if chapters:
            filename = self.bookName + ".txt"
            print("创建小说文件:\t%s"%filename)
            file = open(filename, 'w')
            if file:
                print("\n=================== 依次下载各章节内容 ===================\n")
                for chapter in chapters:
                    print("获取章节:\t%s"%(chapter[1]))#chapter[0]--章节链接 chapter[1]--章节名
                    file.write(chapter[1])
                    real_url = chapter[0].strip()
                    if real_url[0] == '/':
                        real_url = urlcat(self.mainurl, real_url) 
                    text = self.getOneChapterContent(real_url)
                    file.write(text)
                file.close()
            else:
                print("文件创建or打开失败")


    def start(self, bqg_url):
        self.mainurl = bqg_url
        pageCode = self.getPage(bqg_url) #getPage_proxy(bqg_url)
        self.getBookName(pageCode)
        self.getChapters(pageCode)
        print("\n已下载完成,退出!\n")



url = pop_up_box()  #获取小说主页网址(章节目录页)
if url: # 从输入框获取到小说主页网址后,判断其是否为“www”开头,是的话给其添加头部"http://"
    if url[0:3] == "www":
        url = urlcat("https://", url)
    spider = BQG()
    spider.start(url)


else:
    print("请输入合理的网址")




阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭