本節是自己改編的用正則表達式抓取數據案例,在第一篇結尾我們留下了兩個問題,學習了第二篇的正則表達式後,我們可以嘗試去解決了。
在獲取小說內容之前,我肯定得先從目錄拿到每一章的標題和鏈接
也就是要的數據在不在源代碼中。建議多往後翻幾頁看看,有些網頁源碼只是有第一頁的數據,後面的就沒有了(例如csdn)。如果是客戶端渲染,就不能用正則表達式來取。
這裡我看了幾頁,數據都是在源碼中的,那就比較簡單了。
首先寫個最簡單的print,獲取網頁信息,方便起見headers參數一並加上。
import requests
url= "https://m.gdedu.tv/dir/73358/?page=1&sort=asc"
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36"}
resp = requests.get(url=url,headers=headers)
print(resp.text)
resp.close()
我們要標題、鏈接
隨便點進去一章你會發現,上面herf的鏈接只是一部分,還要再拼上網站首頁的地址 https://m.gdedu.tv
匹配思路如下:
<li class="ptm-list-view-cell">
<a href="/73358/tc_729773.html">章節目錄</a>
</li>
<li class="ptm-list-view-cell">
<a href="/73358/tc_729774.html">一只小肥啾01</a>
</li>
簡單是很簡單,但你會發現不好把“章節目錄”跟真正的章節區分開,不過也沒關系,返回數據的時候再處理就可以。
注意:從起始字符串開始,每個值可能會有變化的地方都要匹配到
<a href="/73358/tc_729774.html">一只小肥啾01</a>
obj = re.compile(r'<a href="(?P<link>.*?)">(?P<title>.*?)<',re.S)
it = obj.finditer(html)
for i in it:
print(i.group("title")+'---'+i.group("link"))
跑一下試試:
發現雖然取到了,但有些多余的數據,其實是翻頁的鏈接。
這裡我們還是用標准的傳參方式翻頁,所以在輸出數據的時候過濾掉這些多余的。
it = obj.finditer(html)
for i in it:
if ("章節目錄" in i.group("title")) or ("Sitemap" in i.group("title")) or ("page" in i.group("link")):
pass
else:
print(i.group("title")+'---'+i.group("link"))
ok~
F12抓包,刷新頁面,可以看到它有兩個入參
但是這裡沒找到有總記錄數的數據,如果想要自動判斷一共多少頁的話,可能還是把上面page鏈接存起來會簡單點?
這裡我人工看了下,一共4頁,所以有幾個地方要改:
url= "https://m.gdedu.tv/dir/73358/"
for page in range(1,5):
params = {
"page": page,
"sort": "asc"
}
resp = requests.get(url=url,headers=headers,params=params)
...
dic["link"] = root_page+i.group("link")
測試點擊,可以打開
這兩個變量既然取到了,後面保存到文檔也好,保存到list做後續處理也好,就都OK了。
如果用的是一頁顯示本章全部內容的網站,其實上面的步驟就ok了,不需要這裡。
隨便打開一章,會發現每章裡面也是有分頁的,並且這個頁碼不像目錄的頁碼,抓包裡是沒有的。
但是很明顯這個頁數就在鏈接裡了,這種情況要怎麼判斷每章有多少頁,下頁鏈接是什麼呢?
其實同理,這個“下一章”也有自己對應的鏈接
查看源碼,搜729776,這玩意在JavaScript標簽裡。但是沒關系,只要你會匹配上面的超鏈接,你自然就會匹配下面這個。
這個原理跟拿每章鏈接是一樣的,傳入url,然後通過正則表達式匹配鏈接,再拼起來。
# 傳入每章鏈接,獲取本章的子鏈接
def get_next_url(chapter_url,headers):
# chapter_url = "https://m.gdedu.tv/73358/tc_818487.html"
resp = requests.get(url=chapter_url, headers=headers)
# 獲取各章源碼
html = resp.text
# 匹配下一頁鏈接
obj = re.compile(r'var nexturl = "(?P<nexturl>.*?)"', re.S) # re.S 使.也能匹配換行符
# 因為只會有一個nexturl,這裡用search就可以
s = obj.search(html)
# 拼好的下一頁鏈接
full_nexturl = root_page+s.group("nexturl")
return full_nexturl
下面這個方法是我自己想的,比較直接,如果有更好的方法歡迎指教。
上面那個函數不難,但它只能拿到一個url的下一頁,肯定得循環調用。但是我要怎麼判斷每章有幾個子頁面?這個不像是目錄頁數看一眼就知道,需要再觀察下頁面源碼。
找到章節最後一頁,看nexturl有什麼特點:var nexturl = "/73358/tc_818488.html"; (下一章鏈接)
最大的區別在於下一章的時候tc_後面的數字會+1,而不再是xxx_3,xxx_4 這種子頁面。
所以我們可以把當前鏈接的818487截取出來,再轉成int類型
# 例如 tc_818487.html 截出來是 818487
cur_id = int(chapter_url["link"].split("_")[1].split(".")[0])
然後還要截一個下一頁id
next_id = int(next_url.split("_")[1].split(".")[0])
代碼如下:
if __name__ == '__main__':
chapter_url_list = get_chapter_url()
cnt = 0
for chapter_url in chapter_url_list:
f = open("url_list.txt", 'a', encoding='utf-8')
if cnt == 0:
# print(chapter_url["link"])
f.write(chapter_url["link"]+'\n')
cnt = cnt+1
# 例如 tc_818487.html 截出來是 818487
cur_id = int(chapter_url["link"].split("_")[1].split(".")[0])
next_url = chapter_url["link"]
# 由於不知道每章子頁面一共有多少,這裡來個死循環
while True:
# 獲取下頁鏈接
next_url = get_next_url(next_url, headers=headers,)
#print(next_url)
f.write(next_url+'\n')
# 如果next_id已經是下一章id(cur_id+1),說明這已經是本章最後一頁,此時跳出while循環
next_id = int(next_url.split("_")[1].split(".")[0])
if next_id == cur_id+1:
break;
f.close()
這裡加個計數器(綠色部分)是為了打印第一章第一頁的信息,如果不加的話每章第一頁會重復打印出來,去掉這行輸出的話又會少一章。
4. 將鏈接寫入文件
主要原因是這個匹配量太大了,每次重新匹配效率會很低,所以直接保存起來。上面代碼藍色部分就是,這裡不重復寫了。
運行結果如下:
每一個章節和子頁面的目錄拿到手之後,下一節就可以獲取小說內容了。
完整代碼如下
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2022-06-12 15:17
# @Author: Hehuyi_In
# @File : get_url.py
import requests
import re
# 網站首頁url,用來拼章節鏈接
root_page = "https://m.gdedu.tv"
url= "https://m.gdedu.tv/dir/73358/"
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36"}
# 傳入每章鏈接,獲取本章的子鏈接
def get_next_url(chapter_url,headers):
# chapter_url = "https://m.gdedu.tv/73358/tc_818487.html"
resp = requests.get(url=chapter_url, headers=headers)
# 獲取各章源碼
html = resp.text
# 匹配下一頁鏈接
obj = re.compile(r'var nexturl = "(?P<nexturl>.*?)"', re.S) # re.S 使.也能匹配換行符
# 因為只會有一個nexturl,這裡用search就可以
s = obj.search(html)
# 拼好的下一頁鏈接
full_nexturl = root_page+s.group("nexturl")
return full_nexturl
def get_chapter_url():
chapter_url_list=[]
for page in range(1,5):
params = {
"page": page,
"sort": "asc"
}
resp = requests.get(url=url,headers=headers,params=params)
# 網頁源碼
html = resp.text
# 匹配各章節鏈接
obj = re.compile(r'<a href="(?P<link>.*?)">(?P<title>.*?)<',re.S) # re.S 使.也能匹配換行符
it = obj.finditer(html)
for i in it:
if ("章節目錄" in i.group("title")) or ("Sitemap" in i.group("title")) or ("page" in i.group("link")):
pass
else:
dic={}
dic["title"]=i.group("title")
dic["link"] = root_page+i.group("link")
chapter_url_list.append(dic)
resp.close()
return chapter_url_list
if __name__ == '__main__':
chapter_url_list = get_chapter_url()
cnt = 0
for chapter_url in chapter_url_list:
f = open("url_list.txt", 'a', encoding='utf-8')
if cnt == 0:
# print(chapter_url["link"])
f.write(chapter_url["link"]+'\n')
cnt = cnt+1
# 例如 tc_818487.html 截出來是 818487
cur_id = int(chapter_url["link"].split("_")[1].split(".")[0])
next_url = chapter_url["link"]
# 由於不知道每章子頁面一共有多少,這裡來個死循環
while True:
# 獲取下頁鏈接
next_url = get_next_url(next_url, headers=headers,)
#print(next_url)
f.write(next_url+'\n')
# 找到章節最後一頁,看有什麼特點:var nexturl = "/73358/tc_818488.html"; (下一章鏈接)
# 如果next_id已經是下一章id(cur_id+1),說明這已經是本章最後一頁,此時跳出while循環
next_id = int(next_url.split("_")[1].split(".")[0])
if next_id == cur_id+1:
break;
f.close()
參考:B站視頻 P23-P27
2021年最新Python爬蟲教程+實戰項目案例(最新錄制)_哔哩哔哩_bilibili