Re解析爬蟲響應(yīng)數(shù)據(jù)
需求:爬取起點(diǎn)小說網(wǎng)站中某一本小說的免費(fèi)章節(jié),包括章節(jié)的標(biāo)題和內(nèi)容。
主要分為兩步:
1.獲取每一章節(jié)的標(biāo)題和對應(yīng)內(nèi)容詳情頁的請求URL
2.獲取每一章節(jié)內(nèi)容詳情頁的章節(jié)內(nèi)容
??!注意:我們獲取到的網(wǎng)頁響應(yīng)數(shù)據(jù),可能會(huì)與網(wǎng)頁源代碼中呈現(xiàn)的格式不同。因?yàn)橛行┚W(wǎng)頁文件是用JavaScript加載的,瀏覽器會(huì)自動(dòng)將其解析成html文檔格式,而我們獲取到的內(nèi)容是JavaScript格式的文檔。所以獲取到響應(yīng)數(shù)據(jù)之后先要查看內(nèi)容是否與網(wǎng)頁源碼中的一致,不一致的話,在編寫正則表達(dá)式時(shí)則以獲取到的響應(yīng)數(shù)據(jù)res.text為準(zhǔn),否則會(huì)找不到對應(yīng)數(shù)據(jù)。
一、爬取小說的標(biāo)題和章節(jié)內(nèi)容頁的鏈接
在起點(diǎn)小說網(wǎng)(https://www.qidian.com/all/)打開一篇小說,發(fā)現(xiàn)只有免費(fèi)章節(jié)的內(nèi)容是完整的,收費(fèi)章節(jié)非VIP只會(huì)顯示部分內(nèi)容。所以我們只爬取免費(fèi)的章節(jié)。
1.1 指定URL
打開網(wǎng)頁源碼,查看網(wǎng)頁的URL,請求方法為GET,文本類型是text/html。
我們想要獲取的是119章免費(fèi)章節(jié)的標(biāo)題和章節(jié)內(nèi)容頁的URL。
定位發(fā)現(xiàn)所有的章節(jié)標(biāo)題和章節(jié)內(nèi)容頁鏈接都在<div class=“catalog-volume” 標(biāo)簽中,所以我們可以嘗試定位到該標(biāo)簽中,看能不能取到所有的章節(jié)標(biāo)題和章節(jié)內(nèi)容頁鏈接。
1.2 發(fā)起網(wǎng)頁請求,獲取響應(yīng)
接下來開始請求網(wǎng)頁:
# 導(dǎo)包
import re
import requests
# 指定url
url = 'https://www.qidian.com/book/1016530091/'
headers = {
'Host':'www.qidian.com',
'Referer':'https://www.qidian.com/all/',
'Sec-Ch-Ua':'"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
'Sec-Ch-Ua-Mobile':'?0',
'Sec-Ch-Ua-Platform':'"Windows"',
'Sec-Fetch-Dest':'document',
'Sec-Fetch-Mode':'navigate',
'Sec-Fetch-Site':'same-origin',
'Sec-Fetch-User':'?1',
'Upgrade-Insecure-Requests':'1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31',}
response = requests.get(url,headers=headers)
print(response.encoding) # 查看編碼格式
print(response.status_code) # 查看響應(yīng)狀態(tài)碼
print(response.text) # 打印響應(yīng)內(nèi)容
page_text = response.text
通過對比可以發(fā)現(xiàn),網(wǎng)頁響應(yīng)的內(nèi)容response.text與網(wǎng)頁源碼中的內(nèi)容是一樣的,所有的文章標(biāo)題都在<ul class=“volume-chapters” 中,我們可以先定位到該標(biāo)簽,再進(jìn)一步定位。
1.3 進(jìn)行數(shù)據(jù)解析
下面開始編寫正則表達(dá)式并查找對應(yīng)的內(nèi)容:
(1)獲取小說名稱
在網(wǎng)頁源碼中定位到書名,發(fā)現(xiàn)其在<h1 class="" id="bookName">赤心巡天</h1>
中。在網(wǎng)頁源碼的搜索框查找<h1 class="" id="bookName">
標(biāo)簽,發(fā)現(xiàn)只有一個(gè)結(jié)果,所以我們可以直接定位到該標(biāo)簽中,獲取文本。
# 獲取小說名稱
ex = '<h1 class="" id="bookName">(.*?)</h1>' # !!注意,要將元素復(fù)制出來,查看標(biāo)簽、關(guān)鍵字等之間有無空格或未顯示的東西。如:class=""并未在html文檔中顯示出來
bookname = re.findall(ex,page_text)
print(bookname)
!!注意,要將元素復(fù)制出來,查看標(biāo)簽、關(guān)鍵字等之間有無空格或未顯示的東西。如:class=""并未在html文檔中顯示出來。
輸出結(jié)果是:(獲取到書名)
(2)獲取章節(jié)標(biāo)題
我們從<ul class="volume-chapters>"
開始查找匹配它的每一個(gè)子標(biāo)簽
# 獲取每一章節(jié)的標(biāo)題
ex1 = '<ul class="volume-chapters"><li class="chapter-item".*?<a class="chapter-name".*?章節(jié)字?jǐn)?shù):\d{4}">(.*?)</a>.*?</ul>' # 正則表達(dá)式
titles = re.findall(ex1,page_text) # 查找所有符合條件的內(nèi)容
print(titles)
結(jié)果是:發(fā)現(xiàn)并沒有獲取到內(nèi)容。
接下來,我們嘗試擴(kuò)大查找范圍,即從<ul class="volume-chapters>
的下一級標(biāo)簽開始查找匹配。
# 獲取每一章節(jié)的標(biāo)題
ex1 = '<li class="chapter-item".*?<a class="chapter-name".*?章節(jié)字?jǐn)?shù):\d{4}">(.*?)</a>.*?</li>' # 正則表達(dá)式
titles = re.findall(ex1,page_text)
print(titles)
結(jié)果是:
取到了所有的章節(jié)標(biāo)題并存放在一個(gè)列表中。
但是我們只需要免費(fèi)章節(jié)的內(nèi)容,即從“赤心巡天世界地圖設(shè)定文稿”到“第一百一十八章 白骨道子”,需要對列表進(jìn)行切片。
detail_titles = titles[:120] # 只取免費(fèi)章節(jié),即前118章的標(biāo)題
print(detail_titles)
輸出結(jié)果是:(獲取到所有免費(fèi)章節(jié)的標(biāo)題)
(3)獲取章節(jié)內(nèi)容頁的鏈接
章節(jié)內(nèi)容頁的鏈接和章節(jié)標(biāo)題在同一個(gè)標(biāo)簽中,只是鏈接是標(biāo)簽屬性的值,標(biāo)題是標(biāo)簽的文本內(nèi)容,因此我們可以在正則表達(dá)式ex1讓進(jìn)行修改。
編寫正則表達(dá)式,并查找匹配響應(yīng)內(nèi)容。
ex2 = '<li class="chapter-item".*?<a class="chapter-name"\shref="(.*?)"\starget=.*?</li>' # 取到所有章節(jié)的url,同樣我們只要免費(fèi)章節(jié)的鏈接
links = re.findall(ex2,page_text) # 獲取每一章詳情頁的部分鏈接
print(links)
結(jié)果是:(獲取到了所有章節(jié)的鏈接并存放到列表中)
點(diǎn)擊查看確實(shí)是對應(yīng)章節(jié)頁面的鏈接。
我們需要從中截取需要的鏈接,并將其拼接成完整的章節(jié)內(nèi)容頁的請求URL。
detail_links = links[:120]
print(detail_links)
print(len(detail_links))
輸出結(jié)果是:
二、爬取小說章節(jié)內(nèi)容頁的內(nèi)容
我們需要先獲取某一章節(jié)的內(nèi)容,然后通過循環(huán)對每一章節(jié)內(nèi)容頁發(fā)起請求,獲取全部章節(jié)內(nèi)容。
2.1爬取某一章節(jié)的內(nèi)容
以爬取第一章的內(nèi)容為例
打開網(wǎng)頁源碼,在NetWork下可以發(fā)現(xiàn)其請求方法為get方法,文本類型為text/html,編碼格式為utf-8。所以我們獲得的響應(yīng)的文本數(shù)據(jù)是html文檔,編碼格式是utf-8,我們需要對其進(jìn)行數(shù)據(jù)解析。這里使用re進(jìn)行數(shù)據(jù)解析。
我們之前已經(jīng)獲取了全部章節(jié)內(nèi)容頁的URL,然后需要進(jìn)行UA偽裝,準(zhǔn)備好GET請求所需的參數(shù)之后,嘗試對網(wǎng)頁發(fā)起請求。響應(yīng)狀態(tài)碼為200就說明請求成功,獲取到響應(yīng)。然后打印響應(yīng)的內(nèi)容,res.text。(建議每次發(fā)起請求后都輸出狀態(tài)碼查看是否請求成功)
# 指定url
url = detail_urls[1] # 以爬取第一章為例
print(url)
# UA偽裝
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31',}
# 發(fā)起請求,獲取響應(yīng)
res = requests.get(url,headers=header)
page_text = res.text
print(res.status_code) # 建議在每一次請求之后輸出響應(yīng)的狀態(tài)碼,這樣可以知道我們是否請求成功
print(res.encoding)
print(res.text)
獲取到響應(yīng)數(shù)據(jù)res.text之后可以發(fā)現(xiàn),其與網(wǎng)頁源碼的格式不同,說明該網(wǎng)頁文件是由JavaScript加載的。所以要根據(jù)res.text的格式來編寫正則表達(dá)式。
打開網(wǎng)頁源碼,選擇所有的文本內(nèi)容,可以發(fā)現(xiàn),該章節(jié)全部內(nèi)容都在<main標(biāo)簽下,在源代碼中按下ctrl+F查找<main,只有一個(gè)結(jié)果,所以我們只要定位到main標(biāo)簽就可以找出全部的章節(jié)內(nèi)容。
由于網(wǎng)頁是由JavaScript文件加載的,所以我們在res.text中查找是否有<main標(biāo)簽,通過對比可以發(fā)現(xiàn),網(wǎng)頁源碼中p標(biāo)簽下還有子標(biāo)簽span,而網(wǎng)頁響應(yīng)數(shù)據(jù)的p標(biāo)簽下并沒有子標(biāo)簽。我們需要根據(jù)網(wǎng)頁響應(yīng)內(nèi)容編寫正則表達(dá)式。
其中文本的內(nèi)容如下:
#<p> 太陽懸在高天,將它的光和熱,不偏不倚灑落人間。不分老幼,不辨貴賤。大愛如無情。</p>
# <p> 入此地牢者,一息呼氣凝霜,二息血流凍結(jié),三息肉身僵死。</p>
編寫正則表達(dá)式獲取<main標(biāo)簽下所有子標(biāo)簽p中的文本內(nèi)容。
ex = '<main id=.*?<p>(.*?)</p>.*?</main>' # 正則表達(dá)式
content = re.findall(ex,page_text) 查找所有符合要求的內(nèi)容
print(content)
輸出后得到的結(jié)果是:
只取到了第一個(gè)p標(biāo)簽中的文本內(nèi)容,而且文本前出現(xiàn)了\u3000,是空格的ASCII碼,我們需要將文本前面的內(nèi)容去掉,并且減少正則表達(dá)式中的標(biāo)簽數(shù),擴(kuò)大查找范圍,以獲取更多文本。
去除文本前面的空格:
# \s表示匹配空格,+表示數(shù)量大于等于1
ex = '<main id=.*?<p>\s+(.*?)</p>.*?</main>' # 只能取到一個(gè)p標(biāo)簽下的文本
輸出的結(jié)果是:(已經(jīng)去除掉文本前面的空格)
接下來嘗試減少正則表達(dá)式中的標(biāo)簽數(shù),看看能不能獲取到更多內(nèi)容。
ex = '<p>\s+(.*?)</p>' # 擴(kuò)大范圍,取到所有文本內(nèi)容
content = re.findall(ex,page_text)
print(content)
輸出結(jié)果:(獲取到該章節(jié)的全部文本內(nèi)容并存儲(chǔ)到列表中)
2.2 通過循環(huán)獲取每個(gè)章節(jié)中的內(nèi)容
我們在上節(jié)中已經(jīng)獲取到了某個(gè)章節(jié)的全部文本內(nèi)容,想要獲取全部章節(jié)的內(nèi)容,可以通過for循環(huán)依次對每一章節(jié)發(fā)起請求,獲取響應(yīng),然后解析出文本內(nèi)容。
需要注意的是,在上一節(jié)中我們獲取的某一章節(jié)的內(nèi)容被存儲(chǔ)到列表中,如果我們想將內(nèi)容保存到本地,就需要將其轉(zhuǎn)換成字符類型,再寫入txt文檔中。
content_string = '' # 創(chuàng)建一個(gè)空字符串,用于存放章節(jié)內(nèi)容
for j in range(len(content)): # 循環(huán)遍歷列表
# 字符串拼接
content_string = content_string + content[j]
通過循環(huán)爬取全部章節(jié)內(nèi)容的代碼如下:
# 創(chuàng)建文件,并將小說每一章節(jié)標(biāo)題和內(nèi)容寫入
fp = open(f'D:\\Python\\{bookname}.txt','w',encoding='utf-8')
# 獲取每一章節(jié)詳情頁的小說內(nèi)容
for i in range(len(detail_titles)):
detail_url = detail_urls[i] # 依次取每個(gè)章節(jié)內(nèi)容的URL
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31',}
# 發(fā)起請求
res = requests.get(detail_url,headers=header)
detail_text=res.text
detail_ex = '<p>\s+(.*?)</p>' # 編寫正則表達(dá)式
content = re.findall(detail_ex,detail_text) # 內(nèi)容存儲(chǔ)在列表中,需要把它逐個(gè)取出并寫入
content_string = '' # 創(chuàng)建一個(gè)空字符串,用于存放章節(jié)內(nèi)容
for j in range(len(content)):
content_string = content_string + content[j]
# 將每一章節(jié)的標(biāo)題和內(nèi)容存儲(chǔ)到txt文件中
fp.write(detail_titles[i]+'\n'+content_string+'\n')
print(f"{detail_titles[i]}爬取結(jié)束!")
# 關(guān)閉文件
fp.close()
爬取的文件保存到txt文件中:文章來源:http://www.zghlxwxcb.cn/news/detail-763877.html
整個(gè)程序的代碼如下:
'''
爬取起點(diǎn)小說網(wǎng)的某篇小說,用re解析網(wǎng)頁數(shù)據(jù),只要免費(fèi)章節(jié)即1-118章節(jié)
'''
# 導(dǎo)包
import re
import requests
# 指定url
url = 'https://www.qidian.com/book/1016530091/'
# UA偽裝
headers = {
'Host':'www.qidian.com',
'Referer':'https://www.qidian.com/all/',
'Sec-Ch-Ua':'"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117"',
'Sec-Ch-Ua-Mobile':'?0',
'Sec-Ch-Ua-Platform':'"Windows"',
'Sec-Fetch-Dest':'document',
'Sec-Fetch-Mode':'navigate',
'Sec-Fetch-Site':'same-origin',
'Sec-Fetch-User':'?1',
'Upgrade-Insecure-Requests':'1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31',}
# 發(fā)起請求,獲取響應(yīng)
response = requests.get(url,headers=headers)
print(response.encoding) # 查看編碼格式
print(response.status_code) # 查看狀態(tài)碼
# print(response.text)
page_text = response.text # 獲取響應(yīng)內(nèi)容
# 獲取小說名稱
ex = '<h1 class="" id="bookName">(.*?)</h1>' # !!注意,要將元素復(fù)制出來,查看標(biāo)簽、關(guān)鍵字等之間有無空格或未顯示的東西。如:class=""并未在html文檔中顯示出來
bookname = re.findall(ex,page_text)
print(bookname)
# 獲取每一章節(jié)的標(biāo)題
# ex1 = '<ul class="volume-chapters"><li class="chapter-item".*?<a class="chapter-name".*?章節(jié)字?jǐn)?shù):\d{4}">(.*?)</a>.*?</ul>'
# ex1 = '<ul class="volume-chapters><li class="chapter-item".*?<a class="chapter-name".*?alt="(.*?)"\stitle=.*?</ul>'
# ex1 = '<li class="chapter-item".*?<a class="chapter-name".*?alt="(.*?)"\stitle=.*?</li>'
# 正則表達(dá)式
ex1 = '<li class="chapter-item".*?<a class="chapter-name".*?章節(jié)字?jǐn)?shù):\d{4}">(.*?)</a>.*?</li>'
titles = re.findall(ex1,page_text)
# print(titles)
detail_titles = titles[:120] # 只取免費(fèi)章節(jié),即前118章的標(biāo)題
print(detail_titles)
#獲取每一章詳情頁的鏈接
# ex2 = '<ul class="volume-chapters">\s+<li class="chapter-item".*?<a class="chapter-name"\shref="(.*?)"\starget=.*?</li>.*?</ul>' # 只能取到每一卷第一章的url
# ex2 = '<a class="chapter-name"\shref="(.*?)"\starget=.*?</a>' # !?。。∽⒁猓嚎崭褚欢ㄒ肻s來匹配,否則取不到值
ex2 = '<li class="chapter-item".*?<a class="chapter-name"\shref="(.*?)"\starget=.*?</li>' # 取到所有章節(jié)的url,同樣我們只要免費(fèi)章節(jié)的鏈接
links = re.findall(ex2,page_text) # 獲取每一章詳情頁的部分鏈接
detail_links = links[:120]
print(detail_links)
print(len(detail_links))
# 拼接出每一章節(jié)詳情頁面的url
detail_urls = []
for link in detail_links:
detail_urls.append('https:'+link)
print(detail_urls)
# 創(chuàng)建文件,并將小說每一章節(jié)標(biāo)題和內(nèi)容寫入
fp = open(f'D:\\Python\\{bookname}.txt','w',encoding='utf-8')
# 獲取每一章節(jié)詳情頁的小說內(nèi)容
for i in range(len(detail_titles)):
detail_url = detail_urls[i]
header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31',}
# 發(fā)起請求
res = requests.get(detail_url,headers=header)
detail_text=res.text
detail_ex = '<p>\s+(.*?)</p>' # 編寫正則表達(dá)式
content = re.findall(detail_ex,detail_text) # 內(nèi)容存儲(chǔ)在列表中,需要把它逐個(gè)取出并寫入
content_string = '' # 創(chuàng)建一個(gè)空字符串,用于存放章節(jié)內(nèi)容
for j in range(len(content)):
content_string = content_string + content[j]
fp.write(detail_titles[i]+'\n'+content_string+'\n')
print(f"{detail_titles[i]}爬取結(jié)束!")
# 關(guān)閉文件
fp.close()
總結(jié):
1、需要注意響應(yīng)內(nèi)容是否與網(wǎng)頁源碼格式相同
2、編寫正則表達(dá)式時(shí)需要將網(wǎng)頁源碼或者響應(yīng)內(nèi)容中的對應(yīng)元素復(fù)制出來,觀察其格式,按照格式去編寫正則表達(dá)式
3、如果我們查找不到對應(yīng)的內(nèi)容,或者只取到對應(yīng)內(nèi)容的一部分,則我們需要擴(kuò)大查找范圍,正則表達(dá)式的編寫從開始標(biāo)簽的下級標(biāo)簽開始查找。
4、建議每次獲取到數(shù)據(jù)都輸出查看是否是我們想要的格式和內(nèi)容。文章來源地址http://www.zghlxwxcb.cn/news/detail-763877.html
到了這里,關(guān)于Python網(wǎng)頁爬蟲爬取起點(diǎn)小說——re解析網(wǎng)頁數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!