python爬蟲爬取小說
注:本教程僅限交流學(xué)習(xí),支持小說作者,請上起點中文網(wǎng)訂閱。?
因為爬蟲涉及到的知識有點多,所以不建議零基礎(chǔ)的同學(xué)學(xué)習(xí),但是可以通過對我的代碼的修改去爬取自己需要的小說,請注意,不要直接拉到最下面,這個帖子我會詳細的說明各個代碼的作用,以及出錯會導(dǎo)致的問題,如果不去仔細看的話,代碼一旦出問題你就會手足無措,無從下手。?
推薦粒粒的python基礎(chǔ)和爬蟲教程:
?https://www.bilibili.com/video/BV1d54y1g7db/?share_source=copy_web&vd_source=717e252d12ec490be1cd613be97ab875
本代碼使用的是pycharm編輯器,如果不會安裝的請百度,python不會配置的也去百度。?
代碼:
鏈接:https://pan.baidu.com/s/1KNmNHSiubDAT3u4ogD5VYw?pwd=1453?
提取碼:1453?
1.第一步我們要做的是導(dǎo)入我們要用到的python包,分別是:?
bs4、requests、time、os、random?
看到陌生的包不用擔(dān)心,在代碼的編寫中,我會一一介紹他們的作用。?
下面開始導(dǎo)入包:?

?

?

?
在經(jīng)歷了漫長的等待中,終于下好了第一個包,下載成功的標志是:出現(xiàn)successfully........語句?
然后用相同的方法下載其他包,其中time、os、radom包是python內(nèi)置,不需要用pip下載,因此我們只需要下載requests包?

?
現(xiàn)在我們下好了所需要的包?
2.第二步我們就要開始敲代碼了,我們首先導(dǎo)入需要的包:?

?
因為我們這個爬蟲代碼的目的是爬取小說所以我們先來打開我們要爬取的網(wǎng)站:
https://www.biqukan8.cc/
這是一個盜版小說網(wǎng)站,我們今天的目的就是從里面爬取到《一念永恒》這本小說。(再次聲明支持耳根大大)?
我們先來打開一念永恒的第一章:?
https://www.biqukan8.cc/1_1094/5403177.html

?
這就是一念永恒第一章的界面了,我們首先嘗試爬取第一章的內(nèi)容,然后再拓展到每一章節(jié)。?
首先我們來編寫代碼把這個網(wǎng)頁文件爬取下來,然后進行分析,提取出我們真正想要的——章節(jié)內(nèi)容,代碼如下:?

?
通過requests包中的get函數(shù)來獲取網(wǎng)頁文件,其中g(shù)et函數(shù)的url參數(shù)是網(wǎng)頁的地址,并給網(wǎng)頁文件創(chuàng)建req對象存儲,同時通過.status_code函數(shù)打印出網(wǎng)頁狀態(tài)碼,來判斷是否成功連接。如果狀態(tài)碼為200,則證明成功連接,如果是其他狀態(tài)碼,如著名的404,則表示沒有正確連接.?

?
好的,接下來我們運行代碼,果然不出所料,報錯了:?

?
代碼報錯,大眼一看很是嚇人,但是只要分析所給的錯誤代碼,再去找度娘,基本都能解決,那我們這個為什么報錯呢?答案是請求頭,正常通過瀏覽器向服務(wù)器發(fā)送請求會在請求頭上顯示瀏覽器的信息,但是通過python爬蟲程序發(fā)送請求,在請求頭上會有python的字樣,會被服務(wù)器駁回請求,這是一種非常常見的反爬蟲措施。?
那我們怎么辦呢?只需要通過給get函數(shù)傳入一個header參數(shù)進行請求頭的偽裝即可,問題是我們要偽裝成什么呢?其實我們只需要偽裝我們自己的瀏覽器即可:?

