從零開始寫Python爬蟲 --- 1.7 爬蟲實踐: 排行榜小說批量下載Ehco
5 個月前
本來只是準備做一個爬起點小說名字的爬蟲,后來想了一下,為啥不順便把小說的內容也爬下來呢?于是我就寫了這個爬蟲,他爬下了各類小說排行榜上的所有章節內容,并保存到本地。仔細想了一下,各種盜版小說閱讀器,是不是就是這樣做的呢?
目標分析:首先來看看我們排行榜的地址:
http://www.qu.la/paihangbang/
我們的目的很明確:
找到各類排行旁的的每一部小說的名字,和在該網站的鏈接:
觀察一下網頁的結構:
我們很容易就能發現,每一個分類都是包裹在:
之中
這種調理清晰的網站,大大方便了我們爬蟲的編寫
小說標題和鏈接:我們在剛才那個div里自己尋找:
玄幻奇幻排行
發現所有的小說都是在一個個列表里,并且里面清晰的定義了:
標題:title = div.a['title']
鏈接:link = 'http://www.qu.la/'
+ div.a['href']
這樣一來,我們只需要在當前頁面找到所有小說的連接,并保存在列表就行了。
列表去重的小技巧:信息的同學會發現,就算是不同類別的小說,也是會重復出現在排行榜的。
這樣無形之間就會浪費我們很多資源,尤其是在面對爬大量網頁的時候。
那么我們如何從抓取的url列表里去重呢?
剛學Python的小伙伴可能會去實現一個循環算法,來去重,
但是Python的強大之處就在于他可以通過及其優美的方式來解決很多問題,這里其實只要一行代碼就能解決:
url_list = list(set(url_list))
這里我們調用了一個list的構造函數set:這樣就能保證列表里沒有重復的元素了。是不是很簡單?
單個小說所有章節鏈接:首先我們從前面獲取到的小說url連接選取一個做實驗:
比如我最愛的擇天記:
http://www.qu.la/book/168/
依然是無比清晰的網頁結構,點個贊:
- 《擇天記》正文
我們可以很容易的找到對應章節的連接:
這個代碼是節選,不能直接用!后面會有說明
link='http://www.qu.la/' + url.a['href']
好的,這樣我們就能把一篇小說的所有章節的鏈接爬下來了。
剩下最后一步:爬取文章內容:
文章內容的爬取:首先我們打開一章,并查看他的源代碼:
我們能發現所有的正文內容,都保存在:
所有的章節名就更簡單了:
第一章 我改主意了
那我們通過bs4庫的各種標簽的查找方法,就能很簡單的找到啦
好了,讓我們看看具體代碼的實現:
代碼的實現:模塊化,函數式編程是一個非常好的習慣,我們堅持把每一個獨立的功能都寫成函數,這樣會使你的代碼簡單又可復用。
網頁抓取頭:def get_html(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status
# 我手動測試了編碼。并設置好,這樣有助于效率的提升
r.encoding = ('utr-8')
return r.text
except:
return "Someting Wrong!"
獲取排行榜小說及其鏈接:def get_content(url):
'''
爬取每一類型小說排行榜,
按順序寫入文件,
文件內容為 小說名字+小說鏈接
將內容保存到列表
并且返回一個裝滿url鏈接的列表
'''
url_list = []
html = get_html(url)
soup = bs4.BeautifulSoup(html, 'lxml')
# 由于小說排版的原因,歷史類和完本類小說不在一個div里
category_list = soup.find_all('div', class_='index_toplist mright mbottom')
history_finished_list = soup.find_all(
'div', class_='index_toplist mbottom')
for cate in category_list:
name = cate.find('div', class_='toptab').span.string
with open('novel_list.csv', 'a+') as f:
f.write("\n小說種類:{} \n".format(name))
# 我們直接通過style屬性來定位總排行榜
general_list = cate.find(style='display: block;')
# 找到全部的小說名字,發現他們全部都包含在li標簽之中
book_list = general_list.find_all('li')
# 循環遍歷出每一個小說的的名字,以及鏈接
for book in book_list:
link = 'http://www.qu.la/' + book.a['href']
title = book.a['title']
# 我們將所有文章的url地址保存在一個列表變量里
url_list.append(link)
# 這里使用a模式,防止清空文件
with open('novel_list.csv', 'a') as f:
f.write("小說名:{:<} \t 小說地址:{:<} \n".format(title, link))
for cate in history_finished_list:
name = cate.find('div', class_='toptab').span.string
with open('novel_list.csv', 'a') as f:
f.write("\n小說種類:{} \n".format(name))
general_list = cate.find(style='display: block;')
book_list = general_list.find_all('li')
for book in book_list:
link = 'http://www.qu.la/' + book.a['href']
title = book.a['title']
url_list.append(link)
with open('novel_list.csv', 'a') as f:
f.write("小說名:{:<} \t 小說地址:{:<} \n".format(title, link))
return url_list
獲取單本小說的所有章節鏈接:def get_txt_url(url):
'''
獲取該小說每個章節的url地址:
并創建小說文件
'''
url_list = []
html = get_html(url)
soup = bs4.BeautifulSoup(html, 'lxml')
lista = soup.find_all('dd')
txt_name = soup.find('h1').text
with open('/Users/ehco/Documents/codestuff/Python-crawler/小說/{}.txt'.format(txt_name), "a+") as f:
f.write('小說標題:{} \n'.format(txt_name))
for url in lista:
url_list.append('http://www.qu.la/' + url.a['href'])
return url_list, txt_name
獲取單頁文章的內容并保存到本地:這里有個小技巧:
我們從網上趴下來的文件很多時候都是帶著
之類的格式化標簽,
我們可以通過一個簡單的方法把他過濾掉:
html = get_html(url).replace('
', '\n')
我這里單單過濾了一種標簽,并將其替換成‘\n’用于文章的換行,
具體怎么擴展,大家可以開動一下自己的腦袋啦
還有,我這里的代碼是不能直接在你們的機子上用的,
因為在寫入文件的時候,絕對目錄不一樣
def get_one_txt(url, txt_name):
'''
獲取小說每個章節的文本
并寫入到本地
'''
html = get_html(url).replace('
', '\n')
soup = bs4.BeautifulSoup(html, 'lxml')
try:
txt = soup.find('div', id='content').text.replace(
'chaptererror();', '')
title = soup.find('title').text
with open('/Users/ehco/Documents/codestuff/Python-crawler/小說/{}.txt'.format(txt_name), "a") as f:
f.write(title + '\n\n')
f.write(txt)
print('當前小說:{} 當前章節{} 已經下載完畢'.format(txt_name, title))
except:
print('someting wrong')
缺點:本次爬蟲寫的這么順利,更多的是因為爬的網站是沒有反爬蟲技術,以及文章分類清晰,結構優美。
但是,按照我們的這篇文的思路去爬取小說,
我大概計算了一下:
一篇文章需要:0.5s
一本小說(1000張左右):8.5分鐘
全部排行榜(60本):?8.5小時!
是的! 時間太長了!
那么,這種單線程的爬蟲,速度如何能提高呢?
自己洗個多線程模塊?
其實還有更好的方式:下一個大的章節我們將一起學習Scrapy框架
學到那里的時候,我再把這里代碼重構一邊,
你會驚奇的發現,速度幾十倍甚至幾百倍的提高了!
這其實也是多線程的威力!
最后看一下結果吧:排行榜結果:
小說結果:
學到這里是不是越來越喜歡爬蟲這個神奇的東西了呢?
加油,更神奇的東西還在后面呢!
每天的學習記錄都會 同步更新到:
微信公眾號: findyourownway
知乎專欄:從零開始寫Python爬蟲 - 知乎專欄
blog :?www.ehcoblog.ml
Github:?Ehco1996/Python-crawler