近些年裏,網絡小說盛行,可是小說網站爲了增長收益,在小說中增長了不少廣告彈窗,使人煩不勝煩,那如何安靜觀看小說而不看廣告呢?答案就是爬蟲。本文主要以一個簡單的小例子,簡述如何經過爬蟲來爬取小說,僅供學習分享使用,若有不足之處,還請指正。html
本文爬取的爲【某橫中文網】的一部小說【妙手小醫仙】,已完結,共187章,信息以下:數組
網址:http://book.abcde.com/showchapter/1102448.html瀏覽器
本次主要爬取小說章節信息,及每一章對應的正文信息。章節信息以下所示:網絡
經過瀏覽器自帶的開發人員工具【快捷鍵F12或Ctrl+Shift+I】進行分析,發現全部的章節都包含在ul【無序列表標籤】中,每個章節連接對應於li【列表項目標籤】標籤中的a【超連接標籤】標籤,其中a標籤的href屬性就是具體章節網址,a標籤的文本就是章節標題,以下所示:app
經過分析,發現章節所有內容,均在div【class=reader_box】中,其中包括標題div【class=title_txtbox】,章節信息div【class=bookinfo】,及正文信息div【class=content】,全部正文包含在p【段落標籤】中。以下所示:ide
獲取請求頁面內容,由於本例須要屢次獲取頁面內容,因此封裝爲一個單獨的函數,以下所示:函數
1 def get_data(url: str = None): 2 """ 3 獲取數據 4 :param url: 請求網址 5 :return:返回請求的頁面內容 6 """ 7 # 請求頭,模擬瀏覽器,不然請求會返回418 8 header = { 9 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 10 'Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363'} 11 resp = requests.get(url=url, headers=header) # 發送請求 12 13 if resp.status_code == 200: 14 if resp.encoding != resp.apparent_encoding: 15 # 若是返回的編碼和頁面顯示編碼不一致,直接獲取text會出現亂碼,須要轉碼 16 return resp.content.decode(encoding=resp.apparent_encoding) 17 else: 18 # 若是返回成功,則返回內容 19 return resp.text 20 else: 21 # 不然,打印錯誤狀態碼,並返回空 22 print('返回狀態碼:', resp.status_code) 23 return
注意:有可能不一樣網站,返回內容的編碼和頁面顯示的編碼不一致,可能會出現中文亂碼的狀況,因此本例進行編碼設置。工具
要獲取整本小說內容,首先就要獲取章節列表,而後保存到內存數組中,以便於獲取具體正文。以下所示:學習
1 def parse_chapters(html: str = None): 2 """ 3 爬取章節列表 4 :param html: 5 :return: 6 """ 7 if html is None: 8 return 9 else: 10 chapters = [] 11 bs = BeautifulSoup(html, features='html.parser') 12 ul_chapters = bs.find('ul', class_='chapter-list clearfix') 13 # print(ul_chapters) 14 li_chapters = ul_chapters.find_all('li', class_='col-4') # 此處須要注意,頁面源碼查看是有空格,可是BeautifulSoup轉換後空格消失 15 for li_chapter in li_chapters: 16 a_tag = li_chapter.find('a') 17 # print(a_tag) 18 a_href = a_tag.get('href') # 此處也能夠用a_tag['href'] 19 a_text = a_tag.get_text() # 獲取內容:章節標題 20 chapters.append({'title': a_text, 'href': a_href}) 21 22 return chapters
當獲得單個章節的連接時,就能夠獲取單個章節的內容,並進行解析,以下所示:網站
1 def parse_single_chapter(html: str = None): 2 """ 3 解析單個章節內容 4 :param html: 5 :return: 6 """ 7 bs = BeautifulSoup(html, features='html.parser') 8 div_reader_box = bs.find('div', class_='reader_box') 9 div_title = div_reader_box.find('div', class_='title_txtbox') 10 title = div_title.get_text() # 獲取標題 11 div_book_info = div_reader_box.find('div', class_='bookinfo') 12 book_info = div_book_info.get_text() 13 div_content = div_reader_box.find('div', class_='content') 14 content = '' 15 p_tags = div_content.find_all('p') 16 for p_tag in p_tags: 17 content =content + p_tag.get_text() + '\r\n' 18 # content = div_content.get_text() 19 return title + '\n' + book_info + '\n' + content
循環獲取單個章節正文頁面,並進行解析,而後保存。以下所示:
1 def get_and_parser_single_chapter(chapters: list = []): 2 """ 3 獲取單個章節 4 :param chapters: 章節列表 5 :return: 6 """ 7 for (index, chapter) in enumerate(chapters, 1): 8 title = chapter.get('title') 9 href = chapter.get('href') 10 while True: 11 print('開始第%d章爬取' % index) 12 html = get_data(href) 13 if html is not None: 14 content = parse_single_chapter(html) 15 save_data(title, content) # 保存數據 16 print('第%d章爬取成功' % index) 17 break 18 else: 19 print('第%d章爬取失敗' % index) 20 time.sleep(2)
當寫好單個功能函數時,順序調用就是完整的爬蟲,以下所示:
1 url = 'http://book.abcde.com/showchapter/1102448.html' 2 print('開始時間>>>>>', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 3 html_chapters = get_data(url) 4 chapters = parse_chapters(html_chapters) 5 get_and_parser_single_chapter(chapters) 6 print('結束時間>>>>>', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 7 print('done')
爬取到的小說列表,以下所示:
每個章節內容,以下所示:
示例完整代碼,以下所示:
1 import requests # 請求包 用於發起網絡請求 2 from bs4 import BeautifulSoup # 解析頁面內容幫助包 3 import time 4 5 """ 6 說明:爬取小說 7 步驟:1. 先爬取全部章節,及章節明細對應的URL 8 2. 解析單個章節的內容 9 3. 保存 10 """ 11 12 13 def get_data(url: str = None): 14 """ 15 獲取數據 16 :param url: 請求網址 17 :return:返回請求的頁面內容 18 """ 19 # 請求頭,模擬瀏覽器,不然請求會返回418 20 header = { 21 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 22 'Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363'} 23 resp = requests.get(url=url, headers=header) # 發送請求 24 25 if resp.status_code == 200: 26 if resp.encoding != resp.apparent_encoding: 27 # 若是返回的編碼和頁面顯示編碼不一致,直接獲取text會出現亂碼,須要轉碼 28 return resp.content.decode(encoding=resp.apparent_encoding) 29 else: 30 # 若是返回成功,則返回內容 31 return resp.text 32 else: 33 # 不然,打印錯誤狀態碼,並返回空 34 print('返回狀態碼:', resp.status_code) 35 return 36 37 38 def parse_chapters(html: str = None): 39 """ 40 爬取章節列表 41 :param html: 42 :return: 43 """ 44 if html is None: 45 return 46 else: 47 chapters = [] 48 bs = BeautifulSoup(html, features='html.parser') 49 ul_chapters = bs.find('ul', class_='chapter-list clearfix') 50 # print(ul_chapters) 51 li_chapters = ul_chapters.find_all('li', class_='col-4') # 此處須要注意,頁面源碼查看是有空格,可是BeautifulSoup轉換後空格消失 52 for li_chapter in li_chapters: 53 a_tag = li_chapter.find('a') 54 # print(a_tag) 55 a_href = a_tag.get('href') # 此處也能夠用a_tag['href'] 56 a_text = a_tag.get_text() # 獲取內容:章節標題 57 chapters.append({'title': a_text, 'href': a_href}) 58 59 return chapters 60 61 62 def get_and_parser_single_chapter(chapters: list = []): 63 """ 64 獲取單個章節 65 :param chapters: 章節列表 66 :return: 67 """ 68 for (index, chapter) in enumerate(chapters, 1): 69 title = chapter.get('title') 70 href = chapter.get('href') 71 while True: 72 print('開始第%d章爬取' % index) 73 html = get_data(href) 74 if html is not None: 75 content = parse_single_chapter(html) 76 save_data(title, content) # 保存數據 77 print('第%d章爬取成功' % index) 78 break 79 else: 80 print('第%d章爬取失敗' % index) 81 time.sleep(2) 82 83 84 def parse_single_chapter(html: str = None): 85 """ 86 解析單個章節內容 87 :param html: 88 :return: 89 """ 90 bs = BeautifulSoup(html, features='html.parser') 91 div_reader_box = bs.find('div', class_='reader_box') 92 div_title = div_reader_box.find('div', class_='title_txtbox') 93 title = div_title.get_text() # 獲取標題 94 div_book_info = div_reader_box.find('div', class_='bookinfo') 95 book_info = div_book_info.get_text() 96 div_content = div_reader_box.find('div', class_='content') 97 content = '' 98 p_tags = div_content.find_all('p') 99 for p_tag in p_tags: 100 content =content + p_tag.get_text() + '\r\n' 101 # content = div_content.get_text() 102 return title + '\n' + book_info + '\n' + content 103 104 105 def save_data(name, content): 106 """ 107 保存數據 108 :param name: 文件名 109 :param content: 文件內容 110 :return: 111 """ 112 with open('妙手小醫仙\\' + name + '.txt', 'w', encoding='utf-8') as f: 113 f.write(content) 114 115 116 url = 'http://book.zongheng.com/showchapter/1102448.html' 117 print('開始時間>>>>>', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 118 html_chapters = get_data(url) 119 chapters = parse_chapters(html_chapters) 120 get_and_parser_single_chapter(chapters) 121 print('結束時間>>>>>', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 122 print('done')
我歷來不認爲半小時是我微不足道的很小的一段時間。真正的強者,不是沒有眼淚的人,而是含着眼淚奔跑的人。但行前路,無問西東 。
長相思·山一程
山一程,水一程,身向榆關那畔行,夜深千賬燈。
風一更,雪一更,聒碎鄉心夢不成,故園無此聲。