本文轉載自以下網站: Python For 和 While 循環爬取不確定頁數的網頁 https://www.makcyun.top/web_scraping_withpython16.html
需要學習的地方
有兩種方法。
第一種方式 使用 For 循環配合 break 語句,尾頁的頁數設置一個較大的參數,足夠循環爬完所有頁面,爬取完成時,break 跳出循環,結束爬取。
第二種方法 使用 While 循環,可以結合 break 語句,也可以設起始循環判斷條件為 True,從頭開始循環爬取直到爬完最后一頁,然后更改判斷條件為 False 跳出循環,結束爬取。
Requests 和 Scrapy 中分別用 For 循環和 While 循環爬取不確定頁數的網頁。
摘要:Requests 和 Scrapy 中分別用 For 循環和 While 循環爬取不確定頁數的網頁。
我們通常遇到的網站頁數展現形式有這么幾種:
第一種是直觀地顯示所有頁數,比如此前爬過的酷安、東方財富網,
文章見:
∞ Scrapy 爬取並分析酷安 6000 款 App,找到良心佳軟
第二種是不直觀顯示網頁總頁數,需要在后台才可以查看到,比如之前爬過的虎嗅網,文章見:
第三種是今天要說的,不知道具體有多少頁的網頁,比如豌豆莢:
對於,前兩種形式的網頁,爬取方法非常簡單,使用 For 循環從首頁爬到尾頁就行了,第三種形式則不適用,因為不知道尾頁的頁數,所以循環到哪一頁結束無法判斷。
那如何解決呢?有兩種方法。
第一種方式 使用 For 循環配合 break 語句,尾頁的頁數設置一個較大的參數,足夠循環爬完所有頁面,爬取完成時,break 跳出循環,結束爬取。
第二種方法 使用 While 循環,可以結合 break 語句,也可以設起始循環判斷條件為 True,從頭開始循環爬取直到爬完最后一頁,然后更改判斷條件為 False 跳出循環,結束爬取。
實際案例
下面,我們以 豌豆莢 網站中「視頻」類別下的 App 信息為例,使用上面兩種方法抓取該分類下的所有 App 信息,包括 App 名稱、評論、安裝數量和體積。
首先,簡要分析下網站,可以看到頁面是通過 Ajax 加載的,GET 請求附帶一些參數,可以使用 params 參數構造 URL 請求,但不知道一共有多少頁,為了確保下載完所有頁,設置較大的頁數,比如 100頁 甚至 1000 頁都行。
下面我們嘗試使用 For 和 While 循環爬取 。
Requests
▌For 循環
主要代碼如下:
class Get_page(): |
這里,首先創建了一個 Get_page 類,get_page 方法用於獲取 Response 返回的 json 數據,通過 json.cn 網站解析 json 解析后發現需要提取的內容是一段包裹在 data 字段下 content 鍵中的 html 文本,可以使用 parse_page 方法中的 pyquery 函數進行解析,最后提取出 App 名稱、評論、安裝數量和體積四項信息,完成抓取。
在主函數中,使用了 if 函數進行條件判斷,若 content 不為空,表示該頁有內容,則循環爬下去,若為空則表示此頁面已完成了爬取,執行 else 分支下的 break 語句結束循環,完成爬取。
爬取結果如下,可以看到該分類下一共完成了全部 41 頁的信息抓取。
▌While 循環
While 循環和 For 循環思路大致相同,不過有兩種寫法,一種仍然是結合 break 語句,一種則是更改判斷條件。
總體代碼不變,只需修改 For 循環部分:
page = 2 # 設置爬取起始頁數 |
或者:
page = 2 # 設置爬取起始頁數 |
結果如下,可以看到和 For 循環的結果是一樣的。
我們可以再測試一下其他類別下的網頁,比如選擇「K歌」類別,編碼為:718,然后只需要對應修改主函數中的child_cate_code 即可,再次運行程序,可以看到該類別下一共爬取了 32 頁。
由於 Scrapy 中的寫法和 Requests 稍有不同,所以接下來,我們在 Scrapy 中再次實現兩種循環的爬取方式 。
Scrapy
▌For 循環
Scrapy 中使用 For 循環遞歸爬取的思路非常簡單,即先批量生成所有請求的 URL,包括最后無效的 URL,后續在 parse 方法中添加 if 判斷過濾無效請求,然后爬取所有頁面。由於 Scrapy 依賴於Twisted框架,采用的是異步請求處理方式,也就是說 Scrapy 邊發送請求邊解析內容,所以這會發送很多無用請求。
def start_requests(self): |
下面,我們選取豌豆莢「新聞閱讀」分類下的「電子書」類 App 頁面信息,使用 For 循環嘗試爬取,主要代碼如下:
def start_requests(self): |
上面代碼很好理解,簡要說明幾點:
第一、判斷當前頁是否爬取完成的判斷條件改為了 response.body 的長度大於 100。
因為請求已爬取完成的頁面,返回的 Response 結果是不為空的,而是有長度的 json 內容(長度為 87),其中 content 鍵值內容才為空,所以這里判斷條件選擇比 87 大的數值即可,比如 100,即大於 100 的表示此頁有內容,小於 100 表示此頁已爬取完成。
{"state":{"code":2000000,"msg":"Ok","tips":""},"data":{"currPage":-1,"content":""}} |
第二、當需要從文本中解析內容時,不能直接解析,需要先轉換。
通常情況下,我們在解析內容時是直接對返回的 response 進行解析,比如使用 response.css() 方法,但此處,我們的解析對象不是 response,而是 response 返回的 json 內容中的 html 文本,文本是不能直接使用 .css() 方法解析的,所以在對 html 進行解析之前,需要添加下面一行代碼轉換后才能解析。
contents = scrapy.Selector(text=contents, type="html") |
結果如下,可以看到發送了全部 48 個請求,實際上該分類只有 22 頁內容,即多發送了無用的 26 個請求。
▌While 循環
接下來,我們使用 While 循環再次嘗試抓取,代碼省略了和 For 循環中相同的部分:
def start_requests(self): |
這里,簡要說明幾點:
第一、While 循環的思路是先從頭開始爬取,使用 parse() 方法進行解析,然后遞增頁數構造下一頁的 URL 請求,再循環解析,直到爬取完最后一頁即可,這樣 不會像 For 循環那樣發送無用的請求。
第二、parse() 方法構造下一頁請求時需要利用 start_requests() 方法中的參數,可以 使用 meta 方法來傳遞參數。
運行結果如下,可以看到請求數量剛好是 22 個,也就完成了所有頁面的 App 信息爬取。
以上,就是本文的所有內容,小結一下:
- 在爬取不確定頁數的網頁時,可以采取 For 循環和 While 循環兩種思路,方法大致相同。
- 在 Requests 和 Scrapy 中使用 For 循環和 While 循環的方法稍有不同,因此本文以豌豆莢網站為例,詳細介紹了循環構造方法。