?
打開瀏覽器控制臺,在網(wǎng)絡(luò)欄目,打開一個文件,然后在他的標頭欄目里,下滑找到user-agent,里面的就是你瀏覽器的請求頭。?
然后我們在代碼中進行請求頭的偽裝:?

?
可以發(fā)現(xiàn)請求頭是以字典的形式輸入的參數(shù),因為除了user-agent還有其他的參數(shù)可以偽裝,在這里我們不做討論。?
我們運行嘗試一下:?

?
?
可以看到打印出的狀態(tài)碼是200,證明我們請求成功。?
此時req對象已經(jīng)包含了網(wǎng)頁文件的內(nèi)容,通過,text函數(shù)可以提取出網(wǎng)頁文件:?

?
運行結(jié)果如下圖:?

?
可以看到這和我們在瀏覽器控制臺左下角的網(wǎng)頁源代碼相同,可以確定就是我們需要的網(wǎng)頁文件,但是我們需要的是第一章的內(nèi)容,而不是網(wǎng)頁源代碼。?
3.第三步我們就開始對第一章的內(nèi)容進行爬取了:?
在將章節(jié)文件提取出來之前,我們需要先對網(wǎng)頁文件進行一些方便我們閱讀和控制的操作,這時候就要用到我們的Beautifulsoup類了:?

?
代碼運行結(jié)果如下:?

?
好吧,和之前確實沒有什么方便閱讀,但是他的主要作用是對網(wǎng)頁文件進行操作:?
我們首先在瀏覽器控制臺尋找章節(jié)內(nèi)容對應(yīng)的標簽:?

?

?
可以看到我們需要的只是這一div標簽下的內(nèi)容,我們就需要把這一div標簽截取下來,但是我們可以發(fā)現(xiàn)網(wǎng)頁文件中有很多的div標簽,那怎么把這一div標簽取出來呢??
我們可以看到,這一div標簽具有一個class屬性,而他的class屬性的值是唯一的showtxt,我們就可以通過這一點,唯一的識別并截取這一div標簽:?
具體操作是BeautifulSoup類的findall方法:?
代碼如下:?

?
其中findAll方法的class參數(shù)因為要與python的calss關(guān)鍵字分開,參數(shù)名是class_?
代碼運行結(jié)果如下:?

?
這是一個很長的列表,其中只有一個元素,因為class屬性是showtxt的div標簽只有這一個,列表中的這一個元素即為div標簽下的所有內(nèi)容。?
但是這里面還是有除了我們想要的章節(jié)內(nèi)容以外的很多符號,我們可以使用 .text函數(shù)來取出其中的文本文件(注意,使用.text的對象是text_1[0],因為text_1是一個列表,我們操作的只是他的第一個元素),代碼如下:?
?

運行結(jié)果如下:?
?

可以看到目的已經(jīng)基本實現(xiàn)了,但是每一段之間是由很多個看似空格的東西組成的,我們復(fù)制到代碼里面會發(fā)現(xiàn),這是一些奇怪的東西,但我們只需要把他們換成換行符,使用replace函數(shù)即可。?
代碼如下:?
?

運行結(jié)果:?
?

實際上,這一大堆奇怪的東西可以用:'\xa0' * 8代替(我也不知道為什么),替換之后的代碼:?
?

不管怎么樣,看樣子是美觀了不少,運行結(jié)果如下:?
?

嗯,結(jié)果是一樣的。?
我們已經(jīng)爬取了一念永恒的第一章,接下來我們的目標是:
1.讀取出整本小說的每一章節(jié)?
2.把他們保存為文本格式(.txt)?
3.每一章的文件名就是章名?
好的,接下來我們就開始一條條實現(xiàn)嘍:?
1.要想讀取出每一章節(jié),最重要的是什么呢?我們現(xiàn)在只需要第一章的網(wǎng)頁鏈接,就可以爬取第一章的內(nèi)容,如果我們有第二章的網(wǎng)頁鏈接,既可以爬取第二章,以此類推。當然,我們不可能直接手動輸入每一章的網(wǎng)頁鏈接,那么我們就得想辦法把每一章的網(wǎng)頁鏈接爬取出來,那么從哪里可以獲得每一章的網(wǎng)頁鏈接呢?沒錯,就是這本書的目錄界面,因為我們可以從目錄點擊任一一章的章名跳轉(zhuǎn)到該章的網(wǎng)頁:?

