以下內容轉載自:https://www.makcyun.top/web_scraping_withpython4.html
文章關於網站使用Ajaxj技術加載頁面數據,進行爬取講的很詳細
大致步驟如下:
(1)爬取索引頁數據
(2)解析索引頁面數據
(3)爬取詳情頁數據
(4)解析詳情頁數據
(5)保存圖片
澎湃網文章的質量不錯,它的”美數課”欄目的信息圖做得也很好。圖片干貨多還能帶來ppt和圖表制作的技巧。為了更方便瀏覽所有文章圖片,通過分析Ajax爬取欄目至今所有信息圖的圖片。
摘要: 上一篇文章介紹了單頁圖片的爬取,但是當爬取多頁時,難度會增加。同時,前幾篇爬蟲文章中的網站有一個明顯的特點是:可以通過點擊鼠標實現網頁的翻頁,並且url會發生相應的變化。除了此類網站以外,還有一類非常常見的網站特點是:沒有”下一頁”這樣的按鈕,而是”加載更多”或者會不斷自動刷新從而呈現出更多的內容,同時網頁url也不發生變化。這種類型的網頁通常采用的是Ajax技術,要抓取其中的網頁內容需要采取一定的技巧。本文以信息圖做得非常棒的澎湃”美數課”為例,抓取該欄目至今所有文章的圖片。
欄目網址:https://www.thepaper.cn/list_25635
本文知識點:
- Ajax知識
- 多頁圖片爬取
1. Ajax知識
在該主頁上嘗試不斷下拉,會發現網頁不斷地加載出新的文章內容來,而並不需要通過點擊”下一頁”來實現,而且網址url也保持不變。也就是說在同一個網頁中通過下拉源源不斷地刷新出了網頁內容。這種形式的網頁在今天非常常見,它們普遍是采用了Ajax技術。
Ajax 全稱是 Asynchronous JavaScript and XML(異步 JavaScript 和 XML)。
它不是一門編程語言,而是利用 JavaScript 在保證頁面不被刷新、頁面鏈接不改變的情況下與服務器交換數據並更新部分網頁的技術。
采用了Ajax的網頁和普通的網頁有一定的區別,普通網頁的爬蟲代碼放在這種類型的網頁上就行不通了,必須另辟出路。下面我們就來嘗試一下如何爬取網易”數讀”所有的文章。
主頁右鍵-檢查
,然后按f5
刷新,會彈出很多鏈接文件。鼠標上拉回到第一個文件:list_25635,在右側按ctrl+f
搜索一下第一篇文章的標題:”娃娃機生意經”,可以看到在html網頁中找到了對應的源代碼。
接着,我們拖動下拉鼠標,顯示出更多文章。然后再次搜索一篇文章的標題:”金磚峰會”,會發現搜不到相應的內容了。是不是感覺很奇怪?
其實,這里就是用了Ajax的技術,和普通網頁翻頁是刷新整個網頁不同,這種類型網頁可以再保持url不變的前提下只刷新部分內容。這就為我們進行爬蟲帶來了麻煩。因為,我們通過解析網頁的url:https://www.thepaper.cn/list_25635
只能爬取前面部分的內容而后面通過下拉刷新出來的內容是爬取不到的。這顯然不完美,那么怎么才能夠爬取到后面不斷刷新出來的網頁內容呢?
2. url分析
我們把右側的選項卡從ALL
切換到Network
,然后按再次按f5
刷新,可以發現Name
列有4個結果。選擇第3個鏈接打開並點擊Response
,通過滑動可以看到一些文本內容和網頁中的文章標題是一一對應的。比如第一個是:娃娃機生意經|有沒有好奇過抓娃娃機怎么又重新火起來了?,一直往下拖拽可以看到有很多篇文章。此時,再切換到headers選項卡,復制Request URL
后面的鏈接並打開,會顯示一部分文章的標題和圖片內容。數一下的話,可以發現一共有20個文章標題,也就是對應着20篇文章。
這個鏈接其實和上面的list_25635鏈接的內容是一致的。這樣看來,好像發現不了什么東西,不過不要着急。
接下來,回到Name
列,嘗試滾動下拉鼠標,會發現彈出好幾個新的開頭為load_index
的鏈接來。選中第一個load_index
的鏈接,點擊Response
查看一下html源代碼,嘗試在網頁中搜索一下:十年金磚峰
這個文章的標題,驚奇地發現,在網頁中找到了對於的文章標題。而前面,我們搜索這個詞時,是沒有搜索到的。
這說明了什么呢?說明十年金磚峰
這篇文章的內容不在第一個list_25635鏈接中,而在這個load_index
的鏈接里。鼠標點擊headers
,復制Request URL
后面的鏈接並打開,就可以再次看到包括這篇文章在內的新的20篇文章。
是不是發現了點了什么?接着,我們繼續下拉,會發現彈出更多的load_index
的鏈接。再搜索一個標題:地圖湃|海外港口熱
,可以發現在網頁中也同樣找到了文章標題。
回到我們的初衷:下載所有網頁的圖片內容。那么現在就有解決辦法禮:一個個地把出現的這些url網址中圖片下載下來就大功告成了。
好,我們先來分析一下這些url,看看有沒有相似性,如果有很明顯的相似性,那么就可以像普通網頁那樣,通過構造翻頁頁數的url,實現for循環就可以批量下載所有網頁的圖片了。復制前3個鏈接如下:
https://www.thepaper.cn/load_index.jsp?nodeids=25635&topCids=&pageidx=2&isList=true&lastTime=1533169319712 |
發現pageidx
鍵的值呈現規律的數字遞增變化,看起來是個好消息。但同時發現后面的lastTime鍵的值看起來是隨機變化的,這個有沒有影響呢? 來測試一下,復制第一個鏈接,刪掉&lastTime=1533169319712
這一串字符,會發現網頁一樣能夠正常打開,就說明着一對參數不影響網頁內容,那就太好了。我們可以刪除掉,這樣所有url的區別只剩pageidx
的值了,這時就可以構造url來實現for循環了。構造的url形式如下:
https://www.thepaper.cn/load_index.jsp?nodeids=25635&pageidx=2 |
同時,嘗試把數字2改成1並打開鏈接看看會有什么變化,發現呈現的內容就是第1頁的內容。這樣,我們就可以從第一頁開始構造url循環了。https://www.thepaper.cn/load_index.jsp?nodeids=25635&pageidx=1
既然確定了首頁,那么也要相應地確定一下尾頁。很簡單,我們把數字改大然后打開鏈接看是否有內容即可。比如改為10 ,打開發現有內容顯示,很好。接着,再改為30,發現沒有內容了。說明該欄目的頁數介於這兩個數之間,嘗試幾次后,發現25
是最后一個有內容的網頁,也意味着能夠爬取的頁數一共是25頁。
確定了首頁和尾頁后,下面我們就可以開始構造鏈接,先爬取第一篇文章網頁里的圖片(這個爬取過程,我們上一篇爬取網易”數讀”已經嘗試過了),然后爬取這一整頁的圖片,最后循環25頁,爬取所有圖片,下面開始吧。
3. 程序代碼
import os import re from multiprocessing import Pool from urllib.parse import urlencode import requests from bs4 import BeautifulSoup from requests.exceptions import RequestException headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36' } # 1 獲取索引界面網頁內容
def get_page_index(i): # 下載1頁
# url = 'https://www.thepaper.cn/newsDetail_forward_2370041'
# 2下載多頁,構造url
paras = { 'nodeids': 25635, 'pageidx': i } url = 'https://www.thepaper.cn/load_index.jsp?' + urlencode(paras) response = requests.get(url, headers=headers) if response.status_code == 200: return response.text # print(response.text) # 測試網頁內容是否提取成功ok
# 2 解析索引界面網頁內容
def parse_page_index(html): soup = BeautifulSoup(html, 'lxml') # 獲取每頁文章數
num = soup.find_all(name='div', class_='news_li') for i in range(len(num)): yield { # 獲取title
'title': soup.select('h2 a')[i].get_text(), # 獲取圖片url,需加前綴
'url': 'https://www.thepaper.cn/' + soup.select('h2 a')[i].attrs['href'] # print(url) # 測試圖片鏈接
} # 3 獲取每條文章的詳情頁內容
def get_page_detail(item): url = item.get('url') # 增加異常捕獲語句
try: response = requests.get(url, headers=headers) if response.status_code == 200: return response.text # print(response.text) # 測試網頁內容是否提取成功
except RequestException: print('網頁請求失敗') return None # 4 解析每條文章的詳情頁內容
def parse_page_detail(html): soup = BeautifulSoup(html, 'lxml') # 獲取title
if soup.h1: # 有的網頁沒有h1節點,因此必須要增加判斷,否則會報錯
title = soup.h1.string # 每個網頁只能擁有一個<H1>標簽,因此唯一
items = soup.find_all(name='img', width=['100%', '600']) # 有的圖片節點用width='100%'表示,有的用600表示,因此用list合並選擇
# https://blog.csdn.net/w_xuechun/article/details/76093950
# print(items) # 測試返回的img節點ok
for i in range(len(items)): pic = items[i].attrs['src'] # print(pic) #測試圖片鏈接ok
yield { 'title': title, 'pic': pic, 'num': i # 圖片添加編號順序
} # 5 下載圖片
def save_pic(pic): title = pic.get('title') # 標題規范命名:去掉符號非法字符| 等
title = re.sub('[\/:*?"<>|]', '-', title).strip() url = pic.get('pic') # 設置圖片編號順序
num = pic.get('num') if not os.path.exists(title): os.mkdir(title) # 獲取圖片url網頁信息
response = requests.get(url, headers=headers) try: # 建立圖片存放地址
if response.status_code == 200: file_path = '{0}\{1}.{2}'.format(title, num, 'jpg') # 文件名采用編號方便按順序查看,而未采用哈希值md5(response.content).hexdigest()
if not os.path.exists(file_path): # 開始下載圖片
with open(file_path, 'wb') as f: f.write(response.content) print('文章"{0}"的第{1}張圖片下載完成'.format(title, num)) else: print('該圖片%s 已下載' % title) except RequestException as e: print(e, '圖片獲取失敗') return None def main(i): # get_page_index(i) # 測試索引界面網頁內容是否獲取成功ok
html = get_page_index(i) data = parse_page_index(html) # 測試索引界面url是否獲取成功ok
for item in data: # print(item) #測試返回的dict
html = get_page_detail(item) data = parse_page_detail(html) for pic in data: save_pic(pic) # 單進程 # if __name__ == '__main__': # for i in range(1, 26): # main(i)
# 多進程
if __name__ == '__main__': pool = Pool() pool.map(main, [i for i in range(1, 26)]) pool.close() pool.join()
文章代碼和欄目從2015年至今437篇文章共1509張圖片資源,可在下方鏈接中得到。
https://github.com/makcyun/web_scraping_with_python
本文完。