1. 程式人生 > 實用技巧 >詳細記錄了python爬取小說《元尊》的整個過程,看了你必會~

詳細記錄了python爬取小說《元尊》的整個過程,看了你必會~

學了好幾天的滲透測試基礎理論,週末了讓自己放鬆一下,最近聽說天蠶土豆有一本新小說,叫做《元尊》,學生時代的我可是十分喜歡讀天蠶土豆的小說,《鬥破蒼穹》相信很多小夥伴都看過吧。今天我們就來看看如果一步一步爬下來《元尊》的所有內容。

首先我們需要選擇一個網站進行爬取,我這邊使用的是書家園網站,其它網站的操作也是類似原理。

相關庫檔案

我們使用的庫有requests、re和time,其中re和time都是python自帶庫,我們只需要安裝一個requests庫。

pip install requests

編碼過程

我們可以先訪問書家園網站找到《元尊》書籍首頁的url——https://www.shujy.com/5200/9683/。

通過requests進行請求,然後將html打印出來。

import requests
url ='https://www.shujy.com/5200/9683/'
response = requests.get(url)
html = response.text

print(html)

打印出來如下圖:

我們找到html中關於文章標題和作者的部分

我們通過正則表示式將標題和作者提取出來

title = re.findall(r'<meta property="og:novel:book_name" content="(.*?)"/>',html)[0]
author = re.findall(r'<meta property="og:novel:author" content="(.*?)"/>',html)[0]

接下來我們就需要將每一章小說的連結拿出來了,我們通過瀏覽器中F12工具

  1. 點選左上角的箭頭
  2. 點選我們需要定位的元素,我們需要找到每一章的連結,所以我們點選“正文 第一章”
  3. 我們就可以看到開發者工具中出現了物件在html中的位置

分析連結的位置我們發現都是在“<div id="list"></div>”這個div標籤中間,然後每個連結都是在href後面。那我們先將這個div標籤中的內容取出來然後對取出的內容找到所有的href標籤中的連結放在一個列表中。

dl =re.findall(r'<div id="list">.*?</div>',html,re.S)[0]
chapter_info_list=re.findall(r'<a href="(.*?)">(.*?)</a>',dl)

這樣我們就拿到了所有的章節列表,現在我們需要考慮如何去獲取每一章節的內容。我們將首頁的URL和每一個章節的連結進行拼接。

chapter_url = url+'/'+chapter_url
chapter_url = chapter_url.replace(' ','')

然後我們同樣通過requests庫獲取到章節內容的HTML檔案

chapter_response = requests.get(chapter_url)
chapter_html = chapter_response.text

通過同樣的方式,我們發現正文的內容都是在“<div id="content"></div>”這樣一個div標籤中

我們將這個div標籤中的正文內容全部拿出來

#獲取第一頁正文內容
chapter_content = re.findall(r'<div id="content">(.*?)</div>',chapter_html,re.S)[0]

我們將取出來的內容列印一下看看

我們發現還存在一些“<br />”和“&emsp;&emsp;”這樣的元素,這些都是我們不希望看到的,那我們就通過replace函式給過濾掉。

chapter_content = chapter_content.replace('&emsp;&emsp;','')
chapter_content = chapter_content.replace('<br />','')

我們看看過濾後的內容

發現還是有些不對,為什麼每一行文字都空了一行呢?我們通過debug看一下過程中的chapter_content內容

果然還存在一些製表符,那我們就只保留一個換行符“\n”

chapter_content = chapter_content.replace('\r\n\r', '')

這樣我們就將這個頁面的正文全部扒下來了,但我們翻到頁面的末尾我們發現每章可能不僅僅一頁,可能存在兩頁、三頁甚至更多內容,怎麼才能完整的將這不確定的內容拿下來呢?

我們看到在每一頁的正文中都寫明瞭這一章一共需要多少頁,並且提供了下一頁的連結,我們就通過這個線索來完成。

首先我們需要將總共的頁數和下一頁的連結取出來。

chapter_url,current_page,total_pages = re.findall(r'本章未完,請點選<a style="color:red;" href="(.*?)">下一頁</a>繼續閱讀!第(.*?)頁 / 共(.*?)頁</p>', chapter_content,re.S)[0]

然後我們利用一個for迴圈去取內容,取的方式和前面類似,就不再詳細描述了,直接上程式碼。

for i in range(1,int(total_pages)):
    chapter_url,current_page,total_pages = re.findall(r'本章未完,請點選<a style="color:red;" href="(.*?)">下一頁</a>繼續閱讀!第(.*?)頁 / 共(.*?)頁</p>',chapter_content)[0]
    chapter_url = url+'/'+chapter_url
    chapter_url = chapter_url.replace(' ','')
    chapter_response = requests.get(chapter_url)
    chapter_html =chapter_response.text
    chapter_content = re.findall(r'<div id="content">(.*?)</div>', chapter_html, re.S)[0]
    chapter_content = chapter_content.replace('&emsp;&emsp;', '')
    chapter_content = chapter_content.replace('<br />', '')
    chapter_content = chapter_content.replace('\r\n\r', '')
    f.write('\n')
    f.write(chapter_content)

最後我們只需要在外面加一個檔案寫入操作,將每一次讀出的正文內容寫入進去就好了。

with open('%s.txt'%title,'w') as f:
    f.write(title)
    f.write('\n')
    f.write(author)
    f.write('\n')

list index out of range報錯的處理

看著是一切都完成了,可是在我最後來下載的時候,經常在不同的章節出現這樣的錯誤。

這一次可能是在第四章出現問題,下一個可能是在第十章出現問題,總之不固定。我查詢了一下這種錯誤一般會有兩種情況

  1. list[index]index超出範圍
  2. list是一個空的,沒有一個元素,進行list[0]就會出現錯誤!

雖說查詢到了原因,這兩種情況都不能應該出現隨機章節出現報錯呀,我還是沒有找到原因,如果有大神看到了可以指定一二。

但是我找到一個規避的措施,就是既然它是隨機章節報錯,那就是我一旦檢測到報錯之後就再重新請求一次url,重新通過正則校驗一次。為此我拎出來一個這樣的函式。

def find(pattern,string,url):
    try:
        chapter_content = re.findall(pattern, string, re.S)[0]
        return chapter_content
    except Exception as e:
        print(e)
        time.sleep(1)
        chapter_response = requests.get(url)

        chapter_html = chapter_response.text
        print(chapter_html)
        print(url)
        i = find(pattern,chapter_html,url)
    return i

執行之後果然可行,我一直執行著,現在已經下載了一百多章了

現在看來唯一的缺點就是有點慢,原諒菜鳥小白還沒有怎麼弄過多執行緒和多程序,下次我們再來改進吧。