?
那我們打開瀏覽器的開發(fā)者工具來查看網(wǎng)頁源代碼:?

?

?
我們可以看到,每一章的章名在一個dd標簽里面,而所有的dd標簽在一個大的div標簽里面,所以,我們想要提取到每一章的網(wǎng)頁1鏈接的話,只需要提取到這個div標簽,那這一標簽是怎么和其他div標簽區(qū)分的呢?沒錯,就是class = "listmain",這和我們之前講的是一樣的,那么讓我們來寫代碼:?
因為網(wǎng)頁鏈接的爬取和章內(nèi)容的爬取并關(guān)聯(lián),所以我們新建一個py文件,最后再和之前的組合使用:?

?
前面的代碼和之前的差不多,只是網(wǎng)頁鏈接的不同,但是當我們1運行之后,就會發(fā)現(xiàn)第一個有意思的bug了:?

?
可以看到,打印出的網(wǎng)頁源代碼中居然有很多亂碼,我們回到瀏覽器,看一下瀏覽器開發(fā)者工具的源代碼:?

?
經(jīng)過對比可以發(fā)現(xiàn),亂碼的部分正是每一章的章名,但是我們的章名還是有用的,即保存為tex格式的文件名要命名為章名,那么這些中文究竟是為什么亂碼的呢??
本來我也是一頭霧水,然后在我搜索度娘之后得到了答案:?
讓我們打開瀏覽器的開發(fā)者界面:?

?
可以看到,我們隨便點一個文件之后,發(fā)現(xiàn)他的請求頭里面有一項accept-encoding里面有一個gzip,這就是導(dǎo)致中文亂碼的元兇,因為瀏覽器返回給服務(wù)器時為了節(jié)省流量會壓縮后發(fā)送,然后壓縮后的文件被python爬蟲爬取下來,所以導(dǎo)致了中文亂碼,解決方法也很簡單,首先我們看一下究竟改成了什么編碼方式:?

?
使用encoding方法就可以查看該文本的編碼方式,我們發(fā)現(xiàn)編碼方式時ISO-8859-1,我們只需要把他轉(zhuǎn)換為我們通常使用的UTF-8或者gbk即可,由于pycharm終端默認編碼方式為gbk,所以我們把他轉(zhuǎn)換為gbk格式:?

?
結(jié)果正常輸出,需要注意的時:此時我們的爬蟲文件編碼方式為UTF-8,如果換成gbk會報錯(我也不知道為什么)?
好了,我們現(xiàn)在已經(jīng)把目錄網(wǎng)頁文件的源代碼打印到終端上了,我們接下來就要提取其中包括每一章的章名和鏈接的位置了:?

?
和之前的代碼一樣,現(xiàn)在我們又得到了一個只有一個元素的列表:?

?
(姑且不去理會這些亂碼的中文)?
我們已經(jīng)得到了需要的div標簽下的內(nèi)容,然后我們需要獲得里面的a標簽(不用管dd標簽,因為他們都是一樣的),只需要再次使用BeautufulSoup和findALL:?

?

?
我們已經(jīng)得到了一個具有很多元素的列表,接下來需要循環(huán)遍歷出每一個章節(jié)的網(wǎng)頁鏈接和章名:?

?

?
我們已經(jīng)得到了這一堆的網(wǎng)頁鏈接和章名,接下來通過對他們的處理就可以得到我們想要得到東西了:?
對于章名的處理非常簡單,因為只有章名是文本文件:?

?

?
運行結(jié)果中,我們已經(jīng)成功的把章名打印出來了,接下來處理網(wǎng)頁鏈接:?

