Python爬虫:爬取小说,支持多线程和导入读书软件

前言

   还记得今天是2022年6月10日的上午,只因为昨晚无意间看到一篇小说《那年那蝉那把剑》,顿时就作者的文笔给吸引了,一口气就看了50章,可惜好日子不长久。之后就居然开始收费了,作为一名IT从业者,我深知技术自由的重要性,于是便有了这篇文章。

在这里插入图片描述

最终实现的效果如下:

在这里插入图片描述

可选背景、可选章节、可调字体几乎与付费的 效果一样。


分析目标网站

目标网站:新笔趣阁:#https:#//www.#xbiq#uge.la/
把#号去掉
目标网站
图片描述
   通过研究我们发现,这里使用的是异步加载,也就是搜索的网页不会因搜索内容改变而改变URL。 所以这里就简单了,我们只要单独研究搜索框就行了。

在

   研究之后,我们发现需要配置的请求头的格式如下

headers = {
   "User-Agent": r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36",
   "Cookie": "_abcde_qweasd=0; PHPSESSID=cev9lenc2bsooso46revbor1k5; username=User",
   "Host": "#ww#w.xbiq#uge.la",
   "Origin": "https#://www.xbiq#uge.la",
  "Accept":r"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
   }  

   第一个是User-Agent浏览器配置,第二个Cookie是用来识别用户的,第三个Host是主机地址,第四个Origin是原地址,第五个Accept是接受的类型。
   请求头配置结束,接下来是请求数据配置。
在这里插入图片描述
   很简单,直接配置成json格式就行。
   接着,我们就可以搜索了。

搜索目标小说

具体代码是:

def get_title_url():
    x =str(input("请输入书名或作者"))# "那年那蝉那把剑"
    data = {'searchkey': x}
    url = 'https:#//www.xbi#quge.la/#modules/ar#ticle/waps#.php'
    global headers, b_n
    r = requests.post(url, data=data, headers=headers)
    soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser")
    book_author = soup.find_all("td", class_="even")
    books = []          #  书名
    authors = []        #  作者名
    directory = []      #  目录链接
    tem = 1
    for each in book_author:
        if tem == 1:
            books.append(each.text)
            tem -= 1
            directory.append(each.a.get("href"))
        else:
            authors.append(each.text)
            tem += 1
    print('搜索结果:')
    for num,book, author in zip(range(1, len(books)+1),books, authors):
        print((str(num)+": ").ljust(4)+(book+"\t").ljust(25) + ("\t作者:" + author).ljust(20))
    search = dict(zip(books, directory))
    if books == []:
        print("没有找到任何一本书,请重新输入!")
        get_title_url()
    try:
        i =1 #int(input("输入需要下载的序列号(重新搜索输入'0')"))
    except:
        print("输入错误重新输入:")
        i = int(input("输入需要下载的序列号(重新搜索输入'0')"))
    if i == 0:
        books = []
        authors = []
        directory = []
        get_title_url()
    if i>len(books) or i<0:
        print("输入错误重新输入:")
        i = int(input("输入需要下载的序列号(重新搜索输入'0')"))
    b_n=books[i-1]
    try:
        os.mkdir(books[i-1])
        os.chdir(b_n)
    except:
        os.chdir(b_n)
        b_n = books[i - 1]
    return search[books[i-1]]

   通过这串代码,我们会得到的搜索的结果如下:

Alt


在这里插入图片描述

拼接小说目录

  再然后,根据用户输入的序号,确定需要爬取的小说,这里我要看的是这本,然后根据搜索到的内容,访问具体的小说书架。

在这里插入图片描述

  进来之后,接着再研究网站结构,通过研究,我们发现所有链接都是dd标签,所以这里我们直接取得所有的dd标签就行,具体操作是利用BeautifulSoup.find_all("dd")取得的

在这里插入图片描述

  最后的得到的结果如下:是一个列表,是关于所有章节的url+标题的列表,这里之所以这么弄,是为了后面的多线程做铺垫的。

在这里插入图片描述

  基本上到了这里,我们几乎主要的就做的差不多了。

  这里的主要代码如下

