編寫Python腳本抓取網(wǎng)絡(luò)小說來制作自己的閱讀器
你是否苦惱于網(wǎng)上無法下載的“小說在線閱讀”內(nèi)容?或是某些文章的內(nèi)容讓你很有收藏的沖動,卻找不到一個下載的鏈接?是不是有種自己寫個程序把全部搞定的沖動?是不是學(xué)了 python,想要找點(diǎn)東西大展拳腳,告訴別人“哥可是很牛逼的!”?那就讓我們開始吧! 哈哈~
好吧,我就是最近寫 Yii 寫多了,想找點(diǎn)東西調(diào)劑一下.... = =
本項(xiàng)目以研究為目的,所有版權(quán)問題我們都是站在作者的一邊,以看盜版小說為目的的讀者們請自行面壁!
說了這么多,我們要做的就是把小說正文的內(nèi)容從網(wǎng)頁上爬下來,我們的研究對象是全本小說網(wǎng)....再次聲明,不對任何版權(quán)負(fù)責(zé)....
一開始先做最基礎(chǔ)的內(nèi)容,就是把某一章的內(nèi)容抓取下來。
環(huán)境:Ubuntu, Python 2.7
基礎(chǔ)知識
這個程序涉及到的知識點(diǎn)有幾個,在這里列出來,不詳細(xì)講,有疑問的直接百度會有一堆的。
1.urllib2 模塊的 request 對像來設(shè)置 HTTP 請求,包括抓取的 url,和偽裝瀏覽器的代理。然后就是 urlopen 和 read 方法,都很好理解。
2.chardet 模塊,用于檢測網(wǎng)頁的編碼。在網(wǎng)頁上抓取數(shù)據(jù)很容易遇到亂碼的問題,為了判斷網(wǎng)頁是 gtk 編碼還是 utf-8 ,所以用 chardet 的 detect 函數(shù)進(jìn)行檢測。在用 Windows 的同學(xué)可以在這里 http://download.csdn.net/detail/jcjc918/8231371 下載,解壓到 python 的 lib 目錄下就好。
3. decode 函數(shù)將字符串從某種編碼轉(zhuǎn)為 unicode 字符,而 encode 把 unicode 字符轉(zhuǎn)為指定編碼格式的字符串。
4. re 模塊正則表達(dá)式的應(yīng)用。search 函數(shù)可以找到和正則表達(dá)式對應(yīng)匹配的一項(xiàng),而 replace 則是把匹配到的字符串替換。
思路分析:
我們選取的 url 是 http://www.quanben.com/xiaoshuo/0/910/59302.html,斗羅大陸的第一章。你可以查看網(wǎng)頁的源代碼,會發(fā)現(xiàn)只有一個 content 標(biāo)簽包含了所有章節(jié)的內(nèi)容,所以可以用正則把 content 的標(biāo)簽匹配到,抓取下來。試著把這一部分內(nèi)容打印出來,會發(fā)現(xiàn)很多 <br /> 和  ,<br /> 要替換成換行符,   是網(wǎng)頁中的占位符,即空格,替換成空格就好。這樣一章的內(nèi)容就很美觀的出來了。完整起見,同樣用正則把標(biāo)題爬下來。
程序
# -*- coding: utf-8 -*-
import urllib2
import re
import chardet
class Book_Spider:
def __init__(self):
self.pages = []
# 抓取一個章節(jié)
def GetPage(self):
myUrl = "http://www.quanben.com/xiaoshuo/0/910/59302.html";
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
request = urllib2.Request(myUrl, headers = headers)
myResponse = urllib2.urlopen(request)
myPage = myResponse.read()
#先檢測網(wǎng)頁的字符編碼,最后統(tǒng)一轉(zhuǎn)為 utf-8
charset = chardet.detect(myPage)
charset = charset['encoding']
if charset == 'utf-8' or charset == 'UTF-8':
myPage = myPage
else:
myPage = myPage.decode('gb2312','ignore').encode('utf-8')
unicodePage = myPage.decode("utf-8")
try:
#抓取標(biāo)題
my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S)
my_title = my_title.group(1)
except:
print '標(biāo)題 HTML 變化,請重新分析!'
return False
try:
#抓取章節(jié)內(nèi)容
my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S)
my_content = my_content.group(1)
except:
print "內(nèi)容 HTML 變化,請重新分析!"
return False
#替換正文中的網(wǎng)頁代碼
my_content = my_content.replace("<br />","\n")
my_content = my_content.replace(" "," ")
#用字典存儲一章的標(biāo)題和內(nèi)容
onePage = {'title':my_title,'content':my_content}
return onePage
# 用于加載章節(jié)
def LoadPage(self):
try:
# 獲取新的章節(jié)
myPage = self.GetPage()
if myPage == False:
print '抓取失??!'
return False
self.pages.append(myPage)
except:
print '無法連接服務(wù)器!'
#顯示一章
def ShowPage(self,curPage):
print curPage['title']
print curPage['content']
def Start(self):
print u'開始閱讀......\n'
#把這一頁加載進(jìn)來
self.LoadPage()
# 如果self的pages數(shù)組中存有元素
if self.pages:
nowPage = self.pages[0]
self.ShowPage(nowPage)
#----------- 程序的入口處 -----------
print u"""
---------------------------------------
程序:閱讀呼叫轉(zhuǎn)移
版本:0.1
作者:angryrookie
日期:2014-07-05
語言:Python 2.7
功能:按下回車瀏覽章節(jié)
---------------------------------------
"""
print u'請按下回車:'
raw_input()
myBook = Book_Spider()
myBook.Start()
程序運(yùn)行完在我這里可是很好看的,不信請看:^_^

