1. 程式人生 > >【python爬蟲】動態載入頁面的解決辦法(以ins為例)

【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)


參考網頁:

新手小白,邏輯混亂,歡迎大佬指正錯誤,求輕噴