?
網(wǎng)頁鏈接被儲存在href標簽下,我們只要用get函數(shù)將其取出即可,運行結(jié)果:?

?
可以看到我們已經(jīng)成功的獲得了網(wǎng)頁鏈接,但是會有人說:不對啊啊,這和我平常看到的網(wǎng)頁鏈接長得不一樣啊,其實這只是網(wǎng)頁鏈接的后半部分,我們只需要在前面加上該網(wǎng)站的域名就可以進行訪問了:?

?
?
我們已經(jīng)得到了這一堆的網(wǎng)頁鏈接和章名,接下來通過對他們的處理就可以得到我們想要得到東西了:?
對于章名的處理非常簡單,因為只有章名是文本文件:?

?

?
運行結(jié)果中,我們已經(jīng)成功的把章名打印出來了,接下來處理網(wǎng)頁鏈接:?

?
網(wǎng)頁鏈接被儲存在href標簽下,我們只要用get函數(shù)將其取出即可,運行結(jié)果:?

?
可以看到我們已經(jīng)成功的獲得了網(wǎng)頁鏈接,但是會有人說:不對啊啊,這和我平常看到的網(wǎng)頁鏈接長得不一樣啊,其實這只是網(wǎng)頁鏈接的后半部分,我們只需要在前面加上該網(wǎng)站的域名就可以進行訪問了:?

?
?
好的我們現(xiàn)在擁有了章名和網(wǎng)頁鏈接,和先前的代碼組合使用,就可以進行正本小說的爬取,代碼如下:?

?
我們已經(jīng)兩個代碼粘在一起并把重復(fù)的變量名替換,但是這并不完善,因為我們要把這些寫入到文本文件里面并保存在電腦中,代碼如下:?

?
最后的time.sleep(1)指每次循環(huán)結(jié)束睡眠一秒繼續(xù)1下一次循環(huán),為了防止我們請求過快被服務(wù)器拒絕響應(yīng)?
運行之后,只是不斷的提示警告:?

?
但是打開我們設(shè)置的文件目錄之后,就能發(fā)現(xiàn)已經(jīng)輸出了我們想要的小說內(nèi)容:?

?
那我們的教程到這里就結(jié)束了??
當然不是,因為還有一個問題沒有解決:當我們爬取的章數(shù)過多時(大概是三四百章)服務(wù)器會識別出爬蟲程序或者干脆直接認為是ddos攻擊,然后就會把我們的請求頭給禁掉,我們的程序也就會終止,如下圖:?

?

?

?

?
這些是比較有用的報錯信息。?
接下來我們嘗試繼續(xù)用該請求頭訪問該網(wǎng)站后會發(fā)現(xiàn)該請求頭已經(jīng)被禁止了?
那么這種問題有解決的方法嗎?有的:?
我們只需要弄一大堆的請求頭然后每次爬取就隨機使用一個請求頭即可解決:?

?
這就是一個請求頭列表,里面有很多很多的請求頭,?

?
通過random包里面的choice隨機取一個請求頭賦值給header?
但是這樣做只能隨機一開始連接目錄時的請求頭,一旦爬蟲開始運行,就會一直使用該請求頭,并沒有達到我們想要的目的,那怎么解決呢??
我們只需要在for循環(huán)的結(jié)尾(可以是time的前面,也可以是后面)再次把請求頭隨機賦值一次即可,代碼如下:?

?
好了我們現(xiàn)在可以重新運行代碼了,但是此時還是有很小很小的概率會被禁止,然而我們現(xiàn)在重新運行一次之后,還是會從第一章開始下載,那么這個問題怎么解決呢??
還有我們終端一直彈出的警告怎么回事,怎么解決呢??
下載文本的過程中沒有任何提示是否不太友好呢??
如果我想要換一本小說下載,就需要重新研究一邊代碼將其中的某些東西進行改變,這又該怎么解決呢??
這一切,請聽下回分解。?
下面我們來依次解決:?
1.首先請求頭被禁止的問題暫時沒有好的解決方法,但是我們可以使用代碼把開始下載的章節(jié)設(shè)置為最新的一章,但是我們不能第二次運行的過程中讀取到第一次運行后的結(jié)果與變量,那么我們該怎么去讀取上次下載的最后一章呢?答案是去讀取我們下載到的文件夾里面的最新章節(jié),代碼如下:?

