JS逆向爬虫案例分享

2023-10-31

某域网站数据爬取之反爬策略JS逆向分析

本次分享解析某域网站数据的反爬机制。

此次只做技术分享,如有侵权,请联系删除。

1、分析网

首先打开网站发送请求,点击F12,发送请求发现电脑端并没有接口返回数据,即返回切换手机端观察。点解F12观察找到数据接口如下图:在这里插入图片描述
打开一个具体的商品,点击搜索,输入商品对应的价格,观察返回的接口链接,找到存在商品信息的接口。如下图:
在这里插入图片描述

观察可以发现这个链接对应能找到关于此商品的基本数据连接,接下来我们对该链接进行分析。
	在整个请求headers里面有一个关键的字段值为如图:

很显然这个ehsy-verify字段是一个JS加密后的值,所以我们的重点就在于如何得到这个ehsy-verify值。最简单查找办法就是在搜索框中输入ehsy-verify,然后找到有关的ehsy-verify的JS文件进行分析。
在这里插入图片描述

最后找到了这个文件中有关ehsy-verify值得由来,接下来我们将进入源文件中对其进行运行调试。

2、JS文件解密分析

ehsy-verify的值是由(0, o.aes_gobal)()得来的,所以对其尽心断点调试,然后发现(0, o.aes_gobal)()的值跟两个函数有关如图:
在这里插入图片描述

所以对其进行调试运行发现函数p中定义了e,n,i,o,t几个变量,即e的值为(0,a.default)(t),可以看出a.default是另外一个函数,其传入的参数就是t,t的值很容易的得到就是t = ((new Date).getTime() + "").slice(0, 10),其值就是当前的时间戳进行了下标0-10的切片。那么就开始对a.default函数进行代码反扒,进行断点进入函数。整个过程比较复杂,其嵌套涉及的函数比较多,主要就是找到相关的函数并对其进行处理,我直接列出部分内容:
var ERROR = "input is invalid type"
    , WINDOW = "object" === typeof window
    , root = WINDOW ? window : {};
root.JS_MD5_NO_WINDOW && (WINDOW = !1);
var WEB_WORKER = !WINDOW && "object" === typeof self
    , NODE_JS = !root.JS_MD5_NO_NODE_JS && "object" === typeof process && process.versions && process.versions.node;
NODE_JS ? root = global : WEB_WORKER && (root = self);
var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && "object" === typeof module && module.exports,
    // AMD = __webpack_require__("3c35"),
    ARRAY_BUFFER = 'True',
    HEX_CHARS = "0123456789abcdef".split(""), EXTRA = [128, 32768, 8388608, -2147483648], SHIFT = [0, 8, 16, 24],
    OUTPUT_TYPES = ["hex", "array", "digest", "buffer", "arrayBuffer", "base64"],
    BASE64_ENCODE_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""), blocks = [],
    buffer8;
