Python爬蟲之煎蛋網妹子圖爬蟲,解密圖片連結加密方式__Python

來源:互聯網
上載者:User

之前在魚C論壇的時候,看到很多人都在用Python寫爬蟲爬煎蛋網的妹子圖,當時我也寫過,爬了很多的妹子圖片。後來煎蛋網把妹子圖的網頁改進了,對圖片的地址進行了加密,所以論壇裡面的人經常有人問怎麼請求的頁面沒有連結。這篇文章就來說一下煎蛋網OOXX妹子圖的連結擷取方式。

首先說明一下,之前煎蛋網之所以增加了反爬蟲機制,應該就是因為有太多的人去爬他們的網站了。爬蟲頻繁的訪問網站會給網站帶來壓力,所以,建議大家寫爬蟲簡單的運行成功就適可而止,不要過分地去爬別人的東西。 爬蟲思路分析 圖片下載流程圖

首先,用一張簡單的流程圖(非規範流程圖格式)來展示一下爬取簡單網的妹子圖的整個流程:

流程圖解讀

1、爬取煎蛋網的妹子圖,我們首先要開啟任意一個妹子圖的頁面,比如 http://jandan.net/ooxx/page-44#comments 然後,我們需要請求這個頁面,擷取2個關鍵的資訊(後續會說明資訊的具體作用),其中第一個資訊是每個妹子圖片的 hash 值,這個是後續用來解密產生圖片地址的關鍵資訊。

2、在頁面中除了提取到圖片的 hash 之外,還有提取到當前頁的一個關鍵的js檔案的地址,這個js檔案中包含了一個同樣是用來產生圖片地址的關鍵參數,要得到這個參數,必須去請求這個JS地址,當時妹子圖的每個頁面的js地址是不同的,所以需要從頁面中提取。

3、得到了圖片的 hash 和 js 中的關鍵參數之後,可以根據js 中提供的解密方式,得到圖片的連結,這個解密方式後續用Python代碼和js代碼的參照來說明。

4、有了圖片連結,下載圖片就不多說了,後續會有第二篇文章,來使用多線程+多進程的方式下載圖片。 頁面分析 網頁原始碼解讀

我們可以開啟一個妹子圖的頁面,還是最開始的 http://jandan.net/ooxx/page-44#comments 為例,然後查看原始碼(注意,不是審查元素),可以看到本應該放圖片地址的地方並沒有圖片地址,而是類似於下面的代碼:

<p><img src="//img.jandan.net/img/blank.gif" onload="jandan_load_img(this)" /><span class="img-hash">ece8ozWUT/VGGxW1hlbITPgE0XMZ9Y/yWpCi5Rz5F/h2uSWgxwV6IQl6DAeuFiT9mH2ep3CETLlpwyD+kU0YHpsHPLnY6LMHyIQo6sTu9/UdY5k+Vjt3EQ</span></p>

從這個代碼可以看出來,圖片地址被一個js函數代替了,也就是說圖片地址是由這個jandan_load_img(this)函數來擷取並載入的,所以,現在的關鍵是,需要到JS檔案中尋找這個函數的意義。 js檔案解讀

通過在每個js檔案中搜尋jandan_load_img,最後可以在一個地址類似於 http://cdn.jandan.net/static/min/1d694f08895d377af4835a24f06090d0.29100001.js 的檔案中找到這個函數的定義,將壓縮的JS代碼格式化查看,可以看到具體的定義如下片段:

