爬取筆趣閣小說(搜索+爬取)
首先看看最終效果(gif):
實現步驟:1.探查網站“http://www.xbiquge.la/”,看看網站的實現原理。
2.編寫搜索功能(獲取每本書目錄的URL)。
3.編寫寫入功能(按章節寫入文件)。
4.完善代碼(修修bug,建了文件夾)。
ps:所需模塊
1 import requests 2 import bs4 # 爬網站必備兩個模塊不解釋 3 import os # 用來創建文件夾的 4 import sys # 沒啥用單純為了好看 5 import time 6 import random # 使用隨機數設置延時
一、網站搜索原理,並用Python實現。
我本以為這個網站和一般網站一樣,通過修改URL來進行搜索,結果並不然。
可以看出這個網站不會因搜索內容改變而改變URL。
那還有一種可能:通過POST請求,來更新頁面。讓我們打開Network驗證一下。
我的猜想是對的。接下來開始模擬。
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36", "Cookie": "_abcde_qweasd=0; Hm_lvt_169609146ffe5972484b0957bd1b46d6=1583122664; bdshare_firstime=1583122664212; Hm_lpvt_169609146ffe5972484b0957bd1b46d6=1583145548", "Host": "www.xbiquge.la"} # 設置頭盡量多一點 以防萬一 x = str(input("輸入書名或作者名:")) # 通過變量來控制我們要搜索的內容 data = {'searchkey': x} url = 'http://www.xbiquge.la/modules/article/waps.php' r = requests.post(url, data=data, headers=headers) soup = bs4.BeautifulSoup(r.text.encode('utf-8'), "html.parser") # 用BeautifulSoup方法方便我們提取網頁內容網頁
可是如果現在我printf(soup)后發現里面的中文全為亂碼!
這不難看出是編碼格式不對,但我們可以用encoding方法來獲取編碼方式。
改完編碼后就可以正常提取了,並且和瀏覽器顯示的一致,都是我們搜索的內容。
二、接下來我們就來在這一堆代碼里找到我們想要的內容了(書名,作者,目錄URL)
通過元素審查我們很容易就可以定位到它們所在位置。
鏈接和書名在"td class even"<a>標簽里,作者在"td class=even"里。
什么!標簽重名了!怎么辦!管他三七二十一!先把"td class=even"全打印出來看看。
1 book_author = soup.find_all("td", class_="even") 2 for each in book_author: 3 print(each)
可以發現每個each分為兩層。
那我們可以奇偶循環來分別處理這兩層。(因為如果不分層處理的話第一層要用的方法(each.a.get("href")在第二層會報錯,好像try也可以處理這個錯,沒試)
並且用創建兩個三個列表來儲存三個值。
1 books = [] # 書名 2 authors = [] # 作者名 3 directory = [] # 目錄鏈接 4 tem = 1 5 for each in book_author: 6 if tem == 1: 7 books.append(each.text) 8 tem -= 1 9 directory.append(each.a.get("href")) 10 else: 11 authors.append(each.text) 12 tem += 1
成功!三個列表全部一樣對應!
那么要如何實現選擇一個序號,來讓Python獲得一個目錄鏈接呢?
我們可以這樣:
1 print('搜索結果:') 2 for num,book, author in zip(range(1, len(books)+1),books, authors): 3 print((str(num)+": ").ljust(4)+(book+"\t").ljust(25) + ("\t作者:" + author).ljust(20)) 4 search = dict(zip(books, directory))
這樣打印出來的效果就是這樣了:
是不是很神奇!“search”是我們用書名和目錄URL組成的字典,我們只要
return search[books[i-1]]
就可以讓下一個函數得到這本書的目錄URL了。
三、獲取章節URL,獲取文本內容,寫入文件。
我們得到目錄的URL后就可以用相同的方法獲取每一章節的URL了(不贅述了)。
1 def get_text_url(titel_url): 2 url = titel_url 3 global headers 4 r = requests.get(url, headers=headers) 5 soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser") 6 titles = soup.find_all("dd") 7 texts = [] 8 names = [] 9 texts_names = [] 10 for each in titles: 11 texts.append("http://www.xbiquge.la"+each.a["href"]) 12 names.append(each.a.text) 13 texts_names.append(texts) 14 texts_names.append(names) 15 return texts_names # 注意這里的返回值是一個包含兩個列表的列表!!
注意這里的返回值是一個包含兩個列表的列表!!
texts_names[0]就是每一章節的URL,texts_names[0]是章節名
為下一個寫內容的函數方便調用。
接下來接是寫文件了!
1 search = dict(zip(books, directory)) 2 url = texts_url[0][n] 3 name = texts_url[1][n] 4 req = requests.get(url=url, headers=headers) 5 time.sleep(random.uniform(0, 0.5)) # 即使設置了延遲,他還有會可能503(沒辦法小網站) 6 req.encoding = 'UTF-8' # 這里的編碼是UTF-8,跟目錄不一樣,要注意! 7 html = req.text 8 soup = bs4.BeautifulSoup(html, features="html.parser") 9 texts = soup.find_all("div", id="content") 10 while (len(texts) == 0): # 他如果503的話,讀取內容就什么都木有,那直接讓他再讀一次,直到讀出來為止。 11 req = requests.get(url=url, headers=headers) 12 time.sleep(random.uniform(0, 0.5)) 13 req.encoding = 'UTF-8' 14 html = req.text 15 soup = bs4.BeautifulSoup(html, features="html.parser") 16 texts = soup.find_all("div", id="content") 17 else: 18 content = texts[0].text.replace('\xa0' * 8, '\n\n') 19 content = content.replace( 20 "親,點擊進去,給個好評唄,分數越高更新越快,據說給新筆趣閣打滿分的最后都找到了漂亮的老婆哦!手機站全新改版升級地址:http://m.xbiquge.la,數據和書簽與電腦站同步,無廣告清新閱讀!", "\n") 21 # 使用text屬性,提取文本內容,濾除br標簽,隨后使用replace方法,去掉八個空格符號,並用回車代替 再去除每一頁都有得結尾 22 with open(name + '.txt', "w", encoding='utf-8')as f: 23 f.write(content) 24 sys.stdout.write("\r已下載{}章,還剩下{}章".format(count, max - count)) # sys模塊就在這用了一次,為了不讓他換行。。。 25 count += 1
n就是章節的序列,直接for循環就可以把所有章節寫成文件了
這里處理503的方法雖然很暴力,可是是最有用的!
四、整理代碼,修修bug。
把上面的思路寫成三道四個函數打包一下。
然后測試一下,看看有什么bug,能修就修復,修復不了就直接try掉。(哈哈哈)
想要文件的可以研究研究os模塊,很簡單,這里不贅述了。
最后附上完整代碼!
1 import requests 2 import bs4 # 爬網站必備兩個模塊不解釋 3 import os # 用來創建文件夾的 4 import sys # 沒啥用單純為了好看 5 import time 6 import random # 使用隨機數設置延時 7 headers = { 8 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36", 9 "Cookie": "_abcde_qweasd=0; Hm_lvt_169609146ffe5972484b0957bd1b46d6=1583122664; bdshare_firstime=1583122664212; Hm_lpvt_169609146ffe5972484b0957bd1b46d6=1583145548", 10 "Host": "www.xbiquge.la"} # 設置頭盡量多一點 以防萬一 11 b_n = "" 12 def get_title_url(): 13 x = str(input("輸入書名或作者名:")) 14 data = {'searchkey': x} 15 url = 'http://www.xbiquge.la/modules/article/waps.php' 16 global headers, b_n 17 r = requests.post(url, data=data, headers=headers) 18 soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser") 19 book_author = soup.find_all("td", class_="even") 20 books = [] # 書名 21 authors = [] # 作者名 22 directory = [] # 目錄鏈接 23 tem = 1 24 for each in book_author: 25 if tem == 1: 26 books.append(each.text) 27 tem -= 1 28 directory.append(each.a.get("href")) 29 else: 30 authors.append(each.text) 31 tem += 1 32 print('搜索結果:') 33 for num,book, author in zip(range(1, len(books)+1),books, authors): 34 print((str(num)+": ").ljust(4)+(book+"\t").ljust(25) + ("\t作者:" + author).ljust(20)) 35 search = dict(zip(books, directory)) 36 if books == []: 37 print("沒有找到任何一本書,請重新輸入!") 38 get_title_url() 39 try: 40 i = int(input("輸入需要下載的序列號(重新搜索輸入'0')")) 41 except: 42 print("輸入錯誤重新輸入:") 43 i = int(input("輸入需要下載的序列號(重新搜索輸入'0')")) 44 if i == 0: 45 books = [] 46 authors = [] 47 directory = [] 48 get_title_url() 49 if i>len(books) or i<0: 50 print("輸入錯誤重新輸入:") 51 i = int(input("輸入需要下載的序列號(重新搜索輸入'0')")) 52 b_n=books[i-1] 53 try: 54 os.mkdir(books[i-1]) 55 os.chdir(b_n) 56 except: 57 os.chdir(b_n) 58 b_n = books[i - 1] 59 return search[books[i-1]] 60 61 def get_text_url(titel_url): 62 url = titel_url 63 global headers 64 r = requests.get(url, headers=headers) 65 soup = bs4.BeautifulSoup(r.text.encode('ISO-8859-1'), "html.parser") 66 titles = soup.find_all("dd") 67 texts = [] 68 names = [] 69 texts_names = [] 70 for each in titles: 71 texts.append("http://www.xbiquge.la"+each.a["href"]) 72 names.append(each.a.text) 73 texts_names.append(texts) 74 texts_names.append(names) 75 return texts_names # 注意這里的返回值是一個包含兩個列表的列表!! 76 77 78 def readnovel(texts_url): 79 global headers,b_n 80 count=1 81 max=len(texts_url[1]) 82 print("預計耗時{}分鍾".format((max // 60)+1)) 83 tishi = input(str(b_n)+"一共{}章,確認下載輸入'y',輸入其他鍵取消".format(max)) 84 if tishi == "y"or tishi =="Y": 85 for n in range(max): 86 url = texts_url[0][n] 87 name = texts_url[1][n] 88 req = requests.get(url=url,headers=headers) 89 time.sleep(random.uniform(0, 0.5)) # 即使設置了延遲,他還有會可能503(沒辦法小網站) 90 req.encoding = 'UTF-8' # 這里的編碼是UTF-8,跟目錄不一樣,要注意! 91 html = req.text 92 soup = bs4.BeautifulSoup(html, features="html.parser") 93 texts = soup.find_all("div", id="content") 94 while (len(texts) == 0): # 他如果503的話,讀取內容就什么都木有,那直接讓他再讀一次,直到讀出來為止。 95 req = requests.get(url=url, headers=headers) 96 time.sleep(random.uniform(0,0.5)) 97 req.encoding = 'UTF-8' 98 html = req.text 99 soup = bs4.BeautifulSoup(html, features="html.parser") 100 texts = soup.find_all("div", id="content") 101 else: 102 content = texts[0].text.replace('\xa0' * 8, '\n\n') 103 content=content.replace("親,點擊進去,給個好評唄,分數越高更新越快,據說給新筆趣閣打滿分的最后都找到了漂亮的老婆哦!手機站全新改版升級地址:http://m.xbiquge.la,數據和書簽與電腦站同步,無廣告清新閱讀!","\n") 104 # 使用text屬性,提取文本內容,濾除br標簽,隨后使用replace方法,去掉八個空格符號,並用回車代替 再去除每一頁都有得結尾 105 with open(name+'.txt',"w",encoding='utf-8')as f: 106 f.write(content) 107 sys.stdout.write("\r已下載{}章,還剩下{}章".format(count,max-count)) # sys模塊就在這用了一次,為了不讓他換行。。。 108 count += 1 109 print("\n全部下載完畢") 110 else: 111 print("已取消!") 112 os.chdir('..') 113 os.rmdir(b_n) 114 main() 115 116 def main(): 117 titel_url = get_title_url() 118 texts_url = get_text_url(titel_url) 119 readnovel(texts_url) 120 input("輸入任意鍵退出") 121 122 123 if __name__ == '__main__': 124 print("小說資源全部來自於'新筆趣閣'---》http://www.xbiquge.la\n所以搜不到我也沒辦法..........@曉軒\n為了確保下載完整,每章設置了0.5秒到1秒延時!") 125 main()