Md5.prototype.update = function (t) {
    if (!this.finalized) {
        var e, n = typeof t;
        if ("string" !== n) {
            if ("object" !== n)
                throw ERROR;
            if (null === t)
                throw ERROR;
            if (ARRAY_BUFFER && t.constructor === ArrayBuffer)
                t = new Uint8Array(t);
            else if (!Array.isArray(t) && (!ARRAY_BUFFER || !ArrayBuffer.isView(t)))
                throw ERROR;
            e = !0
        }
        var i, r, o = 0, a = t.length, s = this.blocks, c = this.buffer8;
        while (o < a) {
            if (this.hashed && (this.hashed = !1,
                s[0] = s[16],
                s[16] = s[1] = s[2] = s[3] = s[4] = s[5] = s[6] = s[7] = s[8] = s[9] = s[10] = s[11] = s[12] = s[13] = s[14] = s[15] = 0),
                e)
                if (ARRAY_BUFFER)
                    for (r = this.start; o < a && r < 64; ++o)
                        c[r++] = t[o];
                else
                    for (r = this.start; o < a && r < 64; ++o)
                        s[r >> 2] |= t[o] << SHIFT[3 & r++];
            else if (ARRAY_BUFFER)
                for (r = this.start; o < a && r < 64; ++o)
                    i = t.charCodeAt(o),
                        i < 128 ? c[r++] = i : i < 2048 ? (c[r++] = 192 | i >> 6,
                            c[r++] = 128 | 63 & i) : i < 55296 || i >= 57344 ? (c[r++] = 224 | i >> 12,
                            c[r++] = 128 | i >> 6 & 63,
                            c[r++] = 128 | 63 & i) : (i = 65536 + ((1023 & i) << 10 | 1023 & t.charCodeAt(++o)),
                            c[r++] = 240 | i >> 18,
                            c[r++] = 128 | i >> 12 & 63,
                            c[r++] = 128 | i >> 6 & 63,
                            c[r++] = 128 | 63 & i);
            else
                for (r = this.start; o < a && r < 64; ++o)
                    i = t.charCodeAt(o),
                        i < 128 ? s[r >> 2] |= i << SHIFT[3 & r++] : i < 2048 ? (s[r >> 2] |= (192 | i >> 6) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | 63 & i) << SHIFT[3 & r++]) : i < 55296 || i >= 57344 ? (s[r >> 2] |= (224 | i >> 12) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | i >> 6 & 63) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | 63 & i) << SHIFT[3 & r++]) : (i = 65536 + ((1023 & i) << 10 | 1023 & t.charCodeAt(++o)),
                            s[r >> 2] |= (240 | i >> 18) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | i >> 12 & 63) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | i >> 6 & 63) << SHIFT[3 & r++],
                            s[r >> 2] |= (128 | 63 & i) << SHIFT[3 & r++]);
            
        }
        return this.bytes > 4294967295 && (this.hBytes += this.bytes / 4294967296 << 0,
            this.bytes = this.bytes % 4294967296),
            this
    }
}

function Md5(t) {
    if (t)
        blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0,
            this.blocks = blocks,
            this.buffer8 = buffer8;
    else if (ARRAY_BUFFER) {
        var e = new ArrayBuffer(68);
        this.buffer8 = new Uint8Array(e),
            this.blocks = new Uint32Array(e)
    } else
        this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0,
        this.finalized = this.hashed = !1,
        this.first = !0
}


