博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python 抓取小说网站,制作电子书。
阅读量:4687 次
发布时间:2019-06-09

本文共 11044 字,大约阅读时间需要 36 分钟。

分析目的(或者说要达到的效果)

实现一个小说下载器,输入小说的名字然后抓取小说的全部章节,制作成文档。

需要的知识:使用BeautifulSoup或正则解析网页,使用requests下载网页。

搜索小说

直接用小说的站内搜索

966959-20161125154901893-507292595.png
使用小说网站的搜索页面: 这个网站用的是百度的站内搜索
需要提交的数据有q:我们要搜索的文字,clik和entry顾名思义,而s应该是百度站内搜索的用户码吧。
966959-20161125154951034-1632748707.png

import requestsdef directory(name):    params = {"q":name, "click":1, "entry":1, "s":781863708123447302, "nsid":""}    url = "http://so.sanjiangge.com/cse/search"    print(url)    r = requests.get(url,params=params)    r.encoding = "utf-8"    return r.text

返回的是一个HTML文件。注意必须把网页的编码方式改为utf-8。

解析网页

966959-20161125155119534-2038732700.png

先把第一步的网页搜索结果提取出来,使用BeautifulSoup模块找到我们要找到书的链接。
先看看网页的结构

一念永恒

一念成沧海,一念化桑田。一念斩千魔,一念诛万仙。唯我念……永恒

作者: 耳根

类型: 武侠修真

更新时间: 2016-11-10

最新章节: 第405章 送你上天……

上面就包含了我们需要的所有信息(封面,书名,书的地址,最近章节等等)我们只需要排第一的搜索选项中的书名和书的地址,使用BeautfulSoup或正则表达式提取出来。

if r.url == "http://www.baidu.com/search/error.html":        return False    else:        bsObj = BeautifulSoup(r.text, "lxml").findAll("a", {"cpos":"title"})[0]        return (bsObj["href"],bsObj.get_text()[1:-1])

加入搜索错误的检测,如果错误页面将会重定向到百度的错误页面。并且把书的链接提取出来。

接着再来看看书页面的源代码

外传1 柯父。
外传2 楚玉嫣。
外传3 鹦鹉与皮冻。
第一章 他叫白小纯
第二章 火灶房
第三章 六句真言
第四章 炼灵
第五章 万一丢了小命咋办
第六章 灵气上头
第七章 龟纹认主
第八章 我和你拼了!
第九章 延年益寿丹
第十章 师兄别走
第十一章 侯小妹
第十二章 篱笆墙上
第十三章 你也来吧!
第十四章 三师兄?三师姐?
第十五章 不死长生功!
第十六章 心细入微
第十七章 小乌龟
第十八章 引领气氛!
第十九章 白鼠狼的传说
第二十章 一地鸡毛
第二十一章 小纯哥哥……
第二十二章 师姐放心!
第二十三章 偷鸡狂魔
第二十四章 你是谁
第二十五章 不死铁皮!
第二十六章 灵尾鸡好吃么?
第二十七章 这……这是竹子?
第二十八章 压力才是动力
第二十九章 举重若轻
第三十章 来吧!
第三十一章 耻辱啊!
第三十二章 运气逆天
第三十三章 打倒白小纯!
第三十四章 草木碾压
第三十五章 又见许宝财
第三十六章 小乌龟称霸!
第三十七章 举轻若重

目标是把书的章节名和章节链接提取出来

def contents(url, b_name):    link = url[:-10]    print (link)    links = []    r = requests.get(url)    r.encoding = "utf-8"    bsObj = BeautifulSoup(r.text, "lxml").findAll("dd")    for i in bsObj:        i = link + i.a["href"] #把内链合并成完整的链接。        links.append(i)

现在让我们来看看章节页面的源代码是怎样的吧

966959-20161125155217956-803116896.png

    帽儿山,位于东林山脉中,山下有一个村子,民风淳朴,以耕田为生,与世隔绝。
    清晨,村庄的大门前,整个村子里的乡亲,正为一个十五六岁少年送别,这少年瘦弱,但却白白净净,看起来很是乖巧,衣着尽管是寻常的青衫,可却洗的泛白,穿在这少年的身上,与他目中的纯净搭配在一起,透出一股子灵动。
    他叫白小纯。
    

