转自:https://yukunweb.com/2018/5/jiandan-encryption-processing/?page=1 俞坤的博客

最近一直有朋友问我改版的煎蛋网妹子图怎么爬,因为他们花费精力结果抓了一整个文件夹的防盗图。我之前在很久以前的一篇博客说过,对于这种js处理的网页,要想抓取到网页上看到的数据,大致有三种方法:

  • Selenium结合浏览器驱动,直接获取加载js后的页面,解析数据。这种方法最为简单粗暴,不过速度会慢一点,处理煎蛋这样的网页有点大材小用;
  • 直接使用python执行js文件,幸运的是PyV8库很符合要求,不过PyV8似乎不支持python3,python3可以使用PyExecJS库。这种方法也很简单,不过如果执行的js文件依赖pquery库的话,比较麻烦;
  • 用python模拟js加密方式,拿到加密处理后的数据,这种方法就是本篇主要讨论的内容,优点是依赖少速度快,缺点是如果煎蛋加密方式改了,需要跟着改。

分析网页

首先打开审查元素看他的实际响应内容,可以看到img标签中实际src属性的值是一个固定的值//img.jandan.net/img/blank.gif。而onload属性指向的是一个js的jandan_load_img()函数,this参数大多数情况是指的当前标签。后面接着span标签包含的一串hash值。

目前我们找到了jandan_load_img()函数,接着就是确定包含这个函数的js文件。方法很简单,在每一个返回的js响应中,去搜索。

目前的js文件是经过压缩混淆过的,我们可以复制放到线上解压工具里解压。如果用的是Chrome,可以找到source文件,点击如下图标红框的按钮:

jandan_load_img()函数内容为

function jandan_load_img(b) {var d = $(b);var f = d.next("span.img-hash");var e = f.text();f.remove();var c = jdXFKzuIDxRVqKYQfswJ5elNfow1x0JrJH(e, "zE4N6eHuAQP8vkQPb0wcuEcWnLzHYVhy");var a = $('<a href="' + c.replace(/(\/\/\w+\.sinaimg\.cn\/)(\w+)(\/.+\.(gif|jpg|jpeg))/, "$1large$3") + '" target="_blank" class="view_img_link">[查看原图]</a>');d.before(a);d.before("<br>");d.removeAttr("onload");d.attr("src", location.protocol + c.replace(/(\/\/\w+\.sinaimg\.cn\/)(\w+)(\/.+\.gif)/, "$1thumb180$3"));...
}

可以看到js文件依赖PQuery库,它拿到img标签,接着拿到后面的span标签内容,然后将它和一个常量传给一串字符的这个函数,拿到这个一串字符的函数返回的内容,放到img和a标签中。

我们在当前js文件搜索是否有这个jdXFKzuIDxRVqKYQfswJ5elNfow1x0JrJH()函数,如果不出意外的话,可以搜索到2个函数。我们选择后面一个,内容如下:

var jdXFKzuIDxRVqKYQfswJ5elNfow1x0JrJH = 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
};

拿到这段返回正确url的js代码,我们只要用python语言翻译过来就可以了。

抓取思路

  1. 请求网页拿到html;
  2. 从网页中解析出所有img标签后span标签包含的hash值;
  3. 正则匹配到html中对应js文件url,请求得到js代码;
  4. 正则匹配到js中jandan_load_img()传递给解密函数的常量;
  5. 将hash值和常量传递给解密函数,返回对应图片url;
  6. 下载图片。

翻译js函数

对面上面的步骤,我不做多余描述,主要说一下对js代码的翻译,后面会放整理后的代码地址。

如果要翻译上面的js代码,只需要搞清楚代码中调用的函数对应python什么函数就可以了,没有什么难点。

md5()是对md5.jshex_md5()加密方式的封装:

function md5(a) {return hex_md5(a)
}

这个方法就相当于python的hashlib库提供的md5摘要加密算法。感兴趣的朋友自行了解,由于后面多次调用此方法,我们对它进行一个封装:

def md5(str):md5 = hashlib.md5()md5.update(str.encode('utf-8'))return md5.hexdigest()

js中的r.substr(0, 16)调用的是stringObject.substr(start,length)函数。substr()方法可在字符串中抽取从start下标开始的指定length数目的字符。这里注意的是length指的是长度,不是结束的下标,也就是它可以翻译为:

r[0:16]
r[16:32] # 注意不是r[16:16]

base64_decode(m)函数是对js的原生Base64编码api的封装:

function base64_decode(a) {return window.atob(a)
}

大致使用如:

var str = 'javascript'window.btoa(str) //转码结果 "amF2YXNjcmlwdA=="
window.atob("amF2YXNjcmlwdA==") //解码结果 "javascript"

Base64是一种用64个字符来表示任意二进制数据的方法。幸运的是python内置的有base64库,可以直接进行base64的编解码。我们也对他进行一个封装,方便阅读。这里有一个要注意的是,因为base64是把3个字节变为4个字节,base64编码的长度必须是4的倍数,所以对于不是4的倍数的字符,需要加上=把base64字符串的长度变为4的倍数。

