python-爬取网络小说到本地

源码来自 [5.Python3爬虫入门实践——爬取名著] , (http://www.jianshu.com/p/e597b5921112) 我只是自己实现了一遍, 感谢原作者


回忆下我们看网络小说的步骤

  1. 打开小说目录: http://www.shicimingju.com/book/sanguoyanyi.html
  2. 选择我们要看的章节:http://www.shicimingju.com/book/sanguoyanyi/1.html
  3. 重复第2步 直到看完整本小说.

那么我们用爬虫怎么实现上面的过程, 把整本小说写入到我的电脑中呢?
我来把上面的每一步翻译成爬虫

  1. 拿到整个 小说目录网页, 让电脑去看这个网页. 这个网页中有所有目录的章节信息, 也有所有目录章节具体内容的链接.
  2. 让电脑拿到所有的章节 链接, 打开章节链接, 得到整个章节网页, 这个网页中就有章节的所有内容, 让电脑把章节内容复制下来, 保存到本地的一个 txt 文件中
  3. 重复第2步, 直到所有的章节内容都保存在了本地的 txt 文件中. 保存该文件. 这样我们就得到了一个完整的小说 txt 文件.

然后我们就可以打开这个txt 文件 愉快的看小说了.


整个过程还是涉及到不少知识点, (我是说对我们小白来说东西很多, 大神求多拍砖, 有批评就有进步哈)


这个项目过程中 学习到的知识点
python 零散知识点
s = r'hello\t world' 表示字符串中的转义字符无效, 按照普通字符串输出

python 正则表达式相关

  • Python的regex模块——更强大的正则表达式引擎
  • Python正则表达式指南
  • 自学Python六 爬虫基础必不可少的正则

下面这篇文章解决了我的问题[感谢作者]

  • Python中的re.search和re.findall之间的区别和联系 + re.finall中带命名的组,不带命名的组,非捕获的组,没有分组四种类型之间的区别

  • 详解Python中re.sub

decode('uif-8','ignore')
decode不是编码转换,decode是把一个无编码的str,依照给入的参数为解码系统,解码后映射到unicode字符集,返回为unicode对象…

抽空研究下 Python3中内置类型bytes和str用法及byte和string之间各种编码转换????

  • Python3中内置类型bytes和str用法及byte和string之间各种编码转换

最终还是打算使用 pycharm 写 Python
添加三方库: pycharm --> preferences --> Project Interpreter --> 选择加号(+)
因为 pip 还是系统的 2.7 版本, pycharm 是 3.5 版本, 无法下载三方库

使用 Python3.6 自带模块
教程 http://www.jianshu.com/p/e597b5921112

re 模块: 正则表达式相关
urlopen 方法返回的是 response 类型

记录整个项目过程
IDE: pycharm
硬件: mac
语言: Python 3.6.1

因为在 mac 上 的 pip 是 2.7 版本 无法安装第三方 Python库, 这里网络请求不能用 Requests, 我采用了 Python 标准库 urllib.request
解析 HTML 文件 建议使用 三方库lxml 我这里直接使用标准库 re(正则表达式)

这份代码来自5.Python3爬虫入门实践——爬取名著
我只是照着敲了一遍, 总结下第一次爬虫遇到的坑


indexUrl = 'http://www.shicimingju.com/book/sanguoyanyi.html'
html = urllib.request.urlopen(indexUrl).read()

urlopen返回 一个类文件对象,我的理解是,你可以直接把它看做一个txt 文件,它提供了如下方法:read() , readline() , readlines() , fileno() , close()

html = html.decode('utf8','ignore')

urllib.request.urlopen(indexUrl).read() 得到的是 ascii码 的 bytes(字节) 内容 我们需要解码成字符串(utf-8码) 得到人类可以读懂的字符(比如: 汉字, 表情符号等).
ignore: 抓取的网页内容不一定都是用 utf-8 编码, 很有可能同时使用了 gb2312 编码 百或者其他编码. 如果同时存在多种编码, 都解码为 utf-8 就会出错, 所以用 ignore 来忽略那些非 utf-8 码, 保证程序正常运行.

这个时候我们已经拿到了这个网页所有源码, 包括我们需要的章节url

查看系统默认编码:

import sys
print sys.getdefaultencoding()
# 我的打印是 utf-8 也有不少同学的打印是 ASCII

如果打印不是 utf-8 , 打印字符控制台输出是乱码, 需要把IDE 改为 utf-8 编码


book_name = re.findall('

(.*)

',html,re.S)

返回匹配结果列表 tuple , 当正则中有分组的时候, 返回的是分组匹配的内容

  • 关于re.findall方法详解

bookurl 中的 url 是不完整的, 缺一个前缀 完整的 第一章 url 如下
http://www.shicimingju.com/book/sanguoyanyi + /book/sanguoyanyi/1.html
我们需要在后面的代码中 拼接 章节 url


下面的步骤就是 打开所有的章节网页, 把里面的每个章节的内容写入到一个本地 的txt 文件中. 这样我们就得到一个完整的小说txt 文件



import urllib.request
import re
indexUrl = 'http://www.shicimingju.com/book/sanguoyanyi.html'
html = urllib.request.urlopen(indexUrl).read()
html = html.decode('utf8','ignore')

# 拿到书名 << 三国演义 >>
book_name = re.findall('

(.*)

',html,re.S) # 得到的是匹配好的结果中 取所有分组匹配内容组合成的元组 # 没有元组的表达式 1.:href="/book/.{0,30}\d\.html">.*? # 有元组的表达式 2.: href="/book/.{0,30}\d\.html">(.*?) #比起上面的表达式多了一对小括号 # 表达式 2的意思 是 在匹配出 表达式1的结果上 再次匹配出所有分组的内容, 然后把所有分组的 内容 组合成 tuple # 表达式 1. 匹配的结果:['href="/book/sanguoyanyi/1.html">第一回·宴桃园豪杰三结义 斩黄巾英雄首立功', 'href="/book/sanguoyanyi/2.html">第二回·张翼德怒鞭督邮 何国舅谋诛宦竖', 'href="/book/sanguoyanyi/3.html">第三回·议温明董卓叱丁原 馈金珠李肃说吕布',...] # 表达式 2. 匹配的结果: ['第一回·宴桃园豪杰三结义 斩黄巾英雄首立功', '第二回·张翼德怒鞭督邮 何国舅谋诛宦竖', '第三回·议温明董卓叱丁原 馈金珠李肃说吕布',...] chapter = re.findall('href="/book/.{0,30}\d\.html">(.*?)',html,re.S) # 得到所有章节url # /book 里面的/ 忘记写了, 导致得不到错误的结果 bookurl = re.findall('href="(/book/.{0,30}\d\.html)">',html,re.S) # 得到章节 url 的前缀 (剔除 indexUtl 的 .html 字符) # 关于 re.sub 对于输入的一个字符串,利用正则表达式(的强大的字符串处理功能),去实现(相对复杂的)字符串替换处理,然后返回被替换后的字符串 chapterUrlBegin = re.sub('.html','',indexUrl) for i in range(0,len(bookurl)): # 提取每章的number number = re.findall('/(.{1,4})\.html',bookurl[i]) #拼接完整的章节url chapterUrl = re.sub('$',"/"+number[0]+".html",chapterUrlBegin) # 打开章节url 网页 chapterHtml = urllib.request.urlopen(chapterUrl).read() chapterHtml = chapterHtml.decode('utf-8','ignore') # 这里的 ignore 是忽略那些不能被转码成 utf-8 格式的字符 # 小说正文 #
\s*(?: )*(.*?)
chapterText = re.findall('
\s*(.*?)
',chapterHtml,re.S) # 剔除我们不需要的标签

和   chapterText = re.sub('

','',''.join(chapterText)) # 剔除 标签

chapterText = re.sub('

','',''.join(chapterText)) # 剔除 标签

chapterText = re.sub('
','',''.join(chapterText)) #
的意思还不太清楚 暂时剔除他 chapterText = re.sub(' ',' ',''.join(chapterText)) # 将   替换成空格 #---------------------文件操作---------------- #如果文件不存在 就创建一个 f = open('/Users/liuying/Documents/book/' + "".join(book_name) + '.txt', 'a', encoding='utf-8') f.write(chapter[i]+"\n") f.write(chapterText+"\n") f.close()

你可能感兴趣的:(python-爬取网络小说到本地)