python從爬蟲基礎到爬取網路小說例項
一.爬蟲基礎
1.1 requests類
1.1.1 request的7個方法
requests.request() 例項化一個物件,擁有以下方法
requests.get(url, *args)
requests.head() 頭資訊
requests.post()
requests.put()
requests.patch() 修改一部分內容
requests.delete()
url = "http://quanben5.com/n/doupocangqiong/6.html" headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"
} r = requests.get(url, headers=headers) data = { "pinyin": "doupocangqiong", "content_id": "4", } r = requests.post(url, data=data, headers=headers)
1.1.2*arg裡面的引數
params 字典或者位元組序列,作為引數增加到url中
data 字典位元組序列檔案物件, 放在url裡面對應的地方
json 作為requests的內容
headers 字典 模擬服務頭
cookies 字典 cookieJar
auth 元組
files 字典型別,傳輸檔案
timeout 設定的超時時間
proxies 字典型別,設定訪問代理伺服器,可以增加登入認證 pxs={"http":"http://user:[email protected]"}
allow_redirects 預設True 是否允許重定向
stream 預設TRUE 獲得資料立刻下載
verity 預設True 認證SSL證書開關
cert 本地SSL證書路徑
1.2 BeautifulSoup類
soup = BeautifulSoup("","html.parser")
soup.prettify()
soup.find_all(name, attrs, recursive, string, **kwargs)
name: 標籤名稱
attrs: 屬性
string: 檢索字串
soup.head
soup.head.contents 列表
soup.body.contents[1]
soup.body.children
soup.body.descendants
.parent
.parents
.next_sibling 下一個
.previous_sibling 上一個
.next_siblings 下一個所有 迭代
.previous_siblings 上一個所有
二.實戰
首先要找到可以線上看小說的網頁
這裡我隨便百度了一下,首先選擇了一個全本5200小說網("https://www.qb5200.tw")
開啟某小說章節目錄表
https://www.qb5200.tw/xiaoshuo/0/357/
檢視原始碼
發現正文卷是在class為listmain的div下面的第二個dt標籤裡
之後的路徑標籤為a
1 url_text = soup.find_all('div', 'listmain')[0] # 找到第一個class=listmain的div標籤 2 main_text = url_text.find_all('dt')[1] # 找到下面的第二個dt標籤 3 for tag in main_text.next_siblings: 4 try: 5 url = ''.join(['https://', host, tag.a.attrs['href']]) 6 print('parsering', url) 7 except: 8 continue找到每個章節的url
在隨便開啟一章
檢視原始碼為:
1 def getHTMLText(url, headers={}): 2 """ 3 獲取網站原始碼 4 :param url: 5 :return: class response 6 """ 7 if headers != {}: 8 headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"} 9 10 # proxies = get_random_ip() 11 proxies = {} 12 try: 13 # print("start", url) 14 r = requests.get(url, proxies=proxies, headers=headers) 15 r.raise_for_status() 16 # r.encoding = r.apparent_encoding 17 # print('end', url) 18 return r 19 except: 20 return r.status_code 21 22 def parseByQB5200(soup, host, f): 23 """ 24 在全本小說網的原始碼下爬取小說 25 :param soup, host: 26 :param f: 27 :return: 28 """ 29 url_text = soup.find_all('div', 'listmain')[0] 30 main_text = url_text.find_all('dt')[1] 31 x = 0 32 for tag in main_text.next_siblings: 33 time.sleep(1) 34 try: 35 url = ''.join(['https://', host, tag.a.attrs['href']]) 36 print('parsering', url) 37 soup = BeautifulSoup(getHTMLText(url).text, "html.parser") 38 passage = soup.find_all("div", "content")[0] 39 title = passage.h1.string 40 f.writelines(''.join([title, '\n'])) 41 passage = soup.find_all("div", "showtxt")[0] 42 for i in passage.descendants: 43 if i.name != "br": 44 st = i.string 45 if st.strip().startswith('http') or st.strip().startswith('請記住本書'): 46 continue 47 f.writelines(''.join([' ', st.strip(), '\n'])) 48 x += 1 49 print('%d.%s 下載完成' % (x, title)) 50 except: 51 continue 52 53 def getNovelUrls(url): 54 """ 55 通過小說的目錄網址判斷小說所在的網站 56 並呼叫屬於該網站的爬蟲語句 57 :param url: 58 :return: 59 """ 60 61 response = getHTMLText(url) 62 host = url.split('//')[1].split('/')[0] 63 host_list = { 64 "www.qb5200.tw": parseByQB5200, 65 # "www.qu.la": parseByQuLa, 66 "quanben5.com": parseByQB5 67 } 68 print(response) 69 soup = BeautifulSoup(response.text, 'html.parser') 70 with open('1.txt', 'w', encoding='utf8') as f: 71 host_list[host](soup, host, f) 72 73 if __name__ == '__main__': 74 getNovelUrls("https://www.qb5200.tw/xiaoshuo/0/357/")在全本5200爬取小說txt
問題在於
全本小說網("www.qb5200.tw")只允許爬3次,之後就403錯誤
解決方法暫時未知
因此換了一個網站
全本5小說網("quanben5.com")
同樣的方法搜尋原始碼
然而發現了問題
給出的html頁面只有一半的原始碼
因此按F12開啟檢查
發現所有的文字存在這個xhr裡
點選檢視請求頭資訊
發現是post請求
請求的url是/index.php?c=book&a=ajax_content
請求的資料在最下面的form表單裡
開啟網頁原始檔和js檔案,搜尋這些表單資訊
分別在ajax.js裡和原始檔裡找到了這些
原始檔裡面的可以直接生成data資料表單
在ajax.js裡可以知道rndval欄位是當前時間,精確到毫秒
優化
採用了gvent進行非同步IO處理,每一張網頁儲存在temp裡面,最後將檔案合成一個txt
程式碼如下:
1 #!/usr/bin/env python 2 # encoding: utf-8 3 4 """ 5 @version: 6 @author: Wish Chen 7 @contact: [email protected] 8 @file: get_novels.py 9 @time: 2018/11/21 19:43 10 11 """ 12 import gevent 13 from gevent import monkey 14 monkey.patch_all() 15 import requests, time, random, re, os 16 from bs4 import BeautifulSoup 17 18 dir = os.path.dirname(os.path.abspath(__file__)) 19 20 def getHTMLText(url, data=[], method='get'): 21 """ 22 獲取網站的原始碼 23 請求預設為get 24 :param url: 25 :param data: 26 :param method: 27 :return: 28 """ 29 headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36"} 30 proxies = {} 31 try: 32 # print("start", url) 33 r = requests.request(method, url, proxies=proxies, headers=headers, data=data) 34 r.raise_for_status() 35 # r.encoding = r.apparent_encoding 36 # print('end', url) 37 return r 38 except: 39 return r.status_code 40 41 42 def fetch_async(x, url): 43 """ 44 非同步IO所需要執行的操作 45 獲取原始檔 46 模擬向ajax請求獲取完整文字 47 每一章輸入到temp資料夾下 48 :param x: 49 :param url: 50 :return: 51 """ 52 url_main = "http://quanben5.com/index.php?c=book&a=ajax_content" 53 r = getHTMLText(url) # 獲取每一章的原始檔 54 title = re.search(r'<h1 class="title1">(.*)</h1>', r.text).group(1) 55 result = re.search(r'<script type="text/javascript">ajax_post\((.*)\)</script>', r.text).group(1) 56 num_list = result.split("','") 57 num_list[9] = num_list[9][:-1] 58 content = {} # 開始模擬post請求傳送的表單 59 for i in range(1, 5): 60 content[num_list[i * 2]] = num_list[i * 2 + 1] 61 content['_type'] = "ajax" 62 content['rndval'] = int(time.time() * 1000) 63 r = getHTMLText(url_main, data=content, method='post') # 模擬post請求 64 soup = BeautifulSoup(r.text, "lxml") 65 with open(os.path.join(dir, 'temp', "%s.txt" % x), "w") as f: 66 f.writelines(''.join([str(x), '.', title, '\n\n'])) 67 for tag in soup.body.children: 68 if tag.name == 'p': 69 f.writelines(''.join([' ', tag.string.strip(), '\n\n'])) 70 print('%d.%s 下載完成' % (x, title)) 71 72 73 74 def get_together(name, author): 75 """ 76 將temp目錄下的各網頁下載下來的txt 77 合併在一起 78 並刪除temp檔案 79 :param name: 80 :param author: 81 :return: 82 """ 83 with open(os.path.join(dir, "%s.txt" % name), "w") as f: 84 f.writelines(''.join([name, '\n\n作者:', author, '\n\n'])) 85 for i in range(len(os.listdir(os.path.join(dir, 'temp')))): 86 f.write(open(os.path.join(dir, 'temp', "%s.txt" % (i+1)), "r").read()) 87 f.write('\n\n') 88 # os.remove(os.path.join(dir, 'temp', "%s.txt" % (i+1))) 89 90 91 def parseByQB5(response, host): 92 """ 93 在全本5小說網的原始碼下爬取小說 94 獲得書名和作者 95 採用gevent非同步IO優化 96 :param response: 97 :param host: 98 :return: 99 """ 100 soup = BeautifulSoup(response.text, 'html.parser') 101 url_text = soup.find_all('div', 'box')[2] 102 main_text = url_text.find_all('h2')[0].next_sibling 103 url_list = [] 104 for tag in main_text.descendants: 105 if tag.name == 'li': 106 url = ''.join(['http://', host, tag.a.attrs['href']]) 107 url_list.append(url) 108 from gevent.pool import Pool 109 pool = Pool(20) 110 gevent.joinall([pool.spawn(fetch_async, i+1, url=url_list[i]) for i in range(len(url_list))]) 111 name = re.search(r"<h3><span>(.*)</span></h3>", response.text).group(1) 112 author = re.search(r'<span class="author">(.*)</span></p>', response.text).group(1) 113 get_together(name, author) 114 115 116 117 118 def getNovelUrls(url): 119 """ 120 通過小說的目錄網址判斷小說所在的網站 121 並呼叫屬於該網站的爬蟲語句 122 :param url: 123 :return: 124 """ 125 126 response = getHTMLText(url) 127 host = url.split('//')[1].split('/')[0] 128 host_list = { 129 "quanben5.com": parseByQB5 130 } 131 host_list[host](response, host) 132 133 134 if __name__ == '__main__': 135 url_list = [ 136 "https://www.qu.la/book/365/", 137 "https://www.qb5200.tw/xiaoshuo/0/357/", 138 "http://quanben5.com/n/doupocangqiong/xiaoshuo.html", 139 "http://quanben5.com/n/dazhuzai/xiaoshuo.html", 140 "http://quanben5.com/n/douluodalu/xiaoshuo.html", 141 "http://quanben5.com/n/renxingderuodian/xiaoshuo.html" 142 ] 143 if not os.path.exists('temp'): 144 os.mkdir('temp') 145 getNovelUrls(url_list[5])非同步IO優化
封裝性還不夠好
最好一個網站用一個類來封裝
採用scrapy框架
正在設計中...
未解決問題:
代理IP問題:
使用的是免費的代理
還是無法解決只能下載3個頁面之後報錯503的問題
搜尋功能:
模擬搜尋介面提交請求,在返回頁中列出所有小說的名字和作者以及簡介
可以簡單實現
多網站定製
要觀察各個網站的目錄原始碼結構以及文章原始碼結構
每一個網站都可以用一個parse函式來解析其內容