源碼來自 [5.Python3爬蟲入門實踐——爬取名著] , (http://www.lxweimin.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 正則表達式相關
下面這篇文章解決了我的問題[感謝作者]
decode('uif-8','ignore')
decode不是編碼轉換,decode是把一個無編碼的str,依照給入的參數為解碼系統,解碼后映射到unicode字符集,返回為unicode對象…
抽空研究下 Python3中內置類型bytes和str用法及byte和string之間各種編碼轉換????
最終還是打算使用 pycharm 寫 Python
添加三方庫: pycharm --> preferences --> Project Interpreter --> 選擇加號(+)
因為 pip 還是系統的 2.7 版本, pycharm 是 3.5 版本, 無法下載三方庫
使用 Python3.6 自帶模塊
教程 http://www.lxweimin.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('<h1>(.*)</h1>',html,re.S)
返回匹配結果列表 tuple , 當正則中有分組的時候, 返回的是分組匹配的內容
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('<h1>(.*)</h1>',html,re.S)
# 得到的是匹配好的結果中 取所有分組匹配內容組合成的元組
# 沒有元組的表達式 1.:href="/book/.{0,30}\d\.html">.*?</a>
# 有元組的表達式 2.: href="/book/.{0,30}\d\.html">(.*?)</a> #比起上面的表達式多了一對小括號
# 表達式 2的意思 是 在匹配出 表達式1的結果上 再次匹配出所有分組的內容, 然后把所有分組的 內容 組合成 tuple
# 表達式 1. 匹配的結果:['href="/book/sanguoyanyi/1.html">第一回·宴桃園豪杰三結義 斬黃巾英雄首立功</a>', 'href="/book/sanguoyanyi/2.html">第二回·張翼德怒鞭督郵 何國舅謀誅宦豎</a>', 'href="/book/sanguoyanyi/3.html">第三回·議溫明董卓叱丁原 饋金珠李肅說呂布</a>',...]
# 表達式 2. 匹配的結果: ['第一回·宴桃園豪杰三結義 斬黃巾英雄首立功', '第二回·張翼德怒鞭督郵 何國舅謀誅宦豎', '第三回·議溫明董卓叱丁原 饋金珠李肅說呂布',...]
chapter = re.findall('href="/book/.{0,30}\d\.html">(.*?)</a>',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 格式的字符
# 小說正文
#<div id="con2".*?<p>\s*(?: )*(.*?)</div>
chapterText = re.findall('<div id="con2".*?<p>\s*(.*?)</div>',chapterHtml,re.S)
# 剔除我們不需要的標簽 <p> </p> 和  
chapterText = re.sub('<p>','',''.join(chapterText)) # 剔除 標簽<p>
chapterText = re.sub('</p>','',''.join(chapterText)) # 剔除 標簽</p>
chapterText = re.sub('<br>','',''.join(chapterText)) # <br> 的意思還不太清楚 暫時剔除他
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()