理所當(dāng)然地,接下來我們要把整本小說都爬下來。首先,我們要把程序從原來的讀完一章就結(jié)束,改成讀完一章之后可以繼續(xù)進(jìn)行下一章的閱讀。
注意到每個小說章節(jié)的網(wǎng)頁下面都有下一頁的鏈接。通過查看網(wǎng)頁源代碼,稍微整理一下( 不顯示了),我們可以看到這一部分的 HTML 是下面這種格式的:
<div id="footlink"> <script type="text/javascript" charset="utf-8" src="/scripts/style5.js"></script> <a >上一頁</a> <a >返回目錄</a> <a >下一頁</a> </div>
上一頁 、返回目錄、下一頁都在一個 id 為 footlink 的 div 中,如果想要對每個鏈接進(jìn)行匹配的話,會抓取到網(wǎng)頁上大量的其他鏈接,但是 footlink 的 div 只有一個??!我們可以把這個 div 匹配到,抓下來,然后在這個抓下來的 div 里面再匹配 <a> 的鏈接,這時就只有三個了。只要取最后一個鏈接就是下一頁的 url 的,用這個 url 更新我們抓取的目標(biāo) url ,這樣就能一直抓到下一頁。用戶閱讀邏輯為每讀一個章節(jié)后,等待用戶輸入,如果是 quit 則退出程序,否則顯示下一章。
基礎(chǔ)知識:
上一篇的基礎(chǔ)知識加上 Python 的 thread 模塊.
源代碼:
# -*- coding: utf-8 -*-
import urllib2
import re
import thread
import chardet
class Book_Spider:
def __init__(self):
self.pages = []
self.page = 1
self.flag = True
self.url = "http://www.quanben.com/xiaoshuo/10/10412/2095096.html"
# 將抓取一個章節(jié)
def GetPage(self):
myUrl = self.url
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
req = urllib2.Request(myUrl, headers = headers)
myResponse = urllib2.urlopen(req)
myPage = myResponse.read()
charset = chardet.detect(myPage)
charset = charset['encoding']
if charset == 'utf-8' or charset == 'UTF-8':
myPage = myPage
else:
myPage = myPage.decode('gb2312','ignore').encode('utf-8')
unicodePage = myPage.decode("utf-8")
# 找出 id="content"的div標(biāo)記
try:
#抓取標(biāo)題
my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S)
my_title = my_title.group(1)
except:
print '標(biāo)題 HTML 變化,請重新分析!'
return False
try:
#抓取章節(jié)內(nèi)容
my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S)
my_content = my_content.group(1)
except:
print "內(nèi)容 HTML 變化,請重新分析!"
return False
my_content = my_content.replace("<br />","\n")
my_content = my_content.replace(" "," ")
#用字典存儲一章的標(biāo)題和內(nèi)容
onePage = {'title':my_title,'content':my_content}
try:
#找到頁面下方的連接區(qū)域
foot_link = re.search('<div.*?class="chapter_Turnpage">(.*?)</div>',unicodePage,re.S)
foot_link = foot_link.group(1)
#在連接的區(qū)域找下一頁的連接,根據(jù)網(wǎng)頁特點(diǎn)為第三個
nextUrl = re.findall(u'<a.*?href="(.*?)".*?>(.*?)</a>',foot_link,re.S)
nextUrl = nextUrl[2][0]
# 更新下一次進(jìn)行抓取的鏈接
self.url = nextUrl
except:
print "底部鏈接變化,請重新分析!"
return False
return onePage
# 用于加載章節(jié)
def LoadPage(self):
while self.flag:
if(len(self.pages) - self.page < 3):
try:
# 獲取新的頁面
myPage = self.GetPage()
if myPage == False:
print '抓取失?。?
self.flag = False
self.pages.append(myPage)
except:
print '無法連接網(wǎng)頁!'
self.flag = False
#顯示一章
def ShowPage(self,curPage):
print curPage['title']
print curPage['content']
print "\n"
user_input = raw_input("當(dāng)前是第 %d 章,回車讀取下一章或者輸入 quit 退出:" % self.page)
if(user_input == 'quit'):
self.flag = False
print "\n"
def Start(self):
print u'開始閱讀......\n'
# 新建一個線程
thread.start_new_thread(self.LoadPage,())
# 如果self的page數(shù)組中存有元素
while self.flag:
if self.page <= len(self.pages):
nowPage = self.pages[self.page-1]
self.ShowPage(nowPage)
self.page += 1
print u"本次閱讀結(jié)束"
#----------- 程序的入口處 -----------
print u"""
---------------------------------------
程序:閱讀呼叫轉(zhuǎn)移
版本:0.2
作者:angryrookie
日期:2014-07-07
語言:Python 2.7
功能:按下回車瀏覽下一章節(jié)
---------------------------------------
"""
print u'請按下回車:'
raw_input(' ')
myBook = Book_Spider()
myBook.Start()
現(xiàn)在這么多小說閱讀器,我們只需要把我們要的小說抓取到本地的 txt 文件里就好了,然后自己選個閱讀器看,怎么整都看你了。
其實(shí)上個程序我們已經(jīng)完成了大部分邏輯,我們接下來的改動只需要把抓取到每一章的時候不用顯示出來,而是存入 txt 文件之中。另外一個是程序是不斷地根據(jù)下一頁的 Url 進(jìn)行抓取的,那么什么時候結(jié)束呢?注意當(dāng)?shù)竭_(dá)小說的最后一章時下一頁的鏈接是和返回目錄的鏈接是一樣的。所以我們抓取一個網(wǎng)頁的時候就把這兩個鏈接拿出來,只要出現(xiàn)兩個鏈接一樣的時候,就停止抓取。最后就是我們這個程序不需要多線程了,我們只要一個不斷在抓取小說頁面的線程就行了。
不過,小說章節(jié)多一點(diǎn)時候,等待完成的時間會有點(diǎn)久。目前就不考慮這么多了,基本功能完成就 OK....
基礎(chǔ)知識:前面的基礎(chǔ)知識 - 多線程知識 + 文件操作知識。
源代碼:
# -*- coding:utf-8 -*-
import urllib2
import urllib
import re
import thread
import chardet
class Book_Spider:
def __init__(self):
self.pages = []
self.page = 1
self.flag = True
self.url = "http://www.quanben.com/xiaoshuo/0/910/59302.html"
# 將抓取一個章節(jié)
def GetPage(self):
myUrl = self.url
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
req = urllib2.Request(myUrl, headers = headers)
myResponse = urllib2.urlopen(req)
myPage = myResponse.read()
charset = chardet.detect(myPage)
charset = charset['encoding']
if charset == 'utf-8' or charset == 'UTF-8':
myPage = myPage
else:
myPage = myPage.decode('gb2312','ignore').encode('utf-8')
unicodePage = myPage.decode("utf-8")
# 找出 id="content"的div標(biāo)記
try:
#抓取標(biāo)題
my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S)
my_title = my_title.group(1)
except:
print '標(biāo)題 HTML 變化,請重新分析!'
return False
try:
#抓取章節(jié)內(nèi)容
my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S)
my_content = my_content.group(1)
except:
print "內(nèi)容 HTML 變化,請重新分析!"
return False
my_content = my_content.replace("<br />","\n")
my_content = my_content.replace(" "," ")
#用字典存儲一章的標(biāo)題和內(nèi)容
onePage = {'title':my_title,'content':my_content}
try:
#找到頁面下方的連接區(qū)域
foot_link = re.search('<div.*?class="chapter_Turnpage">(.*?)</div>',unicodePage,re.S)
foot_link = foot_link.group(1)
#在連接的區(qū)域找下一頁的連接,根據(jù)網(wǎng)頁特點(diǎn)為第三個
nextUrl = re.findall(u'<a.*?href="(.*?)".*?>(.*?)</a>',foot_link,re.S)
#目錄鏈接
dir_url = nextUrl[1][0]
nextUrl = nextUrl[2][0]
# 更新下一次進(jìn)行抓取的鏈接
self.url = nextUrl
if(dir_url == nextUrl):
self.flag = False
return onePage
except:
print "底部鏈接變化,請重新分析!"
return False
# 用于加載章節(jié)
def downloadPage(self):
f_txt = open(u"斗羅大陸.txt",'w+')
while self.flag:
try:
# 獲取新的頁面
myPage = self.GetPage()
if myPage == False:
print '抓取失??!'
self.flag = False
title = myPage['title'].encode('utf-8')
content = myPage['content'].encode('utf-8')
f_txt.write(title + '\n\n')
f_txt.write(content)
f_txt.write('\n\n\n')
print "已下載 ",myPage['title']
except:
print '無法連接服務(wù)器!'
self.flag = False
f_txt.close()
def Start(self):
print u'開始下載......\n'
self.downloadPage()
print u"下載完成"
#----------- 程序的入口處 -----------
print u"""
---------------------------------------
程序:閱讀呼叫轉(zhuǎn)移
版本:0.3
作者:angryrookie
日期:2014-07-08
語言:Python 2.7
功能:按下回車開始下載
---------------------------------------
"""
print u'請按下回車:'
raw_input(' ')
myBook = Book_Spider()
myBook.Start()