def get_text_url(titel_url):
    url = titel_url
    global headers
    r = requests.get(url, headers=headers)
    soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser")
    titles = soup.find_all("dd")
    texts = []
    names = []
    i=0
    for each in titles:
        i+=1
        texts.append("http#://www#.xbiq#uge.la"+each.a["href"])
        names.append(str(i)+" "+each.a.t#ext)#为了更好的编号

    link_find_name = dict(zip(texts, names))
    global  total_Character
    total_Character=len(names)
    print(str(b_n) + "一共{}马上下载\n".format(total_Character))
    canshu = []
    for link, name in link_find_name.items():
        canshu.append((link, name))
    return canshu          

目标文章爬取

  这里的话,主要就是考虑到多线程,所以加了一点延时,为了防止频繁访问被禁 ip之后就还是利用BeautifulSoup找到所有的find_all("div", id="content") id 为content 的 div , 因为这里就是我们需要的小说正文了,最后再简单的把正文里面的格式调一调,去掉广告,换上换行等等。

def readnovel(texts_url):
    global headers,b_n,total_Character,Character
    count=1
    url = texts_url[0]
    # print(url)
    name = texts_url[1]
    req = requests.get(url=url,headers=headers)
    time.sleep(random.uniform(0, 0.2))          # 即使设置了延迟,他还有会可能503(没办法小网站)
    req.encoding = 'UTF-8'                      # 这里的编码是UTF-8,跟目录不一样,要注意!
    html = req.text
    soup = bs4.BeautifulSoup(html, features="html.parser")
    texts = soup.find_all("div", id="content")
    while (len(texts) == 0):                    #   他如果503的话,读取内容就什么都木有,那直接让他再读一次,直到读出来为止。
        req = requests.get(url=url, headers=headers)
        time.sleep(random.uniform(0,0.2))
        req.encoding = 'UTF-8'
        html = req.text
        soup = bs4.BeautifulSoup(html, features="html.parser")
        texts = soup.find_all("div", id="content")
    else:
        content = re.sub('[\d]','',name)+ str("\n\n   ") # [0-9]#去掉章节数字
        # print(content)
        content=content+texts[0].text.replace('\xa0' * 8, '\n\n')
        # content = content+ texts[0].replace('\xa0' * 8, '\n\n\n\n')
        # print(content)
        content=content.replace("亲,点击进去,给个好评呗,分数越高更新越快,据说给新笔趣阁打满分的最后都找到了漂亮的老婆哦!","")
        content=content.replace(" "," ")
        content=content.replace("手机站全新改版升级地址:https://wap.xbiquge.la,数据和书签与电脑站同步,无广告清新阅读!","\n")
        content=content.replace("<br/>","\n")
        content=content.replace("<p>|<p/>","")
        content=content.replace("\r","\n"*2)
        
        
        # 使用text属性,提取文本内容,滤除br标签,随后使用replace方法,去掉八个空格符号,并用回车代替 再去除每一页都有得结尾
        # print(content)
        # exit()
    with open(name+'.txt',"w",encoding='utf-8')as f:
        f.write(content)
        print("\r已下载{}".format(name))    # sys模块就在这用了一次,为了不让他换行。。。
    # with open(b_n + '.txt', "a+", encoding = 'utf-8') as f:
    #     f.write(content)
    #     print("\r已下载{}".format(name))  # sys模块就在这用了一次,为了不让他换行。。。
    # print("\n全部下载完毕")
    

爬取效果如下(大概是1000章1分钟)

在这里插入图片描述

[(img-21G5rWxT-1654949804259)(C:/Users/20365/AppData/Roaming/Typora/typora-user-images/1654948615943.png)]

这就是最后处理之后的文本。
在这里插入图片描述

目标文章融合

   最后我们不可能还是打开一个 txt 一个一个看的,这样太没有体验感了,肯定都是想导入到手机里面随时看。因此我们还需要将这些文本合成一篇文章,之后导入某个读书软件(微信读书)。这里融合的话,我们不能说乱融合,得按照小说原来的顺序融合,所有这里 有一个对原来小说按照名字排序的操作。


def merge_file(filedir):
    os.remove(filedir+"/newTxt.txt")
    file_list = [os.path.join(root, filespath) \
                 for root, dirs, files in os.walk(filedir) \
                 for filespath in files \
                 if str(filespath).endswith('txt')
                 ]
    file_list.sort(key = lambda x: int(re.findall(r"\d+",x)[0]))
    print(file_list)
    os.makedirs(os.path.dirname(filedir+"/newTxt.txt"), exist_ok = True)
    newTxt = open(filedir + "/newTxt.txt", "w", encoding = "utf-8")
    
    for f in file_list:
        try:
            txt = open(f, "r", encoding = 'utf-8', errors = "ignore")
            strs = txt.read()
            print(strs)
            newTxt.write(strs + "\n")
        except Exception as e:
            print(f)
            print(e)
    newTxt.close()
    print("合并結束")

导入wechat读书

在这里插入图片描述
在这里插入图片描述

  最后在微信读书里面导入我们融合的txt,结果展示的结果,非常完美,还可以支持与微信读书相适应,可以改字体、改背景颜色,等等,几乎就是花钱用户一样的体验了。唯一不同的就是我们这是凭自己的技术免费的。
  到此,我们做的工作也就结束了,可以开心的看小说了。

ps:良心推荐小说:雪中悍刀行

完整全部代码

# -*- coding: utf-8 -*-
"""
Created on Sat Jun 11 09:39:47 2022

@author: 20365
"""
import re
from multiprocessing import Pool
import requests
import bs4          #
import os           #
import sys          #
import time
import random       
headers = {
    "User-Agent": r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36",
    "Cookie": "_abcde_qweasd=0; PHPSESSID=cev9lenc2bsooso46revbor1k5; username=User",
    "Host": "w#ww.xbiquge.la",
    "Origin": "https://w#ww.xbiq#uge.la",
    "Accept":r"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}      # 设置头尽量多一点 以防万一
b_n = ""
total_Character=0
Character=1
def get_title_url():
    x =str(input("请输入书名或作者"))# "那年那蝉那把剑"
    data = {'searchkey': x}
    url = 'https#://ww#w.xbiquge.la/mod#ules/arti#cle/waps.#php'
    global headers, b_n
    r = requests.post(url, data=data, headers=headers)
    soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser")
    book_author = soup.find_all("td", class_="even")
    books = []          #  书名
    authors = []        #  作者名
    directory = []      #  目录链接
    tem = 1
    for each in book_author:
        if tem == 1:
            books.append(each.text)
            tem -= 1
            directory.append(each.a.get("href"))
        else:
            authors.append(each.text)
            tem += 1
    print('搜索结果:')
    for num,book, author in zip(range(1, len(books)+1),books, authors):
        print((str(num)+": ").ljust(4)+(book+"\t").ljust(25) + ("\t作者:" + author).ljust(20))
    search = dict(zip(books, directory))
    if books == []:
        print("没有找到任何一本书,请重新输入!")
        get_title_url()
    try:
        i =int(input("输入需要下载的序列号")) 
    except:
        print("输入错误重新输入:")
        i = int(input("输入需要下载的序列号"))
    if i == 0:
        books = []
        authors = []
        directory = []
        get_title_url()
    if i>len(books) or i<0:
        print("输入错误重新输入:")
        i = int(input("输入需要下载的序列号(重新搜索输入'0')"))
    b_n=books[i-1]
    try:
        os.mkdir(books[i-1])
        os.chdir(b_n)
    except:
        os.chdir(b_n)
        b_n = books[i - 1]
    return search[books[i-1]]

def get_text_url(titel_url):
    url = titel_url
    global headers
    r = requests.get(url, headers=headers)
    soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser")
    titles = soup.find_all("dd")
    texts = []
    names = []
    i=0
    for each in titles:
        i+=1
        texts.append("http:#//ww#w.xbiq#uge.la"+each.a["href"])
        names.append(str(i)+" "+each.a.text)#为了更好的编号

    link_find_name = dict(zip(texts, names))
    global  total_Character
    total_Character=len(names)
    print(str(b_n) + "一共{}马上下载\n".format(total_Character))
    canshu = []
    for link, name in link_find_name.items():
        canshu.append((link, name))
    return canshu          #  注意这里的返回值是一个包含两个列表的列表!!

def write_novel(texts_url):
    global headers,b_n,total_Character,Character
    count=1
    url = texts_url[0]
    # print(url)
    name = texts_url[1]
    req = requests.get(url=url,headers=headers)
    time.sleep(random.uniform(0, 0.2))          # 即使设置了延迟,他还有会可能503(没办法小网站)
    req.encoding = 'UTF-8'                      # 这里的编码是UTF-8,跟目录不一样,要注意!
    html = req.text
    soup = bs4.BeautifulSoup(html, features="html.parser")
    texts = soup.find_all("div", id="content")
    while (len(texts) == 0):                    #   他如果503的话,读取内容就什么都木有,那直接让他再读一次,直到读出来为止。
        req = requests.get(url=url, headers=headers)
        time.sleep(random.uniform(0,0.2))
        req.encoding = 'UTF-8'
        html = req.text
        soup = bs4.BeautifulSoup(html, features="html.parser")
        texts = soup.find_all("div", id="content")
    else:
        content = re.sub('[\d]','',name)+ str("\n\n   ") # [0-9]#去掉章节数字
        # print(content)
        content=content+texts[0].text.replace('\xa0' * 8, '\n\n')
        # content = content+ texts[0].replace('\xa0' * 8, '\n\n\n\n')
        # print(content)
        content=content.replace("亲,点击进去,给个好评呗,分数越高更新越快,据说给新笔趣阁打满分的最后都找到了漂亮的老婆哦!","")
        content=content.replace(" "," ")
        content=content.replace("手机站全新改版升级地址:https://wap.xbiquge.la,数据和书签与电脑站同步,无广告清新阅读!","\n")
        content=content.replace("<br/>","\n")
        content=content.replace("<p>|<p/>","")
        content=content.replace("\r","\n"*2)
       
        # 使用text属性,提取文本内容,滤除br标签,随后使用replace方法,去掉八个空格符号,并用回车代替 再去除每一页都有得结尾
        # print(content)
        # exit()
    with open(name+'.txt',"w",encoding='utf-8')as f:
        f.write(content)
        print("\r已下载{}".format(name))    # sys模块就在这用了一次,为了不让他换行。。。
    # with open(b_n + '.txt', "a+", encoding = 'utf-8') as f:
    #     f.write(content)
    #     print("\r已下载{}".format(name))  # sys模块就在这用了一次,为了不让他换行。。。
    # print("\n全部下载完毕")
    
def merge_file(filedir):
    os.remove(filedir+"/newTxt.txt")
    file_list = [os.path.join(root, filespath) \
                 for root, dirs, files in os.walk(filedir) \
                 for filespath in files \
                 if str(filespath).endswith('txt')
                 ]
    file_list.sort(key = lambda x: int(re.findall(r"\d+",x)[0]))
    print(file_list)
    os.makedirs(os.path.dirname(filedir+"/newTxt.txt"), exist_ok = True)
    newTxt = open(filedir + "/newTxt.txt", "w", encoding = "utf-8")
    
    for f in file_list:
        try:
            txt = open(f, "r", encoding = 'utf-8', errors = "ignore")
            strs = txt.read()
            print(strs)
            newTxt.write(strs + "\n")
        except Exception as e:
            print(f)
            print(e)
    newTxt.close()
    print("合并結束")

if __name__ == '__main__':
    print("小说资源全部来自于'XXX'---》\n所以搜不到我也没办法...\n为了确保下载完整,每章设置了延时!")
    titel_url = get_title_url()
    texts_url = get_text_url(titel_url)
    # write_novel(texts_url)
    print(texts_url)
    print("总共章节:"+str(len(texts_url)))
    pool = Pool(processes = 16)
    pool.map(write_novel,texts_url)
    filedir=r"./{}".format(b_n)
    # # print(filedir)
    merge_file(filedir)#可选可不选,合并文件
    input("输入任意键退出")

引用

https://www.cnblogs.com/dream-boat/articles/16367263.html

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鵬哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值