騰訊動漫爬蟲與動態隨機載入反爬破解技術實戰

韋瑋發表於2017-09-07

本文作者韋瑋原創,轉載請註明出處。

專案需求與問題引入

有時,我們想爬取騰訊動漫中的漫畫,比如,我們不妨開啟騰訊動漫中某一個動漫的網址ac.qq.com/Comic/comic…,如下圖所示:

然後,我們點選“開始閱讀”,出現如下所示介面:

可以看到,在此有一副漫畫,我們可以按常規方式嘗試進行處理,我們檢視該網頁對應的原始碼,可以發現在原始碼中並不能找到這副漫畫的圖片地址,並且,當我們滑鼠往下滑動的時候,才會觸發載入後續的漫畫,所以,我們可以初步斷定,這種資料是通過非同步載入動態觸發出來的。

按照一貫的解決思路,我們接下來嘗試使用抓包分析進行解決這個問題,所以我們開啟Fiddler。

開啟Fiddler之後,我們再次開啟動漫頁拖動觸發出相應的漫畫,與此同時,Fiddler中會依次出現新觸發的資源資訊,如下所示:

我們依次分析這些網址,並把漫畫相關的網址整理複製出來,放到word中,如下所示:

通過對比觀察,我們可以看到漫畫資源的網址規律。

對應的規律如下:

ac.qq.com/store_file_download?buid=動漫ID&uin=uin值&dir_path=/&name=日期_隨機數_漫畫圖片ID.jpg

我們可以看到,其地址中有一段是隨機數,這一段網址我們很難通過以往的網址構造的方法構造出來,所以,即使分析出了網址規律也無濟於事,因為這個網址的規律中有一部分是隨機數,即無規律的欄位。

所以,顯然,這種網址動態觸發+資源隨機儲存的反爬策略我們採用以往的反爬攻關技巧很難解決,這一點大家可以先按常規的方法嘗試寫一遍便會有深刻感觸。

問題的解決辦法總是有的,只要我們思考,接下來,我們就為大家講解這一種反爬策略應該如何攻克解決,今天我們的主要需求與目的是使用Python自動爬取騰訊動漫裡面的各個漫畫,實現自動載入觸發漫畫並得到隨機地址的功能,以此為例為大家講解網址動態觸發+資源隨機儲存的反爬策略的攻克方式。

問題難點與解決思路

由上面的介紹,我們可以知道,目前問題的難點在於:

1、漫畫圖片動態觸發,非同步載入,無法通過漫畫的主網址獲得這些各漫畫圖片的網址,而沒有漫畫圖片的網址,我們無法爬取這些漫畫圖片。

2、漫畫圖片網址中含有隨機引數,即使我們通過抓包分析分析出各漫畫網址的規律,也無法主動構造出這些漫畫圖片的地址。

這些問題其實我們可以解決,先為大家介紹一下解決思路,解決思路如下:

1、通過PhantomJS(無介面瀏覽器)自動觸發出漫畫圖片。

2、通過JS程式碼實現頁面滑動,以自動觸發出剩下的多張漫畫圖片。

3、觸發出漫畫圖片之後,將漫畫地址通過正規表示式提取出來。

4、交給Urllib或者Scrapy普通爬蟲,對相關資源進行自動爬取,在這裡我們使用Urllib模組編寫相關爬蟲。

在這裡稍微解釋一下,PhantomJS雖然可以觸發相關的資料,因為其本質就是瀏覽器,但是其效率是比較慢的,所以,一般情況下,我們會將主要爬蟲處理部分交給Urllib或者Scrapy等常規爬蟲,這樣效率高,而如果常規爬蟲不能處理的部分,我們可以將這一部分交給PhantomJS等處理,處理完成後交由常規爬蟲處理,也就是不同的技術負責不同的部分,整合起來寫,這樣可以讓爬蟲的效率更高,並且不影響爬蟲的功能。

使用PhantomJS實現動態觸發動漫圖片地址的獲取

接下來,我們就來編寫實現相關的專案。

首先,我們匯入相關模組:

from selenium import webdriver
import time
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

然後,我們需要基於PhantomJS建立一個瀏覽器,並且設定一下使用者代理,否則可能出現介面不相容的情況,如下所示:

dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = ("Mozilla/4.0 (compatible; MSIE 5.5; windows NT)" )
browser = webdriver.PhantomJS(desired_capabilities=dcap)

然後,我們通過PhantomJS開啟相關動漫網頁,將相關動漫圖片地址觸發出來,如下所示:

#開啟動漫的第一頁

browser.get("ac.qq.com/ComicView/i…")

#將開啟的介面截圖儲存,方便觀察

a=browser.get_screenshot_as_file("D:/Python35/test.jpg")

#獲取當前頁面所有原始碼(此時包含觸發出來的非同步載入的資源)

data=browser.page_source

#將相關網頁原始碼寫入本地檔案中,方便分析

fh=open("D:/Python35/dongman.html","w",encoding="utf-8")

fh.write(data)

fh.close()

隨後,我們執行相關程式碼,執行完成後,會發現對應的截圖D:/Python35/test.jpg如下所示:

​可以看到,前面幾幅漫畫圖片成功載入,可是後面的漫畫圖片卻沒有載入出來,為什麼呢?

