python-爬取網絡小說到本地

源碼來自 [5.Python3爬蟲入門實踐——爬取名著] , (http://www.lxweimin.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 正則表達式相關

下面這篇文章解決了我的問題[感謝作者]

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> 和 &nbsp
    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))  # 將 &nbsp 替換成空格
    #---------------------文件操作----------------
    #如果文件不存在 就創建一個
    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()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容