Md5.prototype.hex = function () {
    this.finalize();
    var t = this.h0
        , e = this.h1
        , n = this.h2
        , i = this.h3;
    return HEX_CHARS[t >> 4 & 15] + HEX_CHARS[15 & t] + HEX_CHARS[t >> 12 & 15] + HEX_CHARS[t >> 8 & 15] + HEX_CHARS[t >> 20 & 15] + HEX_CHARS[t >> 16 & 15] + HEX_CHARS[t >> 28 & 15] + HEX_CHARS[t >> 24 & 15] + HEX_CHARS[e >> 4 & 15] + HEX_CHARS[15 & e] + HEX_CHARS[e >> 12 & 15] + HEX_CHARS[e >> 8 & 15] + HEX_CHARS[e >> 20 & 15] + HEX_CHARS[e >> 16 & 15] + HEX_CHARS[e >> 28 & 15] + HEX_CHARS[e >> 24 & 15] + HEX_CHARS[n >> 4 & 15] + HEX_CHARS[15 & n] + HEX_CHARS[n >> 12 & 15] + HEX_CHARS[n >> 8 & 15] + HEX_CHARS[n >> 20 & 15] + HEX_CHARS[n >> 16 & 15] + HEX_CHARS[n >> 28 & 15] + HEX_CHARS[n >> 24 & 15] + HEX_CHARS[i >> 4 & 15] + HEX_CHARS[15 & i] + HEX_CHARS[i >> 12 & 15] + HEX_CHARS[i >> 8 & 15] + HEX_CHARS[i >> 20 & 15] + HEX_CHARS[i >> 16 & 15] + HEX_CHARS[i >> 28 & 15] + HEX_CHARS[i >> 24 & 15]



Md5.prototype.finalize = function () {
    if (!this.finalized) {
        this.finalized = !0;
        var t = this.blocks
            , e = this.lastByteIndex;
        t[e >> 2] |= EXTRA[3 & e],
        e >= 56 && (this.hashed || this.hash(),
            t[0] = t[16],
            t[16] = t[1] = t[2] = t[3] = t[4] = t[5] = t[6] = t[7] = t[8] = t[9] = t[10] = t[11] = t[12] = t[13] = t[14] = t[15] = 0),
            t[14] = this.bytes << 3,
            t[15] = this.hBytes << 3 | this.bytes >>> 29,
            this.hash()
    }
}

function createOutputMethod(e) {
    return new Md5(!0).update(e)[t]()
}
function p() {
    var e, n, i, o, t = ((new Date).getTime() + "").slice(0, 10);
    return e = createOutputMethod(t),
        n = e.split(""),
        n.splice(2, 1, "e"),
        n.splice(6, 1, "h"),
        n.splice(12, 1, 6),
        n.splice(25, 1, "b"),
        i = n.join("") + t,
        o = g(i),
        o
}
console.log(p())在这里插入代码片

这样就可以得到函数p的变量e的值,在进行下面的分隔,替换等步骤最终得到变量i的值。然后又将i的值传入到函数g中对其进行加密操作。那么我们就进入到函数g中观察对其i做了那些操作。

function g(e) {
            var n = t.default.enc.Utf8.parse(e)
              , i = t.default.MD5(s)
              , o = t.default.AES.encrypt(n, i, {
                mode: t.default.mode.ECB,
                padding: t.default.pad.Pkcs7
            });
            return o.toString()
        }

这里t.default.enc.Utf8.parse(e)得到对初入参数e进行了从UTF8编码解析出原始字符串,t.default.MD5(s)观察发现这个s是一个固定值为‘GvcaHhBsKa9kkHmf’,对其进行了MD5加密,接下来先商量并确定好采用的 AES 的 vi (初始变量)、key(秘钥)、mode(加密模式)、padding(填充方式),这里的n就是vi初始变量,i就是key秘钥,对其进行ECB的加密模式以及Pkcs7进行填充。

需要介绍的是一个算法库crypto-js,是谷歌开发的一个纯JavaScript的加密算法类库,可以非常方便的在前端进行其所支持的加解密操作。目前crypto-js已支持的算法有:MD5,SHA-1,SHA-256,AES,Rabbit,MARC4,HMAC,HMAC-MD5,HMAC-SHA1,HMAC-SHA256,PBKDF2。常用的加密方式有MD5和AES。使用时可以引用总文件,也可以单独引用某一文件。安装命令为

npm install crypto-js

安装成功后将文件导入到pycharm中,在js代码中可以通过命令导入:

var CryptoJS = require("crypto-js")

接着替换函数g的代码,最终呈现:

var CryptoJS = require("crypto-js")

	function g(e) {
    var n = CryptoJS.enc.Utf8.parse(e)
        , i = CryptoJS.MD5(s)
        , o = CryptoJS.AES.encrypt(n, i, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    });
    return o.toString()
}

最后通过运行结果发现能够得到我们需要的ehsy-verify字段的值,如图:
在这里插入图片描述

接下来去验证,可以通过postman模拟接口请求或者直接编写爬虫代码验证,我直接编写爬虫代码实现验证。

3.、爬取数据

在得到ehsy-verify字段的值后,编写爬取代码就简单了,观察得到是POST请求,form表单提交请求数据,编写headers请求头,需要pyexecjs模块是python爬虫库里关于javaScript的一套程序,它能帮你解析python代码的js代码。 有经验的爬虫程序员应该知道,在你的请求头中有一部分是被js代码加密的,而这一套js加密程序就保存在你当前访问的网站中(事实上就是存在本地),每一次访问都需要调用js做加密再请求。这个机制可以抵挡大部分的爬虫程序,除非你模仿js加密程序之后再做请求。你可以模拟js程序写一段python程序,也可以直接把网页里的js代码复制下来,使用pyexecjs模块来运用。其最终实现部分爬虫代码如下:

    def request_goods_list(self, code):

        with open(r'E:\xiyu.js', 'r',encoding='utf-8') as r:
            js = r.read()
        jsdm = execjs.compile(js)
        result = jsdm.call('p')  #调用js文件中p函数,得到ehsy-verify值

        request_url = "https://m2.ehsy.com/pb/product/sku/desc"  #请求接口

        headers = {
            'Accept': '*/*',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            # 'Content-Length': '32',
            'ehsy-verify':result,
            'content-type': 'application/x-www-form-urlencoded',
            'Origin': 'https://m.ehsy.com',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-site',
            'Referer': 'https://m.ehsy.com /',
            'Host': 'm2.ehsy.com',
            'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
        }

        formdata = dict(skuCode=code, cityId="321", t*o*k*e*n="",watermark="true" )

        meta={
            "code":code,
        }
        return scrapy.FormRequest(
            method="POST",
            url=request_url,
            headers=headers,
            meta=meta,
            formdata=formdata,  #表单提交
            callback=self.request_product_response,
            dont_filter=True
        )

