python爬蟲入門之爬取小說.md
新手教學:用Python爬取小說
我們在學習Python之餘總想著讓其更具趣味性,可以更好地學習。下面我將講解如何去從網站中爬取我們想看的小說。讓我們枯燥無聊的學習生涯稍微多些趣味。 需要只是一點點對requests庫、Beautiful庫及python基礎知識的瞭解。 Python版本:Python3.X 執行平臺:Windows IDE:PyCharm 瀏覽器:Chrome 本次講解所有程式碼已放入GitHub上託管。詳細瞭解請點選
瞭解爬蟲
我們一直口口聲聲說著用爬蟲去爬取某某資料。 那麼問題來了,什麼是爬蟲呢? 網路爬蟲,別稱網路蜘蛛,它是一段程式(一個指令碼),能夠模擬瀏覽器自動的瀏覽網頁,來自動地批量地採集我們需要的資源。 廢話不多說,開始今天的爬蟲之旅。
爬取章節內容
俗話說得好,飯要一口一口吃,路要一步一步走。我們要爬取每一章內容,須先知曉如何爬取一章內容。 本次爬取網站:筆趣閣 本次爬取小說:《修真四萬年》 第一步,根據URL獲取網頁的HTML資訊 在python3中可以用requests庫進行網頁爬取。
import requests
if __name__=='__main__':
target='http://www.biquge.cm/6/6217/3638117.html'
req=requests.get(url=target)
print(req.text)
執行結果如下: 我們輕鬆地獲得了HTML資訊,但為什麼會出現亂碼呢? 事實上這是由於字元編碼問題引起的。往往用requests庫抓取中文網頁時容易出現亂碼問題。那如何解決呢? 下面介紹兩種方法: (1)我們可以在呼叫response.text()之前使用response.encoding=‘編碼格式’ 這個方法需要我們進入所爬去網站去檢視編碼格式。 點選滑鼠右鍵,點選檢查,得到如下介面:
點選右側head標籤得到如圖: 可以很顯然看到編碼格式為‘gbk’。 所以我們可以寫出程式碼來解決亂碼問題:
import requests
if __name__=='__main__':
target='http://www.biquge.cm/6/6217/3638117.html'
req=requests.get(url=target)
req.encoding='gbk'
print(req.text)
執行得到: (2)使用req.content返回bytes型資料,然後將bytes型資料轉化為str資料。 程式碼如下:
import requests if __name__=='__main__': target='http://www.biquge.cm/6/6217/3638117.html' req=requests.get(url=target) html=req.content html_doc=str(html,'gbk') print(html_doc)
解決完亂碼問題後我們得到了我們需要的能理解看懂的html資訊,但是,有很多資訊是我們不想看到的,我們只想要小說正文內容。我們不關心div、br這些html標籤。 那如何把正文內容從這些眾多的html標籤中提取出來呢? 這就用到了BeautifulSoup庫。 同理按上面方法我們檢視第一章目標網頁: 我們可以看到文章的所有內容都放在了一個名為div的東西下面,這個東西就是html標籤。 HTML標籤是HTML語言中最基本的單位,HTML標籤是HTML最重要的組成部分。 我們可以看到,我們所需的文字內容在一個div標籤下,這個標籤是這樣的:
div id=“content”
知道了這個內容,我們就可以用BeautifulSoup來提取我們想要的內容了。編寫程式碼如下:
from bs4 import BeautifulSoup
import requests
if __name__=='__main__':
target='http://www.biquge.cm/6/6217/3638117.html'
req=requests.get(url=target)
html=req.content
html_doc=str(html,'gbk')
bf=BeautifulSoup(html_doc)
texts=bf.find_all('div',id="content")
print(texts)
在解析html之前,先建立一個BeautifulSoup物件,BeautifulSoup函式裡面的引數就是我們獲得的html資訊。然後使用find_all方法,獲取html資訊中所有id屬性為content的div標籤。 執行程式碼檢視我們匹配結果: 我們可以看到,我們已經順利匹配到我們關心的正文內容,但是還有一些我們不想要的東西。比如div標籤名,br標籤,以及各種空格。怎麼去除這些東西呢?我們繼續編寫程式碼:
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
target = 'http://www.biquge.cm/6/6217/3638117.html'
req = requests.get(url = target)
html = req.content
html_doc=str(html,'gbk')
bf = BeautifulSoup(html_doc)
texts = bf.find_all('div', id="content")
print(texts[0].text)
find_all匹配的返回的結果是一個列表。提取匹配結果後,使用text屬性,提取文字內容,濾除br標籤。得到:
我們已經順利獲得了一個章節的內容,要想下載正本小說,我們就要獲取每個章節的連結。我們先分析下小說目錄:http://www.biquge.cm/6/6217/
在網址進行審查得到:
我們看到每個章節的名字存放在了<a>
標籤裡面。<a>
標籤還有一個href屬性。<a>
標籤定義了一個超連結,用於從一張頁面連結到另一張頁面。<a>
標籤最重要的屬性是 href 屬性,它指示連結的目標。
我們將之前獲得的第一章節的URL和<a>
標籤對比看一下:
我們可以看到<a>
標籤中href屬性存放的屬性值/1_1094/5403177.html是章節URLhttp://www.biqukan.com/1_1094/5403177.html的後半部分。那這樣,我們就可以根據<a>
標籤的href屬性值獲得每個章節的連結和名稱了。
編寫程式碼如下:
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
target = 'http://www.biquge.cm/6/6217/'
req = requests.get(url = target)
html = req.content
html_doc=str(html,'gbk')
div_bf = BeautifulSoup(html_doc)
div = div_bf.find_all('div', id='list' )
print(div[0])
執行結果:
接下來再匹配每一個<a>
標籤,並用a.get(‘href’)方法獲取href的屬性值,使用a.string方法獲取章節名。程式碼如下:
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
server = 'http://www.biquge.cm/'
target = 'http://www.biquge.cm/6/6217/'
req = requests.get(url = target)
html = req.content
html_doc=str(html,'gbk')
div_bf = BeautifulSoup(html_doc)
div = div_bf.find_all('div', id='list' )
a_bf = BeautifulSoup(str(div[0]))
a = a_bf.find_all('a')
for each in a:
print(each.string, server + each.get('href'))
find_all返回的是一個列表,裡邊存放了很多的<a>
標籤,所以使用for迴圈遍歷每個<a>
標籤並打印出來,執行結果如下:
整合程式碼:
# -*- coding:UTF-8 -*-
from bs4 import BeautifulSoup
import requests, sys
class downloader(object):
# 類說明下載筆趣閣修真四萬年,self只有在類的方法中才會有,
# 獨立的函式或方法是不必帶有self的。self在定義類的方法時是必須有的,
# 雖然在呼叫時不必傳入相應的引數。
def __init__(self): # 初始化變數
# 爬取圖書網站,self.server='http://www.biquge.cm/' 的意思
# 就是把外部傳來的引數server的值賦值給類downloader自己的屬性
# 變數self.server,下面函式可共用
self.server = 'http://www.biquge.cm/'
self.target = 'http://www.biquge.cm/6/6217/'
# 爬取圖書主目錄所在網址,用來獲取每一章節url
self.names = [] # 存放章節名
self.urls = [] # 存放章節連結
self.nums = 0 # 章節數
def get_download_url(self): # 獲取下載連結
req = requests.get(url=self.target) # 獲得該網頁的html資訊
html = req.content # #獲得html檔案
html_doc=str(html,'gbk')
div_bf = BeautifulSoup(html_doc) # 建立一個BeautifulSoup物件
div = div_bf.find_all('div', id='list')
# 匹配div標籤,獲取id屬性list的內容,用find_all是因為獲得的是列表
a_bf = BeautifulSoup(str(div[0])) # 以list中的內容構建Beautiful物件
a = a_bf.find_all('a') # 找到所有a標籤,a標籤中為每一章網址
self.nums = len(a[:]) # 索引從第一章到最後一章,記數
for each in a[:]:
self.names.append(each.string) # 新增a標籤下的字串即章節名
self.urls.append(self.server + each.get('href'))
# 獲取每一章連結,用get()方法獲取a標籤中href屬性
def get_contents(self, target): # 獲取章節內容
req = requests.get(url=target)
html = req.content
html_doc=str(html,'gbk')
bf = BeautifulSoup(html_doc)
texts = bf.find_all('div', id="content")
# 獲取div標籤id屬性content的內容
texts = texts[0].text.replace('\xa0'*8,'\n')
# 剔除"'div'標籤及'id=content'"和br/,
# 提取texts內容中text文字
return texts
def writer(self, name, path, text): # 將爬取的文章內容寫入檔案
with open(path, 'a' ,encoding='utf-8')as f:
# ‘a’表示追加到檔案,encoding='utf-8'表示以utf-8進行編碼
f.write(name + '\n') # write方法寫入章節名並換行
f.writelines(text) # writelines方法寫入文章內容
f.write('\n') # 換行寫完一章
# 當.py檔案被直接執行時,if __name__ == '__main__'之下的程式碼塊將被執行
if __name__ == "__main__":
dl = downloader()
dl.get_download_url()
print('《修真四萬年》開始下載:')
for i in range(dl.nums):
dl.writer(dl.names[i], '修真四萬年.txt', dl.get_contents(dl.urls[i]))
sys.stdout.write(" 已下載:%.3f%%" % float(i / dl.nums) + '\r')
# sys.stdout是對映到開啟指令碼的視窗
sys.stdout.flush()
# 在Linux系統下,必須加入sys.stdout.flush()才能一秒輸一個數字
# 在Windows系統下,加不加sys.stdout.flush()都能一秒輸出一個數字
print('《修真四萬年》下載完成')