提取出一个章节的全部文字,返回一个包含全部文字的字符串

def section(url):    r = requests.get(url)    r.encoding = "gbk"    bsObj = BeautifulSoup(r.text, "lxml").find(id="content")    print (bsObj)    结果:
    帽儿山,位于东林山脉中,山下有一个村子,民风淳朴,以耕田为生,与世隔绝。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。略   D410211

小技巧:按 回车[Enter]键 返回章节目录,按 ←键 回到上一章,按 →键 进入下一章。

上一章
章节目录
下一章

 

这里有一个问题,返回的内容包含太多不需要的东西,接下来配合使用正则表达式我觉得会容易一些

def section(url):    r = requests.get(url)    r.encoding = "gbk"    bsObj = BeautifulSoup(r.text, "lxml").find(id="content")    print (str(bsObj))    content = re.compile(r"
(.*)
").search(str(bsObj))#把章节内容提取出来 content = re.compile("
").sub("\n", content.group(1))#把网页的
替换成换行符 return content

然后汇总全部章节内容。写入TXT文件。

def contents_load(books, f_name = "电子书"):    contents = []    contents.append(f_name+"\n")    for i in books:        contents.append(i[1]+"\n")        contents.append(section(i[0]))        print(i[1], "下载成功")    f = open(f_name+".txt", "w", encoding="utf-8")    f.writelines(contents)    效果:第一章 一觉醒来整个世界都变了 下载成功第二章 果然我穿越的方式不对吗? 下载成功第三章 孙思邈是我“道弟” 下载成功第四章 原来穿越者也可以被打脸 下载成功第五章 一觉醒来世界又变了 下载成功。。。。。

现在爬虫在网络良好的状况下基本可以正常运行了,不过下载速度有点慢,接下来我们使用多线程加快速度,再使用面对对象的程序设计。注意文件编码方式需要指定utf-8。

多线程参看

import requestsfrom bs4 import BeautifulSoupimport reimport threadingclass load_book(object):    def __init__(self,book_name):        self.b_name = book_name        self.b_link = self.b_link_load()        self.b_directory = self.directory()        self.content = {}    def b_link_load(self):        params = {"q": self.b_name, "click": 1, "entry": 1, "s": 7818637081234473025, "nsid": ""}        url = "http://so.sanjiangge.com/cse/search"        r = requests.get(url, params=params)        r.encoding = "utf-8"        if r.url == "http://www.baidu.com/search/error.html":            return False        else:            bsObj = BeautifulSoup(r.text, "lxml").findAll("a", {"cpos": "title"})[0]        return bsObj["href"]    def directory(self):        link = self.b_link[:-10]        links = []        r = requests.get(self.b_link)        r.encoding = "gbk"        bsObj = BeautifulSoup(r.text, "lxml").findAll("dd")        for i in bsObj:            url = link + i.a["href"]            links.append((url, i.get_text()))        return links    def section_load(self, links):        for i in links:            r = requests.get(i[0])            r.encoding = "gbk"            bsObj = BeautifulSoup(r.text, "lxml").find(id="content")            content = re.compile(r"
(.*)
").search(str(bsObj)) # 把章节内容提取出来 content = re.compile("
").sub("\n", content.group(1)) # 把网页的
替换成换行符 self.content[i[1]]=content[:-8] def contents_load(self,loops=10): threads = [] nloops = range(loops) task_list = [] task_size = len(self.b_directory)//loops+1 for i in nloops:#分割任务 try: task_list.append(self.b_directory[i*task_size:(i+1)*task_size]) except: task_list.append(self.b_directory[i*task_size:]) for i in nloops: t = threading.Thread(target=self.section_load, args=([task_list[i]])) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() def write_txt(self): f = open(self.b_name, "w",encoding="utf-8") f.write(" "*20+self.b_name+"\n") for i in self.b_directory: title = "\n"+" "*20+i[1]+"\n" f.write(title) f.write(self.content[i[1]]) f.close()if __name__ == '__main__': book = load_book("游仙镜") book.contents_load(20) book.write_txt()

运行效果;

966959-20161125155350096-1364165831.png
嗯,具体运行过程中hui出现网络异常,以及部分章节为空页的情况,为了使程序正常运行还需要进行错误的处理。

import requestsfrom bs4 import BeautifulSoupimport reimport threadingimport timeclass load_book(object):    def __init__(self,book_name):        self.b_name = book_name        self.b_link = self.b_link_load()        self.b_directory = self.directory()        self.content = {}    def b_link_load(self):        params = {"q": self.b_name, "click": 1, "entry": 1, "s": 7818637081234473025, "nsid": ""}        url = "http://so.sanjiangge.com/cse/search"        try:            r = requests.get(url, params=params)            r.encoding = "utf-8"            if r.url == "http://www.baidu.com/search/error.html":                return False            else:                bsObj = BeautifulSoup(r.text, "lxml").findAll("a", {"cpos": "title"})[0]                return bsObj["href"]        except:            print("获取目录失败,一秒后重试,失败链接:",url,params)            time.sleep(1)            self.b_link_load()    def directory(self):        link = self.b_link[:-10]        links = []        r = requests.get(self.b_link)        r.encoding = "gbk"        bsObj = BeautifulSoup(r.text, "lxml").findAll("dd")        for i in bsObj:            try:                url = link + i.a["href"]                links.append((url, i.get_text()))            except TypeError:                print(i,"获取章节链接错误")        return links    def section_load(self, links):        for i in links:            try:                r = requests.get(i[0])                r.encoding = "gbk"                bsObj = BeautifulSoup(r.text, "lxml").find(id="content")                content = re.compile(r"
(.*)
").search(str(bsObj)) # 把章节内容提取出来 content = re.compile("
").sub("\n", content.group(1)) # 把网页的
替换成换行符 self.content[i[1]] = content[:-8] print(i[1],"抓取完成") except (TypeError,AttributeError): print("*"*10,"%s章节错漏"% i[1]) except : print("*"*10,"%s抓取错误,重试中"% i[1]) links.insert(0,i) def contents_load(self,loops=10): threads = [] nloops = range(loops) task_list = [] task_size = len(self.b_directory)//loops+1 for i in nloops:#分割任务 try: task_list.append(self.b_directory[i*task_size:(i+1)*task_size]) except: task_list.append(self.b_directory[i*task_size:]) for i in nloops: t = threading.Thread(target=self.section_load, args=([task_list[i]])) threads.append(t) for i in nloops: threads[i].start() for i in nloops: threads[i].join() def write_txt(self): print("开始制作txt文档") f = open(self.b_name+".txt", "w", encoding="utf-8") f.write(" " * 20 + self.b_name + "\n") for i in self.b_directory: try: title = "\n\n" + " " * 20 + i[1] + "\n" f.write(title) f.write(self.content[i[1]]) except KeyError: print("*" * 10, "缺失章节为:", i[1]) f.close()if __name__ == '__main__': book = load_book("美食供应商") book.contents_load(10) book.write_txt()

966959-20161125155513581-473200688.png

好了现在基本可以正常使用了,不过还有很多不足。希望可以和更多的人交流。

可以改进的功能

  1. 使用多个站点进行搜索
  2. 缺失章节使用其他站点数据尝试弥补缺失
  3. 制作图形界面
  4. 加入进度条
  5. 加入发送邮件功能,使制作的电子书可以直接发送到kindle上

转载于:https://www.cnblogs.com/jikeboy/p/6101933.html

你可能感兴趣的文章
类和对象:面向对象编程 - 零基础入门学习Python037
查看>>
寄存器AX
查看>>
angular
查看>>
XML_CPP_资料
查看>>
C/C++UNION中包含STRUCT
查看>>
调试.方便调试的技巧_01
查看>>
C# 导出 Excel 的各种方法总结
查看>>
cocos Uniforms值的赋值
查看>>
Npoi Web 项目中(XSSFWorkbook) 导出出现无法访问已关闭的流的解决方法
查看>>
C# 获取Url 请求方式 域名 端口 路径
查看>>
Lightweight Directory Access Protocol 参考
查看>>
day21
查看>>
[转]java 下载网络上的图片并保存到本地目录
查看>>
Ubuntu安装git
查看>>
记录:通过SSH远程连接Ubuntu
查看>>
38 java 使用标签跳出多层嵌套循环
查看>>
几种主流数据库的压缩技术对比
查看>>
WebGL自由表面流——续
查看>>
判断sqlserver对象是否存在(转)
查看>>
查询最近每天的总和,即查询结果的数量
查看>>