最终实现爬取数据如图:

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JS逆向爬虫案例分享 的相关文章

  • 将函数应用于 3D numpy 数组

    我有一个来自 Image PIL Pillow 对象的 numpy 3D 数组 178 214 235 180 215 236 180 215 235 146 173 194 145 172 193 146 173 194 126 171
  • Node.JS Web 服务器中的安全性

    所以 我正在学习 Node JS 到目前为止我很喜欢它 我已经有几个项目在工作了 我想我可以在其中使用nodejs 不过 我担心安全问题 如果我使用 Node JS http 模块编写自定义 Web 服务器 我是否可能非常容易受到攻击 Ap
  • 解密Javascript源代码[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我已经编写了一段 JavaScrip
  • 使用 Pytest 捕获 SystemExit 消息

    我正在使用 pytest 编写测试 我遇到了一些函数抛出异常的情况SystemExit如果输入错误 终端上会显示一些错误消息 我想为以下情况编写测试SystemExit抛出并验证输出错误消息中是否有特定字符串 这是代码 def test v
  • 如何使 Loopback 模型事件起作用?

    我尝试过一个例子http apidocs strongloop com loopback model http apidocs strongloop com loopback model MyModel on changed functio
  • JavaScript 将 NULL 转换为 0

    我正在使用 jQuery 来获取元素的高度 但如果该元素不存在 以下代码将返回 NULL height menu li active ul height returns integer or null 这是一种跨浏览器安全的方法 可以使用以
  • 删除 CSS Transitionend 事件侦听器不起作用

    我在尝试删除 css Transitionend 事件侦听器时遇到问题 我可以添加监听器 e addEventListener transitionend function event transitionComplete event pr
  • 如何从 dataurl 在服务器上创建图像文件

    我有一个 dataurl 格式的图像 例如 data image jpeg base64 9j 4AAQSkZJRgABAQAAAQABAAD 2wBDAAMCAgMCAgMDAwMEAwME iiigAooooAKKKKACiiigAoo
  • 如何在 Android 设备(平板电脑和手机)方向更改时获得正确的窗口宽度

    我正在尝试使用 jquery 函数计算 Android 设备方向变化时的窗口宽度 window outerWidth true 此计算给出了两个方向变化的正确宽度iphone and ipad但在安卓中不行 如果我最初以横向模式或纵向模式加
  • 将回调函数与原型函数一起使用

    在执行回调时 我无法弄清楚如何传递对象方法而不是排序 通用原型 方法 function Client this name hello Client prototype apiCall function method params callb
  • 同步通用分析

    新的Universal Analytics重新引入了同步事件跟踪 https developers google com analytics devguides collection analyticsjs method reference
  • 使用 Java 进行 AES 加密并使用 Javascript 进行解密

    我正在制作一个需要基于 Java 的 AES 加密和基于 JavaScript 的解密的应用程序 我使用以下代码作为基本形式进行加密 public class AESencrp private static final String ALG
  • 无法使用 Python 3 编写的 gzip.open() 将压缩文件上传到云存储

    当我尝试在 Cloud Shell 实例上使用 python 脚本将压缩的 gzip 文件上传到云存储时 它总是上传一个空文件 这是重现错误的代码 import gzip from google cloud import storage s
  • JavaScript 不是 DOM 的一部分吗?

    为什么即使从 DOM 中删除用于创建脚本的代码 脚本仍然可以运行 我遇到了一种情况 我想阻止损坏的脚本运行 查看我的帖子 https stackoverflow com questions 2685581 is there a way to
  • 内联 YouTube 视频在 iOS 上的 cordova 应用程序中不起作用

    我用 cordova 开发了一个移动应用程序 我确实需要能够播放内联 YouTube 视频 我尝试了一段时间来解决它 我设置了属性playsinline to 1在 YouTube iframe API 中 I put
  • 使用自定义层运行 Keras 模型时出现问题

    我目前正在攻读学士学位论文FIIT STU https www fiit stuba sk en html page id 749 其主要目标是尝试复制和验证以下结果study http arxiv org abs 2006 00885 这
  • JavaScript 反静默技术来指示失败

    当错误确实发生并且函数无法继续执行时 在 JavaScript 中报告错误而不是依赖 null 和 undefined 是一个好方法 我可以想到三种方法 没做什么 抛出异常 assert 这是一个简单的示例场景 一个将传入的金额记入用户帐户
  • 1° 夏令时 Java 和 JS 表现出不同的行为

    假设巴西利亚 GMT 0300 夏令时于 21 10 2012 00 00 00 此时时钟应提前一小时 Java new Date 2012 1900 9 21 0 0 0 Sun Oct 21 01 00 00 BRST 2012 Chr
  • 获取不正确的日期,将时间戳转换为新日期

    我正在尝试将时间戳转换为日期 但得到的日期不正确 我正在开发一个使用 Angular 和 Typescript 的项目 我有这样的时间戳 1451642400 2016年1月1日 和1454320800 2016年2月1日 如果我编码 da
  • 关闭 IPython Notebook 中的自动保存

    我正在寻找一种方法来关闭 iPython 笔记本中的自动保存 我已经通过 Google Stack Overflow 搜索看到了有关如何打开自动保存的参考资料 但我想要相反的内容 关闭自动保存 如果这是可以永久设置的东西而不是在每个笔记本的

随机推荐

  • 服务器温度显示,服务器温度查看

    服务器温度查看 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 设备实时状态查询是检测设备在运行过程中的状态信息 用户可任
  • java 范围注解_Java-----注解

    1 什么是注解 Annotation是JDK5 0开始引入的新技术 Annotation的作用 不是程序本身 可以对程序作出解释 这一点 跟注释没什么区别 可以被其他程序 比如 编译器 读取 注解信息处理流程 是注解和注释的重大区别 Ann
  • Matlab矩阵基本操作(定义,运算)

    关注公众号 coding进阶 获取更多实战技术 一 矩阵的表示 在MATLAB中创建矩阵有以下规则 a 矩阵元素必须在 内 b 矩阵的同行元素之间用空格 或 隔开 c 矩阵的行与行之间用 或回车符 隔开 d 矩阵的元素可以是数值 变量 表达
  • Anaconda安装-超详细版(2023)

    Anaconda安装 超详细版 2023 前言 彻底卸载python Anaconda下载地址 安装详细步骤 配置环境变量 检验安装是否成功 更改conda源 后续安装第三方库可以加快速度 超详细彻底卸载Anaconda教程 Tensorf
  • 交互设计实用指南系列11-减少记忆负担

    http ued taobao org blog 2010 03 交互设计实用指南系列11 减少记忆负担 科普 辞海 中 记忆 的定义是 人脑对经验过的事物的识记 保持 再现或再认 记忆是人类一个非常重要的心理活动 它是人类很多其他思维活动
  • 【HJ72】百钱买百鸡问题

    题目描述 公元前五世纪 我国古代数学家张丘建在 算经 一书中提出了 百鸡问题 鸡翁一值钱五 鸡母一值钱三 鸡雏三值钱一 百钱买百鸡 问鸡翁 鸡母 鸡雏各几何 现要求你打印出所有花一百元买一百只鸡的方式 输入描述 输入任何一个整数 即可运行程
  • 从浏览器地址栏输入url到显示页面的步骤

    浏览器输入URL到渲染过程解析 从浏览器地址栏输入url到显示页面的步骤 从浏览器地址栏输入url到显示页面的步骤 目录 1 概述 2 DNS域名解析 3 三次握手与四次挥手 4 DOM CSSOM 渲染树的构建 渲染与绘制 1 概述 浏览
  • Mac里的airdrop传输文件

    01 两方打开 02 设置对所有人可以发现 03 右键需要传输的文件 04 选择共享 airdrop 05 点击页面上的名称 开始传输 note 可以传输 app文件 这样可以大大节省下载安装所要耗费的时间 word只要一分钟就能从从无到有
  • 机器学习_深度学习毕设题目汇总——图像生成

    下面是该类的一些题目 题目 基于生成式对抗网络的无监督图像生成研究 用于图像生成的机器学习算法在人像合成中的研究与应用 可抵抗相机抖动的高动态图像生成算法研究 图像生成诗歌的端到端模型研究 基于GANs的脑部MRI图像生成方法研究 基于GA
  • 问二十四:分类说明JUC包常用类有哪些?

    JUC中常用类汇总 JUC的atomic包下运用了CAS的AtomicBoolean AtomicInteger AtomicReference等原子变量类 JUC的locks包下的AbstractQueuedSynchronizer AQ
  • 【Ensemble Learning】第 3 章:混合模型

    在第2章中 您学习了如何以不同方式划分和混合训练数据以构建集成模型 其性能优于在未划分数据集上训练的模型 在本章中 您将学习不同的组装方法 与混合训练数据方法不同 混合模型方法在不同的机器学习模型中使用相同的数据集 然后以不同的方式组合结果
  • pads win10显示不全

    打开注册表 WIN R 1 路径 HKEY LOCAL MACHINE SOFTWARE Microsoft Windows NT CurrentVersion Fonts 将原来的 Microsoft YaHei TrueType Mic
  • C++ Vector容器的push_back( )与pop_back( )函数

    C Vector容器的push back 与pop back 函数 push back pop back 参考链接 push back 函数将一个新的元素加到vector的最后面 位置为当前最后一个元素的下一个元素 push back 在V
  • java IDEA加密/解密 源码

    package tool import org apache commons codec binary Base64 import org bouncycastle jce provider BouncyCastleProvider imp
  • Git如何Check Out出指定文件或者文件夹

    原文 http www handaoliang com a 20140506 195406 html 在进行项目开发的时候 有时候会有这样的需求那就是 我们只希望从Git仓库里取指定的文件或者文件夹出来 在SVN里面 这非常容易实现 因为S
  • 实训9——门磁报警

    实验九 门磁报警 一 实验目的 通过门磁传感器 判断三种开门方式 1 正常开门 就是有正常开门方式 例指纹开门 蓝牙开门后 主人打开门 触发门磁 2 门未关好 在正常开门后 主人没有即使关门 会引发门未关好报警 3 有人撬门 非正常开门 即
  • [Python人工智能] 十五.无监督学习Autoencoder原理及聚类可视化案例详解

    从本专栏开始 作者正式研究Python深度学习 神经网络及人工智能相关知识 前一篇文章详细讲解了循环神经网络LSTM RNN如何实现回归预测 通过sin曲线拟合实现如下图所示效果 本篇文章将分享无监督学习Autoencoder的原理知识 然
  • 用户增长漫谈二

    除了功能迭代 增量开发配套敏捷理念 这里重要谈一个概念 宏观 微观上都有可取之处 品牌架构 反应企业意愿 个性 未来可能性留存 兼容不同利益群体方式进程商业活动 主品牌 标识性弱 但是个性强烈 业务 产品特性底的处理方式 以资金 实力彰显
  • MyBatis 学习笔记(八)---源码分析篇--SQL 执行过程详细分析

    前言 在面试中我们经常会被到MyBatis中 占位符与 占位符的区别 大多数的小伙伴都可以脱口而出 会对值进行转义 防止SQL注入 而 则会原样输出传入值 不会对传入值做任何处理 本文将通过源码层面分析为啥 可以防止SQL注入 源码解析 首
  • JS逆向爬虫案例分享

    某域网站数据爬取之反爬策略JS逆向分析 本次分享解析某域网站数据的反爬机制 此次只做技术分享 如有侵权 请联系删除 1 分析网 首先打开网站发送请求 点击F12 发送请求发现电脑端并没有接口返回数据 即返回切换手机端观察 点解F12观察找到