顯然後面的漫畫圖片我們需要觸發才能載入,所以我們可以使用JS程式碼實現自動拖動觸發後面的漫畫的功能。

在沒有觸發後續的漫畫圖片之前,我們不妨看一下此時的網頁原始碼,我們在原始碼中搜尋“ac.tc.qq.com/store_file_download”,即搜尋滿足漫畫圖片資源的網址格式的地址,看看原始碼中有沒有,如下所示:

​可以看到,此時只有4各匹配的網址,說明此時確實沒有載入出相關的剩下的動漫圖片資源網址。

接下來,我們可以通過window.scrollTo(位置1,位置2)實現自動滑動頁面,觸發後續的網址,我們可以在上面的:

browser.get("ac.qq.com/ComicView/i…")

程式碼下面插入如下補充程式碼:

for i in range(10):

js='window.scrollTo('+str(i*1280)+','+str((i+1)*1280)+')'

browser.execute_script(js)

time.sleep(1)

通過該迴圈,我們可以依次進行自動滑動,模擬滑動後自然會觸發後續的圖片資源。

隨後,我們再次執行程式碼,執行完程式碼後,截圖中你可以看到剩下的動漫圖片資源已經載入出來了,並且原始碼中,匹配的網址也變多了,原始碼中網址的匹配情況現在如下所示:

​可以看到,此時符合規律的網址已經變成了25個,所以,現在,當前頁面中的所有圖片資源已經載入出來了。

顯然,我們通過PhantomJS已經實現了非同步資源觸發與隨機網址獲取的功能了。接下來我們將需要提取出相關動漫圖片的網址,並交由Urllib模組進行後續爬取。

結束了PhantomJS的使用之後,我們需要關閉一下瀏覽器,所以,我們在程式碼後新增如下一行程式碼:

browser.quit()

編寫完整爬蟲專案

隨後,我們繼續編寫該爬蟲專案。

我們可以通過正規表示式'<img src="(http:..ac.tc.qq.com.store_file_download.buid=.*?name=.*?).jpg"'將所有動漫資源圖片網址提取出來,提取出來之後,通過urllib對這些圖片進行爬取,爬到本地。

具體程式碼實現如下:

import re
import urllib
#構造正規表示式提取動漫圖片資源網址

pat='<img src="(http:..ac.tc.qq.com.store_file_download.buid=.*?name=.*?).jpg"'

#獲取所有動漫圖片資源網址

allid=re.compile(pat).findall(data)

for i in range(0,len(allid)):

#得到當前網址

thisurl=allid[i]

#去除網址中的多餘元素amp;

thisurl2=thisurl.replace("amp;","")+".jpg"

#輸出當前爬取的網址

print(thisurl2)

#設定將動漫儲存到本地的本地目錄

localpath="D:/Python35/dongman/"+str(i)+".jpg"

#通過urllib對動漫圖片資源進行爬取

urllib.request.urlretrieve(thisurl2,filename=localpath)

隨後,我們執行該程式碼,便可以在本地目錄D:/Python35/dongman/下看到如下資訊:

​可以看到,相關動漫圖片資源已經爬到本地了。

為了方便大家除錯,我們提供完整程式碼,完整程式碼如下所示:

from selenium import webdriver

import time

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

import re

import urllib.request

dcap = dict(DesiredCapabilities.PHANTOMJS)

dcap["phantomjs.page.settings.userAgent"] = ("Mozilla/4.0 (compatible; MSIE 5.5; windows NT)" )

browser = webdriver.PhantomJS(desired_capabilities=dcap)

#開啟動漫的第一頁

browser.get("ac.qq.com/ComicView/i…")

for i in range(10):

js='window.scrollTo('+str(i*1280)+','+str((i+1)*1280)+')'

browser.execute_script(js)

time.sleep(1)

#將開啟的介面截圖儲存,方便觀察

a=browser.get_screenshot_as_file("D:/Python35/test.jpg")

#獲取當前頁面所有原始碼(此時包含觸發出來的非同步載入的資源)

data=browser.page_source

#將相關網頁原始碼寫入本地檔案中,方便分析

fh=open("D:/Python35/dongman.html","w",encoding="utf-8")

fh.write(data)

fh.close()

browser.quit()

#構造正規表示式提取動漫圖片資源網址

pat='<img src="(http:..ac.tc.qq.com.store_file_download.buid=.*?name=.*?).jpg"'

#獲取所有動漫圖片資源網址

allid=re.compile(pat).findall(data)

for i in range(0,len(allid)):

#得到當前網址

thisurl=allid[i]

#去除網址中的多餘元素amp;

thisurl2=thisurl.replace("amp;","")+".jpg"

#輸出當前爬取的網址

print(thisurl2)

#設定將動漫儲存到本地的本地目錄

localpath="D:/Python35/dongman/"+str(i)+".jpg"

#通過urllib對動漫圖片資源進行爬取

urllib.request.urlretrieve(thisurl2,filename=localpath)

可以看到,當我們直到了解決方法之後,專案實現起來並不難,在這裡,大家需要通過這一個例子,掌握這一類問題的解決思路,即掌握網址動態觸發+資源隨機儲存的反爬策略的攻克解決方案。希望大家可以多多練習,希望這篇文章能夠讓遇到這一類問題的同學獲得解決的思路與啟發。

本文作者韋瑋原創,轉載請註明出處。

作者新書推薦


相關文章