def decode_base64(data):return base64.b64decode(data + (4 - len(data) % 4) * '=')

var h = new Array(256);翻译过来就是h = list(range(256))

c.charCodeAt(g % c.length)调用的是js的stringObject.charCodeAt(index)函数,返回指定位置的字符的Unicode编码。这个返回值是0 - 65535之间的整数。这个方法相当于python的ord()函数。这行翻译过来就是:

b[g] = ord(c[g % len(c)])

k = k.split("");是将字符串k分解成单独的字符列表,在python中字符串本身也是一个可迭代对象,忽略就好。

chr()ord()和python内置的chr()ord()函数类似,我们直接调用对于函数,运行则回报错:ord() expected string of length 1, but int found。因为python中的ord()函数参数是一个str类型的参数,而k[g]实际上是一个字节,我们就不用调用ord()函数就可以了。即:

t += chr(k[g] ^ (h[(h[p] + h[f]) % 256]))

最后

原作者代码链接:https://github.com/Blackyukun/Jiandan

加密方式一直在变,可能会出错,我写的时候按下面程序就能运行。

import re
import os
import hashlib
import base64import requests
from lxml import etree
from requests import ConnectionErrordef get_html(url):"""请求页面,返回响应"""headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}try:resp = requests.get(url, headers=headers)if resp.status_code == 200:return respreturn Noneexcept ConnectionError:print('Error.')return Nonedef get_js_file():"""正则匹配加密 js url"""base_url = 'http://jandan.net/ooxx/'html = get_html(base_url).textjs_url = ''try:pattern = r'.*<script\ssrc=\"\/\/(cdn.jandan.net\/static\/min.*?)\"><\/script>.*'result = re.findall(pattern, html)js_url = "http://" + result[len(result) - 1]except Exception as e:print(e)js = get_html(js_url).textreturn jsdef get_salt(js):"""正则匹配 js 中的加密常量"""pattern = r'jandan_load_img.*?var c.*?"(.*?)"'salt = re.findall(pattern, js, re.S)[0]# print(salt)return saltdef all_img_hash(page_url):"""请求页面,返回页面中所有图片 hash"""html = get_html(page_url).textdoc = etree.HTML(html)img_hash = doc.xpath('//span[@class="img-hash"]/text()')# print(img_hash)return img_hashdef init_md5(str):"""封装 md5"""md5 = hashlib.md5()md5.update(str.encode('utf-8'))return md5.hexdigest()def decode_base64(data):"""封装 base64"""return base64.b64decode(data + (4 - len(data) % 4) * '=')def simulation_js(img_hash, salt):"""翻译 js 加密方式:param img_hash: 图片 hash:param salt: 加密常量:return: 图片 url"""# r = salt if salt else ''# d = 0# q = 4# r = init_md5(r)# o = init_md5(r[:16])# n = init_md5(r[16:32])# if q:#     l = img_hash[:q]# else: l = ''## c = o + init_md5(o + l)# img_hash = img_hash[q:]# k = decode_base64(img_hash)## h = list(range(256))# b = list(range(256))# for g in range(256):#     b[g] = ord(c[g % len(c)])# f = 0# for g in range(256):#     f = (f + h[g] + b[g]) % 256#     h[g], h[f] = h[f], h[g]## t = ''# p = f = 0# for g in range(len(k)):#     p = (p + 1) % 256#     f = (f + h[p]) % 256#     h[p], h[f] = h[f], h[p]#     t += chr(k[g] ^ (h[(h[p] + h[f]) % 256]))# t = t[26:]t = decode_base64(img_hash)  # print(t)return tdef parse_hash(salt, page_url):img_hash = all_img_hash(page_url)# print(img_hash)for i in img_hash:yield simulation_js(i, salt)def download_img(dir_path, img_url):"""下载"""filename = img_url[-14:]# print(img_url)img_content = get_html(img_url).contentif not os.path.exists(dir_path):os.mkdir(dir_path)try:with open(os.path.join(dir_path, filename), 'wb') as f:f.write(img_content)return Trueexcept Exception as e:print(e)return Falsedef main(dir_path, page=1):js = get_js_file()salt = get_salt(js)base_url = 'http://jandan.net/ooxx/'for i in range(page+1):page_url = base_url + 'page-{}/'.format(58-i)# print(page_url)# //wx1.sinaimg.cn/large/672f3952gy1g0u2mqymhyj20u00u0dja.jpg 正确结果for img_url in parse_hash(salt, page_url):# img_url = 'w'+str(img_url, encoding = "utf-8")img_url = str(img_url, encoding = "utf-8")print(img_url)r = download_img(dir_path, 'http:' + img_url)if r: print('success')if __name__ == '__main__':dir_path = 'E:/jiandan/'main(dir_path)

这次不知道为什么煎蛋的解密变简单了.......