?

?
代碼已經(jīng)正常開始下載,但是為我們注意到,他是從最新章節(jié)的前面十幾章開始下載的,那這是為什么呢??
答案是小說網(wǎng)站會把最新的十幾章放在最前面看下圖:?

?
我們打開開發(fā)者工具,查看一些源代碼:?

?
發(fā)現(xiàn)果然和我們想的一樣,這也是為什么我們上面代碼的for循環(huán)里面有一個判斷num是否小于1300,因為新的幾章都在1300之后且距離不遠。?
接下來只需要在下標索引的開始索引里面加上這幾章的數(shù)量即可:?

?
但是我們試運行之后發(fā)現(xiàn)還是少了三章的距離,這是為什么呢??
我也不清楚,但是這三章的距離還是勉強可以接受的,總算不用重新開始下載了(有強迫癥的話可以num+16)?
2.解決終端警告問題:?
這個警告從我們開始用BeautifulSoup就開始出現(xiàn),但是由于沒有對我們的代碼運行造成影響,我也就沒去管他,那我把他復(fù)制下來經(jīng)過搜索后發(fā)現(xiàn),他的大致意思是:GuessedAtParserWarning:未明確指定解析器,因此我正在為此系統(tǒng)使用最佳的HTML解析器(“ html.parser”)?
解決方法很簡單的,給BreatifulSoup類傳入一個html.parser即可:?

?
我們再次運行,發(fā)現(xiàn)果然沒有了警告:?

?
但是新的問題很快就來了,就是在終端界面什么都沒有打印出來,代碼運行到哪個階段我也不知道,這確實不太友好,那么我們就來解決第三個問題:?
3.要解決這個問題其實很簡單,我們只需要在每一章下載完之后手動打印一些XXXX章下載完畢即可,代碼如下:?

?
我們再來看一下終端,是不是就友好很多了呢??

?
好的,那么我就就只剩下最后一個問題了:?
4.解決每一本小說都要往下翻找修改1代碼:?
解決方法就是把我們需要改變的網(wǎng)址,鏈接,保存地址等通過變量命名的形式放在最上面,我們只需要在最上面幾行代碼進行修改即可,代碼如下:?

?

?

?
那我們接下來嘗試一下?lián)Q一本小說下載:?

?
我們就在該站主頁隨便點一個元尊吧:?

?
把目錄網(wǎng)址復(fù)制一下,還有文件保存路徑:?


注意該路徑必須手動創(chuàng)建好,不然會報錯:?
成功運行,但是我們遇到了問題,就是他是從第二章開始下載的:?

?
我推測是num+13太大了,我們改成+12試試:?
注意,此時要把所有已經(jīng)下載的刪除,不然代碼會從最新章節(jié)往后下載:?

?
已經(jīng)解決,說起來剛剛運行時突然報錯,我又運行了一下又正常了,不出意外是隨機到不能用的請求頭了。?
那我們今天的教程就到此結(jié)束了嗎??
本來應(yīng)該是這樣的,但是我又想到,我們能不能做的更厲害一點,把他包裝成一個exe可執(zhí)行文件呢??
實際上是可行的,那么我們面就來做這個吧!
我們剛才留下的目標是把他打包成一個exe可執(zhí)行文件,但是在把他打包成可執(zhí)行文件之前,我們還需要對他進行一些調(diào)整,因為可打包之后就不能隨便的看到源代碼,也就沒辦法更改我們頭的兩個參數(shù),所以我們就需要用戶把更改的參數(shù)傳入進來,代碼如下:?

