一、目標
排行榜的地址:http://www.qu.la/paihangbang/
找到各類排行旁的的每一部小說的名字,和在該網站的鏈接。
二、觀察網頁的結構
很容易就能發現,每一個分類都是包裹在:
<div class="index_toplist mright mbottom">
之中,
這種條理清晰的網站,大大方便了爬蟲的編寫。
在當前頁面找到所有小說的連接,並保存在列表即可。
三、列表去重的小技巧:
就算是不同類別的小說,也是會重復出現在排行榜的。
這樣無形之間就會浪費很多資源,尤其是在面對爬大量網頁的時候。
這里只要一行代碼就能解決:
url_list = list(set(url_list))
這里調用了一個list的構造函數set:這樣就能保證列表里沒有重復的元素了。
四、代碼實現
模塊化,函數式編程是一個非常好的習慣,堅持把每一個獨立的功能都寫成函數,這樣會使代碼簡單又可復用。
1.網頁抓取頭:
import requests from bs4 import BeautifulSoup def get_html(url): try: r = requests.get(url,timeout=30) r.raise_for_status r.encoding='utf-8' return r.text except: return 'error!'
2.獲取排行榜小說及其鏈接:
爬取每一類型小說排行榜, 按順序寫入文件, 文件內容為 小說名字+小說鏈接 將內容保存到列表 並且返回一個裝滿url鏈接的列表
def get_content(url): url_list = [] html = get_html(url) soup = BeautifulSoup(html,'lxml') # 由於小說排版的原因,歷史類和完本類小說不在一個div里 category_list = soup.find_all('div',class_='index_toplist mright mbottom') history_list = soup.find_all('div',class_='index_toplist mbottom') for cate in category_list: name = cate.find('div',class_='toptab').span.text with open('novel_list.csv','a+') as f: f.write('\n小說種類:{} \n'.format(name)) book_list = cate.find('div',class_='topbooks').find_all('li') # 循環遍歷出每一個小說的的名字,以及鏈接 for book in book_list: link = 'http://www.qu.la/' + book.a['href'] title = book.a['title'] url_list.append(link) # 這里使用a模式寫入,防止清空文件 with open('novel_list.csv','a') as f: f.write('小說名:{} \t 小說地址:{} \n'.format(title,link)) for cate in history_list: name = cate.find('div',class_='toptab').span.text with open('novel_list.csv','a') as f: f.write('\n小說種類: {} \n'.format(name)) book_list = cate.find('div',class_='topbooks').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
3.獲取單本小說的所有章節鏈接:
獲取該小說每個章節的url地址,並創建小說文件
# 獲取單本小說的所有章節鏈接 def get_txt_url(url): url_list = [] html = get_html(url) soup = BeautifulSoup(html,'lxml') list_a = soup.find_all('dd') txt_name = soup.find('dt').text with open('C:/Users/Administrator/Desktop/小說/{}.txt'.format(txt_name),'a+') as f: f.write('小說標題:{} \n'.format(txt_name)) for url in list_a: url_list.append('http://www.qu.la/' + url.a['href']) return url_list,txt_name
4.獲取單頁文章的內容並保存到本地
這里有個小技巧:
從網上爬下來的文件很多時候都是帶着<br>之類的格式化標簽,
可以通過一個簡單的方法把它過濾掉:
html = get_html(url).replace('<br/>', '\n')
這里單單過濾了一種標簽,並將其替換成‘\n’用於文章的換行,
def get_one_txt(url,txt_name): html = get_html(url).replace('<br/>','\n') soup = BeautifulSoup(html,'lxml') try: txt = soup.find('div',id='content').text title = soup.find('h1').text with open('C:/Users/Administrator/Desktop/小說/{}.txt'.format(txt.name),'a') as f: f.write(title + '\n\n') f.write(txt) print('當前小說:{}當前章節{}已經下載完畢'.format(txt_name,title)) except: print('ERROR!')
6.主函數
def get_all_txt(url_list): for url in url_list: # 遍歷獲取當前小說的所有章節的目錄,並且生成小說頭文件 page_list,txt_name = get_txt_url(url) def main(): # 小說排行榜地址 base_url = 'http://www.qu.la/paihangbang/' # 獲取排行榜中所有小說的url鏈接 url_list = get_content(base_url) # 除去重復的小說 url_list = list(set(url_list)) get_all_txt(url_list) if __name__ == '__main__': main()
7.輸出結果
5.缺點:
本次爬蟲寫的這么順利,更多的是因為爬的網站是沒有反爬蟲技術,以及文章分類清晰,結構優美。
但是,按照這篇文的思路去爬取小說,
大概計算了一下:
一篇文章需要:0.5s
一本小說(1000張左右):8.5分鍾
全部排行榜(60本): 8.5小時!
那么,這種單線程的爬蟲,速度如何能提高呢?
自己寫個多線程模塊?
其實還有更好的方式:Scrapy框架
后面可將這里的代碼重構一邊遍,
速度會幾十倍甚至幾百倍的提高了!
這其實也是多線程的威力!