【python爬蟲】動態載入頁面的解決辦法(以ins為例)
現在很多的web頁面使用ajax技術動態載入頁面。但對於爬蟲來說,目標資料很可能不在頁面HTML原始碼中(右鍵檢視網頁原始碼,通過F12查詢),針對靜態頁面的爬蟲不再滿足現在的需求。
很多教程都推薦用Selenium和PhantomJS配合使用,實現網頁的渲染,得到網頁的全部資訊。但是對於爬蟲程式,模擬瀏覽器記憶體開銷實在是非常大,而且效率低。
好訊息是,大多是是瀏覽器會在請求和解析HTML之後,根據js的“指示”再發送一次請求,得到頁面展示的內容,然後通過js渲染之後展示到介面。這樣的請求往往得到的內容是json格式的,所以我們非但不會加重爬蟲的任務,反而可能會省去解析HTML的功夫。
本文以爬取instagram上的某位明星上傳的全部圖片為例講解動態載入頁面的解決辦法。文末附上全部程式碼
工具:Chrome
包:json,requests,urllib
分析ins頁面
某使用者的主頁
開啟某使用者ins主頁(https://www.instagram.com/urnotchrislee/?hl=zh-cn)可以看到,首頁只加載了12張圖片,要點選“更多”才會載入更多的圖片。我們先獲取這12張圖片的URL。
獲取前12張圖片的URL
首先檢查原始碼中是否存在圖片的URL。在script標籤中發現前12張圖片的URL。
網頁原始碼
有了這個發現,我們就可以提取首頁的12張圖片URL!!
程式碼如下:
通過使用lxml庫實現解析,不瞭解lxml的童鞋可以去崔大神的部落格 學習學習。
獲取更多圖片的URL
在點選“更多”按鍵時,發現XHR選項卡中載入了一些新檔案。
載入前
載入後
每一次下拉頁面都會加載出12張圖片,“?query_idbalabala”檔案也會增加一個!
點選檔案檢視詳細資訊。發現該檔案返回json資料,並且資料中包含我們要的圖片URL!!
返回的json資料
所以說只要獲取到這些json資料,我們就能提取出更多的圖片URL。
請求資訊
請求資訊
請求資訊
仔細看上圖的Request URL,讓我們分析下它的請求引數(包括query_id,id,first,after)可以看到後續載入的query_id都為“17888483320059182”,“id”為你所訪問的使用者的使用者id,為“1161353543”,“first”為一次載入的圖片數,始終為12。“after”比較複雜。但是按常理來說,這個引數應該在前面的檔案中能夠找到。
找“after”
利用ctrl+F在頁面原始碼中找到了after引數對應的值。同樣是在script標籤中,不過對應的鍵名是“end_cursor”
湊齊了四個請求引數,這下我們可以構造Request URL,通過解析返回的json資料,獲取更多的圖片URL了
程式碼如下:
src_list中儲存了該使用者590張圖片的URL,現在該進入到下載環節啦~
下載圖片
博主懶得換代理,直接用來最簡單粗暴的方式下載了圖片。
結果展示~
全部程式碼
# -*- coding: utf-8 -*-
import json
import requests
from lxml import etree
from urllib import parse
BASE_URL = "https://www.instagram.com/urnotchrislee/"
headers = {
"Origin": "https://www.instagram.com/",
"Referer": "https://www.instagram.com/urnotchrislee/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/58.0.3029.110 Safari/537.36",
"Host": "www.instagram.com"}
def load_rest(table,has_next_page):
rest = []
while has_next_page:
text = json.dumps(table)
URL = 'https://www.instagram.com/graphql/query/?query_id=17888483320059182&variables='+parse.quote(text)
res = requests.get(URL,headers = headers)
dic = json.loads(res.content.decode(),encoding='utf-8')
data = dic['data']['user']['edge_owner_to_timeline_media']
nodes = data['edges']
end_cursor = data['page_info']['end_cursor']
has_next_page = data['page_info']['has_next_page']
for node in nodes:
rest.append(node['node']['display_url'])
#print(node['node']['display_url'])
table['after'] = end_cursor
print('載入..')
print('載入完成')
return rest
if __name__=='__main__':
res = requests.get(BASE_URL,headers = headers)
html = etree.HTML(res.content.decode())
# h = html.xpath('''//script[@type="text/javascript"]/text()''')[1].replace('window._sharedData =','').strip()
h = html.xpath('''//script[@type="text/javascript"]''')[1].text.replace('window._sharedData = ','').strip()[:-1]
dic = json.loads(h,encoding='utf-8')
data = dic['entry_data']['ProfilePage'][0]['user']['media']
nodes = data['nodes']
end_cursor = data['page_info']['end_cursor']
has_next_page = data['page_info']['has_next_page']
lee_id = nodes[0]["owner"]["id"] #'1161353543'
src_list = []
for node in nodes:
src_list.append(node['display_src'])
print(node['display_src'])
print('載入')
table = {
'id':lee_id,
'first':12,
'after':end_cursor}
rest = load_rest(table,has_next_page)
src_list = src_list + rest
print(len(src_list))
# with open('abc', 'w') as f:
# for s in src_list:
# f.write(s)
# f.write('\n')
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/58.0.3029.110 Safari/537.36",}
for i in range(len(src_list)):
url = src_list[i].strip()
res = requests.get(url,headers = headers)
with open('第'+str(i+1)+'張.jpg','wb') as ff:
ff.write(res.content)
參考網頁:
新手小白,邏輯混亂,歡迎大佬指正錯誤,求輕噴