function jandan_load_img(b) {    var d = $(b);    var f = d.next("span.img-hash");    var e = f.text();    f.remove();    var c = f_Qa8je29JONvWCrmeT1AJocgAtaiNWkcN(e, "agC37Is2vpAYzkFI9WVObFDN5bcFn1Px");

這段代碼的意思很容易看懂,首先它提取了當前標籤下css為img-hash的span標籤的文本,也就是我們最開始說的圖片的 hash 值,然後把這個值和一個字串參數(每個頁面的這個參數是變動的,這個頁面是 agC37Is2vpAYzkFI9WVObFDN5bcFn1Px)一起傳遞到另外一個函數f_Qa8je29JONvWCrmeT1AJocgAtaiNWkcN中,所以我們還要去查看這個函數的意義才行,這個函數就是用來產生圖片連結的函數了。 f_ 函數的解讀

可以在js中尋找這個f_函數的定義,可以看到有兩個,但是沒關係,根據代碼從上到下執行的規律,我們只需要看比較靠後的那個就行了,完整的內容如下:

var f_Qa8je29JONvWCrmeT1AJocgAtaiNWkcN = function(m, r, d) {    var e = "DECODE";    var r = r ? r : "";    var d = d ? d : 0;    var q = 4;    r = md5(r);    var o = md5(r.substr(0, 16));    var n = md5(r.substr(16, 16));    if (q) { if (e == "DECODE") { var l = m.substr(0, q) } } else { var l = "" }    var c = o + md5(o + l);    var k;    if (e == "DECODE") {        m = m.substr(q);        k = base64_decode(m)    }    var h = new Array(256);    for (var g = 0; g < 256; g++) { h[g] = g }    var b = new Array();    for (var g = 0; g < 256; g++) { b[g] = c.charCodeAt(g % c.length) }    for (var f = g = 0; g < 256; g++) {        f = (f + h[g] + b[g]) % 256;        tmp = h[g];        h[g] = h[f];        h[f] = tmp    }    var t = "";    k = k.split("");    for (var p = f = g = 0; g < k.length; g++) {        p = (p + 1) % 256;        f = (f + h[p]) % 256;        tmp = h[p];        h[p] = h[f];        h[f] = tmp;        t += chr(ord(k[g]) ^ (h[(h[p] + h[f]) % 256]))    }    if (e == "DECODE") { if ((t.substr(0, 10) == 0 || t.substr(0, 10) - time() > 0) && t.substr(10, 16) == md5(t.substr(26) + n).substr(0, 16)) { t = t.substr(26) } else { t = "" } }    return t};

這個函數需要傳遞3個參數,第一個參數是圖片的 hash值,第二個參數就是在jandan_load_img函數中看到的一個字串,第三個參數其實沒用,因為在jandan_load_img函數中根本沒有傳入。我們只需要按照JS代碼的意思把這個函數改寫成 Python 代碼就行了。 Python改寫函數

使用Python將f_函數改寫之後應該是這樣的:

def get_imgurl(m, r='', d=0):    '''解密擷取圖片連結'''    e = "DECODE"    q = 4    r = _md5(r)    o = _md5(r[0:0 + 16])    n = _md5(r[16:16 + 16])    l = m[0:q]    c = o + _md5(o + l)    m = m[q:]    k = _base64_decode(m)    h = list(range(256))    b = [ord(c[g % len(c)]) for g in range(256)]    f = 0    for g in range(0, 256):        f = (f + h[g] + b[g]) % 256        tmp = h[g]        h[g] = h[f]        h[f] = tmp    t = ""    p, f = 0, 0    for g in range(0, len(k)):        p = (p + 1) % 256        f = (f + h[p]) % 256        tmp = h[p]        h[p] = h[f]        h[f] = tmp        t += chr(k[g] ^ (h[(h[p] + h[f]) % 256]))    t = t[26:]    return t

這個函數需要用到另外兩個函數,第一個是MD5加密的函數,這個函數對應的是JS中這樣的段落:

var o = md5(r.substr(0, 16));

js的substr()函數其實就是Python裡面的切片的用法,稍微查看一下定義就能懂,不解釋。

MD5加密轉化成Python版本如下:

def _md5(value):    '''md5加密'''    m = hashlib.md5()    m.update(value.encode('utf-8'))    return m.hexdigest()

然後還有一個bash64的解碼函數,這個函數在js中的這一個段用到了:

k = base64_decode(m)

使用Python的時候需要注意,如果直接使用Python的base64.b64decode的話會報錯,具體的報錯內容是:

binascii.Error: Incorrect padding

所以在將資料進行解碼之前先要處理一下,具體的函數是:

def _base64_decode(data):    '''bash64解碼,要注意原字串長度報錯問題'''    missing_padding = 4 - len(data) % 4    if missing_padding:        data += '=' * missing_padding    return base64.b64decode(data)

到這裡,擷取圖片連結的函數就完成了,主要就是使用3個函數。

我們可以傳入兩個從網頁中複製到的參數到這個函數中測試一下:

m = 'ece8ozWUT/VGGxW1hlbITPgE0XMZ9Y/yWpCi5Rz5F/h2uSWgxwV6IQl6DAeuFiT9mH2ep3CETLlpwyD+kU0YHpsHPLnY6LMHyIQo6sTu9/UdY5k+Vjt3EQ'r = 'HpRB2OSft5RhlSyZaXV8xYpvEAgDThcA'print(get_imgurl(m,r))

可以看到如下輸出:

//ww3.sinaimg.cn/mw600/0073ob6Pgy1fpet9wku7dj30hs0qljuz.jpg

注意:這裡的r參數是從每個頁面中的js中複製的,每個頁面的js地址是變動的,這個參數也是變動的。 擷取hash和js地址

之前說過,hash值是擷取圖片地址的關鍵參數,而另外的參數在js檔案中,並且這個js檔案每個頁面不同,所以現在來提取這兩個關鍵參數。 批量擷取hash

擷取圖片的hash值很方便,我們可以使用 BeautifulSoup 的方法即可,具體的程式碼片段:

def get_urls(url):    '''擷取一個頁面的所有圖片的連結'''    headers = {        'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',        'Host': 'jandan.net'    }    html = requests.get(url, headers=headers).text    js_url = 'http:' + re.findall('<script src="(//cdn.jandan.net/static/min/[\w\d]+\.\d+\.js)"></script>', html)[-1]    _r = get_r(js_url)    soup = BeautifulSoup(html, 'lxml')    tags = soup.select('.img-hash')    for tag in tags:        img_hash = tag.text        img_url = get_imgurl(img_hash,_r)        print(img_url)

提取圖片hash 的代碼是這一句:

soup = BeautifulSoup(html, 'lxml')    tags = soup.select('.img-hash')    for tag in tags:        img_hash = tag.text
擷取js中關鍵字符串

而擷取js地址的方式是使用的Regex:

js_url = 'http:' + re.findall('<script src="(//cdn.jandan.net/static/min/[\w\d]+\.\d+\.js)"></script>', html)[-1]

這裡要注意,因為正則提取的是一個列表,所以最後需要取列表中的一個連結,經過查看,我發現有的頁面有兩個這種JS檔案,有一個是被注釋掉了,所以都要使用最後一個,這個的表達方式是清單索引中使用[-1]取最後一個。

得到js地址之後需要請求,然後找到關鍵字符串,具體可以寫成一個函數:

def get_r(js_url):    '''擷取關鍵字符串'''    js = requests.get(js_url).text    _r = re.findall('c=f_[\w\d]+\(e,"(.*?)"\)', js)[0]    return _r
完整代碼

下面就是擷取一個頁面的全部的圖片連結的完整代碼:

# -*- coding: utf-8 -*-import requestsfrom bs4 import BeautifulSoupimport hashlibimport reimport base64def _md5(value):    '''md5加密'''    m = hashlib.md5()    m.update(value.encode('utf-8'))    return m.hexdigest()def _base64_decode(data):    '''bash64解碼,要注意原字串長度報錯問題'''    missing_padding = 4 - len(data) % 4    if missing_padding:        data += '=' * missing_padding    return base64.b64decode(data)def get_imgurl(m, r='', d=0):    '''解密擷取圖片連結'''    e = "DECODE"    q = 4    r = _md5(r)    o = _md5(r[0:0 + 16])    n = _md5(r[16:16 + 16])    l = m[0:q]    c = o + _md5(o + l)    m = m[q:]    k = _base64_decode(m)    h = list(range(256))    b = [ord(c[g % len(c)]) for g in range(256)]    f = 0    for g in range(0, 256):        f = (f + h[g] + b[g]) % 256        tmp = h[g]        h[g] = h[f]        h[f] = tmp    t = ""    p, f = 0, 0    for g in range(0, len(k)):        p = (p + 1) % 256        f = (f + h[p]) % 256        tmp = h[p]        h[p] = h[f]        h[f] = tmp        t += chr(k[g] ^ (h[(h[p] + h[f]) % 256]))    t = t[26:]    return tdef get_r(js_url):    '''擷取關鍵字符串'''    js = requests.get(js_url).text    _r = re.findall('c=f_[\w\d]+\(e,"(.*?)"\)', js)[0]    return _rdef get_urls(url):    '''擷取一個頁面的所有圖片的連結'''    headers = {        'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',        'Host': 'jandan.net'    }    html = requests.get(url, headers=headers).text    js_url = 'http:' + re.findall('<script src="(//cdn.jandan.net/static/min/[\w\d]+\.\d+\.js)"></script>', html)[-1]    _r = get_r(js_url)    soup = BeautifulSoup(html, 'lxml')    tags = soup.select('.img-hash')    for tag in tags:        img_hash = tag.text        img_url = get_imgurl(img_hash,_r)        print(img_url)if __name__ == '__main__':    get_urls('http://jandan.net/ooxx/page-44')

運行上面的代碼,可以列印出這個頁面的所有圖片連結,部分連結如下:

//ww3.sinaimg.cn/mw600/0073ob6Pgy1fpet9wku7dj30hs0qljuz.jpg//ww3.sinaimg.cn/mw600/0073tLPGgy1fpet9mszjwj30hs0g1jsv.jpg//ww3.sinaimg.cn/mw600/0073ob6Pgy1fpesskkgobj31jk1jkk5b.jpg//wx3.sinaimg.cn/mw600/006XfbArly1fpesq2jn1vj30j60svaz3.jpg//wx3.sinaimg.cn/mw600/6967abd2gy1fpenoyobrcj20u03d0b2d.jpg//wx3.sinaimg.cn/mw600/6967abd2gy1fpenp38v9uj20u03zkhdy.jpg

總結:到這裡為止,提取煎蛋網妹子圖的圖片連結的方式其實已經給出來了,下一篇會接著講通過多線程+多進程的方式下載圖片。

原文首發:http://www.tendcode.com/article/jiandan-meizi-spider/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.