最后十分感谢原作者的分享,一下子学会了不少。

Python爬虫:煎蛋网图片URL解密处理相关推荐

  1. Python爬虫 煎蛋网全站妹子图爬虫

    爬取流程 从煎蛋网妹子图第一页开始抓取: 爬取分页标签获得最后一页数字: 根据最后一页页数,获得所有页URL: 迭代所有页,对页面所有妹子图片url进行抓取:访问图片URL并且保存图片到文件夹. 开始 ...

  2. [Python爬虫]煎蛋网OOXX妹子图爬虫(1)——解密图片地址

    之前在鱼C论坛的时候,看到很多人都在用Python写爬虫爬煎蛋网的妹子图,当时我也写过,爬了很多的妹子图片.后来煎蛋网把妹子图的网页改进了,对图片的地址进行了加密,所以论坛里面的人经常有人问怎么请求的 ...

  3. [Python 爬虫]煎蛋网 OOXX 妹子图爬虫(1)——解密图片地址

    之前在鱼C论坛的时候,看到很多人都在用 Python 写爬虫爬煎蛋网的妹子图,当时我也写过,爬了很多的妹子图片.后来煎蛋网把妹子图的网页改进了,对图片的地址进行了加密,所以论坛里面的人经常有人问怎么请 ...

  4. python爬虫 煎蛋网妹子图 动态爬虫1

    ------来自某潮汕人的菜鸟教程     爬虫目标网站:http://jandan.net/ooxx     爬虫目标中文名称:煎蛋网 妹子图     爬虫需求:分析js下载 煎蛋网的妹子图     ...

  5. python爬虫图片实例-Python爬虫爬取煎蛋网图片代码实例

    这篇文章主要介绍了Python爬虫爬取煎蛋网图片代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 今天,试着爬取了煎蛋网的图片. 用到的包: ...

  6. python爬图代码实例_Python爬虫爬取煎蛋网图片代码实例

    这篇文章主要介绍了Python爬虫爬取煎蛋网图片代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 今天,试着爬取了煎蛋网的图片. 用到的包: ...

  7. Python爬虫爬取煎蛋网图片代码实例

    这篇文章主要介绍了Python爬虫爬取煎蛋网图片代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 今天,试着爬取了煎蛋网的图片. 用到的包: ...

  8. Python爬虫之煎蛋网图片下载

    受程序员群的影响(自己污的本性),他们总是带我开车,想想我也该收集一些资料了(美女图片) 代码 import requests from lxml import etreeurls = ['http: ...

  9. 【Python】煎蛋网XXOO图片抓取

    今天写一个爬虫爱好者特别喜欢的网站煎蛋网http://jandan.net/ooxx,这个网站其实还是有点意思的,网站很多人写了N多的教程了,各种方式的都有,当然网站本身在爬虫爱好者的不断进攻下,也在 ...

最新文章

  1. 使用OpenCV实现道路车辆计数
  2. php 正则教程,最通俗易懂的php正则表达式教程(上)
  3. Web安全***测试之信息搜集篇
  4. gin使用 GET, POST, PUT, PATCH, DELETE, OPTIONS
  5. Javascript构造函数的继承
  6. vim替换字符串带斜杠_Linux vi/vim最全使用指南
  7. 兰州大学c语言课程作业答案,兰州大学11春学期《C语言程序设计》第1次作业答案...
  8. Python+socket完美实现TCP长连接保持存活
  9. 快速创建精彩的Flash游戏 (二) Flash3D引擎简介
  10. R语言可视化学习笔记之ggrepel包
  11. Eclipse srever起来时,时间超过45s。
  12. AutoPtr 的局限性
  13. 微信支付之H5页面WAP端接入
  14. 微型计算机配置单8000,计算机公共基础知识8000题库答案解析
  15. STL 堆 鱼塘钓鱼
  16. echarts实现世界地图,给不同数值的国家着色,并根据经纬度在对应位置上添加标签(一)
  17. 当女友让程序员去买西瓜...... | 每日趣闻
  18. fater-rcnn绘制loss图
  19. 台式计算机硬盘主要有哪两种接口,台式机硬盘和笔记本硬盘都有哪些区别?
  20. 国内外大数据可视化分析产品点评

热门文章

  1. css 水平+垂直居中
  2. 基于emWin的WAV,MP3软解软件播放器,带类似千千静听频谱,含uCOS-III和FreeRTOS两个版本...
  3. 百度网盘和视频网站都在用的P2P技术是什么?
  4. 工作压力大,如何自我调节?
  5. 调用地区天气预报代码
  6. 红米6a android版本,红米6A 也能升级 Android P,小米公布各机型升级计划
  7. Nature发文:基础科学创新速度放缓,已经进入「增量时代」
  8. 语音识别原理与应用:第三章 语音特征提取 3.1预处理
  9. 详解单片机最小系统组成
  10. 来看Prada大秀吗?在元宇宙里那种!