相關(guān)文章
Django 實(shí)現(xiàn)將圖片轉(zhuǎn)為Base64,然后使用json傳輸
這篇文章主要介紹了Django 實(shí)現(xiàn)將圖片轉(zhuǎn)為Base64,然后使用json傳輸,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
pytest生成Allure報(bào)告以及查看報(bào)告的實(shí)現(xiàn)
本文主要介紹了pytest生成Allure報(bào)告以及查看報(bào)告的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Python 異步之如何保護(hù)任務(wù)免于取消詳解
這篇文章主要為大家介紹了Python 異步之如何保護(hù)任務(wù)免于取消示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
python之tensorflow手把手實(shí)例講解斑馬線識別實(shí)現(xiàn)
目前智慧城市的發(fā)展,人們生活處處有科技,比如人臉識別,智慧交通,無人駕駛等前沿的科技產(chǎn)品也都融入了人們生活中;本篇文章帶你從頭開始實(shí)現(xiàn)斑馬線識別2021-09-09
python利用urllib實(shí)現(xiàn)爬取京東網(wǎng)站商品圖片的爬蟲實(shí)例
下面小編就為大家?guī)硪黄猵ython利用urllib實(shí)現(xiàn)爬取京東網(wǎng)站商品圖片的爬蟲實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08

