源码来自 [5.Python3爬虫入门实践——爬取名著] , (http://www.jianshu.com/p/e597b5921112) 我只是自己实现了一遍, 感谢原作者
回忆下我们看网络小说的步骤
- 打开小说目录: http://www.shicimingju.com/book/sanguoyanyi.html
- 选择我们要看的章节:http://www.shicimingju.com/book/sanguoyanyi/1.html
- 重复第2步 直到看完整本小说.
那么我们用爬虫怎么实现上面的过程, 把整本小说写入到我的电脑中呢?
我来把上面的每一步翻译成爬虫
- 拿到整个 小说目录网页, 让电脑去看这个网页. 这个网页中有所有目录的章节信息, 也有所有目录章节具体内容的链接.
- 让电脑拿到所有的章节 链接, 打开章节链接, 得到整个章节网页, 这个网页中就有章节的所有内容, 让电脑把章节内容复制下来, 保存到本地的一个 txt 文件中
- 重复第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()