爬虫补环境案例---问财网(rpc,jsdom,代理,selenium)

目录

一.环境检测

[1. 什么是环境检测](#1. 什么是环境检测)

2.案例讲解

[二 .吐环境脚本](#二 .吐环境脚本)

[1. 简介](#1. 简介)

[2. 基础使用方法](#2. 基础使用方法)

3.数据返回

[4. 完整代理使用](#4. 完整代理使用)

[5. 代理封装](#5. 代理封装)

[6. 封装所有使用方法](#6. 封装所有使用方法)

jsdom补环境

[1. 环境安装](#1. 环境安装)

[2. 基本使用](#2. 基本使用)

[3. 添加参数形式](#3. 添加参数形式)

Selenium补环境

[1. 简介](#1. 简介)

2.实战案例

[1. 逆向目标](#1. 逆向目标)

[2. 实现代码](#2. 实现代码)

3.实现接口

1.实际使用

RPC

[1. RPC 简介](#1. RPC 简介)

补环境案例

找加密位置

开始补环境

方法1:传统补

方法二-jsdom

方法三----利用selenium补环境

方法四:rpc

一.环境检测

1. 什么是环境检测
  • 由于浏览器和node的差别,会导致浏览器的js代码在node没有办法执行,js代码会根据浏览器的这些属性来判断你是不是在真正的浏览器执行的代码,要不是正确的浏览器环境则不会返回正确的数据信息.

  • 拿到代码在node里面执行、经常看到这一类型的错误,提示xxx未定义,其实这一块就是浏览器对象的一些特征

    if (navigator['userAgent']){
    ^

    ReferenceError: navigator is not defined

2.案例讲解
  • 检测执行代码是否存在navigator, 可以通过补空的方式

    navigator = {}
    navigator.userAgent = '11111'

    function ps(){
    if (navigator['userAgent']){
    return 'hello world'
    } else {
    return '失败'
    }
    }

    console.log(ps());

复制代码
  • 检测属性长度,会根据长度来判断你的数据是否正确,是不是一个空数据

    location = {}
    location.href = '123123'
    function ps(){
    if (location['href'].length > 3){
    return 'hello world'
    } else {
    return '失败'
    }
    }

    console.log(ps());

  • js异常代码捕获,很多情况下可能js代码会把异常给捕获掉导致我们结果不对

  • 可以输出异常捕获的内容, 或者可以直接把异常捕获的代码直接删除,把错误暴露出来

    location = {}
    location.host = '12334'
    navigator = {}
    navigator.userAgent = '1231234'
    function pn() {
    // try {
    verify_local()
    if (navigator['userAgent']) {
    return 'hello world'
    }
    // } catch (e) {
    // console.log(e)
    // return '错误的数据'
    // }
    }

    function verify_local() {
    if (location.host.length > 2) {
    return 'xxx'
    }
    }

    console.log(pn());

  • 浏览器和node环境差异

  • 在 Node.js 中,exports 是一个用于导出模块中的函数、对象、变量等的对象。

  • 浏览器是undefined

  • 可以删除, 或者可以修改的判断成功

    // 浏览器和 node差异
    sss = "undefined" != typeof exports ? exports : void 0
    console.log(typeof sss);

  • global检测

复制代码
glb= "undefined" == typeof window ? global:window

二 .吐环境脚本

1. 简介

Proxy可以理解为,在目标对象之前设一层"拦截",外界对该对象的访问,都必须通过这层拦截,可以对外界的访问进行过滤和改写(表示可以用它"代理"某些操作,可以翻为"代理器")。

api地址: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Prox

Proxy对象由两个部分组成:target、handler

target:目标对象handler:是一个对象,声明了代理target的指定行为,支持的拦截操作,一共13种:

  • get(target,propKey,receiver):拦截对象属性的读取。
    • target: 目标对象
    • propKey: 被获取的属性名。
    • receiver: Proxy 或者继承 Proxy 的对象
  • set(target,propKey,value,receiver):拦截对象属性的设置,返回一个布尔值(修改成功)。
    • target: 目标对象
    • propKey : 被获取的属性名。
    • value: 新属性值。
    • receiver: Proxy 或者继承 Proxy 的对象

一般的补环境的是通过运行程序后的undefined报错去一点一点分析,一点一点的去补一些环境,是非常掉头发的。

所以我们使用 Proxy 对全局遍历window、document、navigator等常见环境检测点进行代理,拦截代理对象的读取、函数调用等操作,并通过控制台输出,这样的话我们就能够实现检测环境自吐的功能,后续我们再针对吐出来的环境统一的进行补环境,这样就会方便的多。

2. 基础使用方法
var target = {
    name: 'JACK',
    age: 18,
};
​
var p = new Proxy(target, {
​
    get: function (target, propertyKey, receiver) {
        // 1 原对象
        // 2 访问属性
        // 3 代理器处理对象
        console.log(target, propertyKey, receiver)
    },
    set: function(target,propertyKey,value,receiver){
        // 1. 原对象
        // 2. 设置的属性
        // 3. 设置的值
        // 4. 代理器代理的对象
        console.log(target, propertyKey, value, receiver)
    }
})
p.age
p.user = 'aa'

注意:

我们现在写的代码,已经能够去拦截到取值和设置的操作,但是这个代码会打乱代码的后续运行,还并没有把对应的操作作用在原对象上,怎么解决呢?

3.数据返回
Reflect.get(target, propertyKey, receiver); //查找并返回target对象的name属性,receiver绑定this
Reflect.set(target, propertyKey, value, receiver); //设置target对象的name属性等于value

Reflect.set(target, name, value, receiver) 是 JavaScript 中的 Reflect 对象的一个方法。它用于设置指定对象的属性值,并返回一个布尔值,表示设置是否成功。

参数的含义如下:

  • target:要设置属性值的目标对象。
  • propertyKey:要设置的属性名。
  • value:要设置的属性值。
  • receiver(可选):设置属性时绑定的 this 值。
4. 完整代理使用
var target = {
    name: 'JACK',
    age: 18,
};
​
var p = new Proxy(target, {
​
    get: function (target, propertyKey, receiver) {
        temp = Reflect.get(target, propertyKey, receiver); //查找并返回target对象的name属性,receiver绑定this
        // 1 原对象
        // 2 访问属性
        // 3 代理器处理对象
        // console.log(target, propertyKey, receiver)
        console.log(`对象${target}--> get了属性--> ${propertyKey} 值是--> ${temp}`);
        return temp
    },
    set: function(target,p,value,receiver){
        temp = Reflect.set(target, p, value, receiver);
        // 1. 原对象
        // 2. 设置的属性
        // 3. 设置的值
        // 4. 代理器代理的对象
        // console.log(target, propertyKey, value, receiver)
        console.log("set: ", target, p, target[p]);
        return temp
    }
})
// console.log(p.age);
p.user = 'aa'
console.log(p.user)
​
复制代码
5. 代理封装
// 目标对象(被代理对象)
var target = {
    name: 'JACK',
    age: 18,
    lili:{
        zs:'nana'
        
    }
};
​
function XlProxy(obj,name){
    return new Proxy(obj,{
        get(target, p, receiver) {
            temp = Reflect.get(target,p,receiver)
            console.log(`对象${name}--> get了属性--> ${p} 值是--> ${temp}`);
            if (typeof temp == 'object'){
                 // 对于对象套对象进行挂代理
                 temp = XlProxy(temp,name + '-->' + p)
            }
            return temp
        }
    })
}
sss = XlProxy(target,'target')
sss.name
sss.lili.zs
6. 封装所有使用方法
// 代理器封装
function get_enviroment(proxy_array) {
    for(var i=0; i<proxy_array.length; i++){
        handler = '{\n' +
            '    get: function(target, property, receiver) {\n' +
            '        console.log("方法:", "get  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            '        return target[property];\n' +
            '    },\n' +
            '    set: function(target, property, value, receiver) {\n' +
            '        console.log("方法:", "set  ", "对象:", ' +
            '"' + proxy_array[i] + '" ,' +
            '"  属性:", property, ' +
            '"  属性类型:", ' + 'typeof property, ' +
            // '"  属性值:", ' + 'target[property], ' +
            '"  属性值类型:", typeof target[property]);\n' +
            '        return Reflect.set(...arguments);\n' +
            '    }\n' +
            '}'
        eval('try{\n' + proxy_array[i] + ';\n'
        + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
        + proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
    }
}
proxy_array = ['window', 'document', 'location', 'navigator', 'history','screen']
get_enviroment(proxy_array)
复制代码

jsdom补环境

参考地址:Create new page · jsdom/jsdom Wiki · GitHub中文文档

jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOMHTML 标准,用于在nodejs中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界的Web应用程序

1. 环境安装
复制代码
npm install jsdom --save
2. 基本使用
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
title = dom.window.document.querySelector("p").textContent
console.log(title)
3. 添加参数形式
const dom = new JSDOM(``, {
  url: "http://q.10jqka.com.cn/",
  referrer: "http://q.10jqka.com.cn/",
  contentType: "text/html",
  includeNodeLocations: true,
  storageQuota: 10000000
});

Selenium补环境

1. 简介

Selenium就是一个真实的环境地址,对于我们拿下来的js代码,在node是需要补环境的,但是在浏览器去执行的话,他就是一个真实的浏览器环境,所以可以节省我们扣代码的时间,我们可以把扣下来的代码直接用Selenium来进行访问

2.实战案例
1. 逆向目标
2. 实现代码
  • 我们把同花顺的js代码直接放到html文件
  • python代码
复制代码
import os
from selenium import webdriver
​
PRO_DIR = os.path.dirname(os.path.abspath(__file__))
def driver_sig(html_file):
    option = webdriver.ChromeOptions()
    option.add_argument('--disable-blink-features=AutomationControlled')
    option.add_argument('headless')
    driver = webdriver.Chrome(options=option)
    driver.get(PRO_DIR +'\\'+ html_file)
    # time.sleep(2)
    # sig = driver.execute_script('return window.aaa()')
    # print(sig)
    return driver
​
html_file = 'index.html'
driv = driver_sig(html_file)
​
print(driv.execute_script('return window.aaa()'))
​
3.实现接口
pip install flask
from flask import Flask
​
# 创建 Flask 应用实例
app = Flask(__name__)
​
# 定义路由和视图函数
@app.route('/')
def hello():
    return 'Hello, Flask!'
​
# 启动应用
if __name__ == '__main__':
    app.run()
  • Flask 是一个基于 Python 的轻量级、简单易用的 Web 应用框架。它提供了一个灵活且容易扩展的方式来构建 Web 应用程序。以下是一个简单的示例展示了如何使用 Flask 框架创建一个简易的 Web 应用:
  • 在上述示例中,我们首先导入了 Flask 模块,并创建了一个 Flask 应用实例 app。然后,使用 @app.route() 装饰器定义了一个路由以及对应的视图函数。在本例中,根路由 '/' 对应的视图函数是 hello(),它返回了一个简单的字符串 'Hello, Flask!'。最后,通过调用 app.run() 来启动应用。
  • 要运行这个应用,你需要确保已经安装了 Flask 模块。运行应用后,在浏览器中访问 http://localhost:5000/,你将看到输出的 'Hello, Flask!'。
1.实际使用
# -*- coding: utf-8 -*-
​
from flask import Flask, request
​
from selenium import webdriver
import os
from selenium.webdriver.common.by import By
# pip install flask
from flask import Flask, jsonify
​
PRO_DIR = os.path.dirname(os.path.abspath(__file__))
​
​
def driver_sig(html_file):
    option = webdriver.ChromeOptions()
    option.add_argument('--disable-blink-features=AutomationControlled')
    option.add_argument('headless')
    driver = webdriver.Chrome(options=option)
    driver.get(PRO_DIR + '\\' + html_file)
    # time.sleep(2)
    # sig = driver.execute_script('return window.aaa()')
    # print(sig)
    return driver
​
​
html_file = 'index.html'
driv = driver_sig(html_file)
​
# 创建 Flask 应用实例
app = Flask(__name__)
​
​
# 定义路由和视图函数
@app.route('/s', methods=['get', 'post'])
def hello():
    context = {
        # 加载本地地址 生成cookie值
        'v': driv.execute_script('return window.aaa()')
    }
    # 返回cookie值
    return jsonify(context=context)
​
​
​
# 启动应用
if __name__ == '__main__':
    app.run()
​

RPC

1. RPC 简介

为什么要使用RPC技术呢?我们在使用websocket时候可以发现,python在操作的时候,需要创建连接,还需要不断去接受传递数据,非常的麻烦, 那这个时候rpc技术可以帮助到我们,简单来说就是网页直接和rpc服务器进行交互,我们python可以直接调用,rpc暴露的接口,不需要关心,创建连接这一块的问题.

RPC 技术是非常复杂的,对于我们搞爬虫、逆向的来说,不需要完全了解,只需要知道这项技术如何在逆向中应用就行了。

RPC 在逆向中,简单来说就是将本地和浏览器,看做是服务端和客户端,二者之间通过 WebSocket 协议进行 RPC 通信,在浏览器中将加密函数暴露出来,在本地直接调用浏览器中对应的加密函数,从而得到加密结果,不必去在意函数具体的执行逻辑,也省去了扣代码、补环境等操作,可以省去大量的逆向调试时间。

补环境案例

案例站:688262.SH - 同花顺问财

接口:

找加密位置

xhr断点发现段不住,原因就是他不是ajax发包的

请求堆栈里面断点

这个地方的接口是公用的

往上多看几个站,发现是在这里弄的

但是发现这里也是公用的多过几个

这个时候并没有加密,逐行运行

f10一次之后发现就加密了

所以是 document.getElementsByTagName("head")[0].appendChild(d),加密的

f11进去

r是script标签

所以加密的方法就在

L方法里面

把整个文件扣下来

因为他是动态的,所以我们请求这个文件,在改代码

网站的调用方式是

复制代码
 var d = document.createElement("script");
                    e.cache || n.cache ? d.setAttribute("src", "" + o) : (o += -1 === o.indexOf("?") ? "?" : "&",
                    d.setAttribute("src", "" + o + s + "=" + c)),
                    e.charset && d.setAttribute("charset", e.charset),
                    d.id = f,
                    document.getElementsByTagName("head")[0].appendChild(d),

我们也这么干

复制代码
 var d = document.createElement("script");
 // # e.cache || n.cache ? d.setAttribute("src", "" + o) : (o += -1 === o.indexOf("?") ? "?" : "&",
  d.setAttribute("src","https://d.10jqka.com.cn/v6/line/17_688262/01/today.jscallback=quotebridge_v6_line_17_688262_01_today"),
  // #e.charset && d.setAttribute("charset", e.charset),
  d.id ="callback_quotebridge_v6_line_17_688262_01_today",
  document.getElementsByTagName("head")[0].appendChild(d)

那我们在浏览器运行成功了,下一步放在node.js里面

运行

开始补环境

方法1:传统补

对比发现真实的环境中q是ture,所以这里逻辑有问题,找到赋值的位置

方法二-jsdom

我们在开头加入

const jsdom = require("jsdom");
const {JSDOM} = jsdom;
var dom = new JSDOM('<!DOCTYPE html><p>hello world</p>')
window = dom.window
document = dom.window.document
Document=dom.window.Document
// console.log(dom.window.document)
navigator = dom.window.navigator
Element=dom.window.Element
// Element =function Element(){}
Element.prototype.insertBefore=function insertBefore(d){
    window.perry=d.src

}

location=dom.window.location
location.href='https://www.iwencai.com/unifiedwap/result?w=688262.SH%20&querytype=stock'
location.hostname='www.iwencai.com'
location.host='www.iwencai.com'
location.protocol='http:'

navigator.userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0'

Document.prototype.getElementsByTagName=function getElementsByTagName(){
    return [{
        appendChild:function (d){
            Element.prototype.insertBefore(d)

        }
    }]
}

运行

发现成功,补完了

方法三----利用selenium补环境

运行

也成功了

方法四:rpc

利用rpc技术更快捷,具体使用在我其他blog里面,具体看
https://t.zsxq.com/lMqck

相关推荐
开心工作室_kaic10 分钟前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā10 分钟前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
向宇it11 分钟前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康16 分钟前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神1 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
宅小海1 小时前
scala String
大数据·开发语言·scala
qq_327342731 小时前
Java实现离线身份证号码OCR识别
java·开发语言
锅包肉的九珍1 小时前
Scala的Array数组
开发语言·后端·scala
心仪悦悦1 小时前
Scala的Array(2)
开发语言·后端·scala
沉默璇年2 小时前
react中useMemo的使用场景
前端·react.js·前端框架