?

?
我們的代碼主要分為三個部分:?
默認參數(shù):?

?
由于每本小說的目錄完全不同,所以目錄網(wǎng)址沒有默認參數(shù)。?
用戶賦值:?

?
條件判斷:?

?
用戶輸入的能用的就替換掉默認的,不能用的就使用默認的。?
這樣之后基本框架就完畢了,但是我們?yōu)榱擞脩簦ㄈ绻械脑挘┑捏w驗,我們還要提示他一些東西,比如把盜版網(wǎng)站的網(wǎng)址打印出來讓他自己去訪問,比如提示他如果代碼運行出錯(即隨機到的錯誤的請求頭)就重新運行一遍.....?

?
寫完也就是這個樣子了,接下來我們就要開始去把他做成exe文件了:?
打包py文件需要用的pyinstaller包:?
我們現(xiàn)在cmd界面下載他,等等,你不會連cmd都不會打開吧??
首先按住鍵盤上的win+r鍵:?

?
在里面輸入:cmd回車:?

?
就進入了控制臺,也就是我們常說的cmd:?
然后和在pycharm終端裝包的操作一樣:?
輸入:pip3 install pyinstaller?

?
(不要看我上面的報錯嗷,報錯的話自己解決)?
安裝完之后,我們打開我們的py文件存放的地方:?

?
在這個界面,點擊上面的目錄位置:?

?
輸入cmd然后回車:?
這是什么意思呢?就是從這個文件目錄里面進入cmd,然后這個文件夾下的文件就成了根目錄(也是大多數(shù)程序的默認目錄)里的文件:?
然后輸入指令:pyinstaller -F xiaoshuopaqu.py回車運行?
xiaoshuopaqu.py是你的py文件的名字?

?
運行之后回到我們的代碼目錄下面:?

?
會發(fā)現(xiàn)多了兩個文件夾,打開里面的dist文件夾:?

?
這就是我們做好的exe文件了?
可以看到我們的原代碼只有10k,然而這個可執(zhí)行文件卻有12mb,這是因為打包過程中把python的解釋器和各種包也都打包進去了,這也意味著,這個可執(zhí)行程序并不需要使用者擁有python解釋器,我們也就可以隨便的分享給任何一個人。?
我們來運行一些這個文件:?

?
完美運行,好的,我們的教程也就到這里結(jié)束了。?
?
附加:這個可執(zhí)行文件還可以加一個騷操作,那就是把他的圖標換成你想要的樣子,因為默認的實在是太丑了:?
只需要打包過程中加入一個參數(shù):?
首先把自己制作的ico文件放到和代碼一個目錄里面:?

?
然后再cmd打包過程中輸入:?
pyinstaller -F -i dao.ico xiaoshuopaqu.py?
其中dao.ico是你的ico文件的名字?

?
然后再次查看dist文件夾:?

?
發(fā)現(xiàn)圖標并沒有變!?
難道是我們出錯了??
不是的,我們只需要把他復(fù)制到桌面就可以發(fā)現(xiàn):?

?
他已經(jīng)變成了圖標的樣子。?
好了我們的教程到這里也就結(jié)束了,整個教程寫了三天,主要是需要一步步的推導(dǎo),而不是說我遇到一個問題就解決一個問題,總的來說,雖然只是一個簡單的教程,但是我投入的時間和精力確實很多,估計我以后寫教程也不會這么詳細了,因為實在是太累了......?
(來一把簡單又快樂的騎砍怎么樣:))?
—————————————————————————————————————————
該教程是我在學(xué)習(xí)完爬取小說之后寫的,其中有些地方借鑒了一篇教程,但是還由于現(xiàn)在找不到了鏈接了,無法引用,深感愧疚……
此教程是之前寫的,現(xiàn)在發(fā)在B站主要目的是方便給別人發(fā)鏈接,有什么問題評論區(qū)留言哦~