(笔记)数据采集基础09

1.获取原型对象

Object.getPrototypeOf

方法在 JavaScript 中用于获取指定对象的原型对象,即对象链中的下一个链接。原型对象是对象继承属性和方法的来源。这个方法对于理解对象的继承结构和访问对象原型上的属性和方法非常有用。

举例:

// 构造函数
function Animal(name) {
  this.name = name;
}

// 添加一个方法到 Animal 原型上
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound.');
};

// 使用构造函数创建一个实例
const animal = new Animal('Generic animal');

// 获取 animal 实例的原型对象
const proto = Object.getPrototypeOf(animal);

// 由于 Animal.prototype 被设置为 animal 的原型,proto 将指向 Animal.prototype
console.log(proto === Animal.prototype); // 输出: true

// 调用通过原型继承的 speak 方法
proto.speak.call(animal); // 输出: Generic animal makes a sound.

// 创建另一个对象,使用 Object.create 方法设置其原型为 animal 实例
const anotherAnimal = Object.create(animal);

// 此时,anotherAnimal 的原型是 animal 实例
const anotherProto = Object.getPrototypeOf(anotherAnimal);
console.log(anotherProto === animal); // 输出: true

// 由于 anotherAnimal 是以 animal 为原型创建的,它不会继承 Animal.prototype 上的方法
anotherProto.speak.call(anotherAnimal); // 输出: undefined makes a sound.

在这个例子中,我们首先定义了一个 Animal 构造函数,并为其原型添加了一个 speak 方法。然后,我们使用 new 关键字创建了一个 animal 实例。

通过 Object.getPrototypeOf 方法,我们获取了 animal 实例的原型对象,它指向 Animal.prototype。因此,当我们调用 proto.speak.call(animal) 时,它能够正确地输出 animal 实例的名字和它发出的声音。

接下来,我们使用 Object.create 方法创建了一个新的对象 anotherAnimal,其原型被设置为 animal 实例。这意味着 anotherAnimal 继承自 animal 实例,而不是 Animal.prototype。当我们尝试通过 anotherProto.speak.call(anotherAnimal) 调用 speak 方法时,由于 animal 实例上没有 speak 方法,所以输出为 undefined makes a sound.

这个例子展示了 Object.getPrototypeOf 方法如何帮助我们访问和理解对象的原型链,以及如何通过原型链访问对象的属性和方法。

 2.设置原型对象

Object.setPrototypeOf

在 JavaScript 中用于设置一个对象的原型(即内部的 [[Prototype]] 属性)。这个方法通常用于改变对象的继承链,或者将一个对象的原型设置为 null 来防止进一步的原型继承。

举例:

// 构造函数
function Animal(name) {
  this.name = name;
}

// 添加一个方法到 Animal 原型上
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound.');
};

// 使用构造函数创建一个实例
const animal = new Animal('Generic animal');

// 创建另一个构造函数
function Vehicle(make, model) {
  this.make = make;
  this.model = model;
}

// 添加一个方法到 Vehicle 原型上
Vehicle.prototype.describe = function() {
  console.log('This is a ' + this.make + ' ' + this.model + ' vehicle.');
};

// 使用构造函数创建一个 Vehicle 实例
const car = new Vehicle('Toyota', 'Corolla');

// 现在,我们将 car 实例的原型设置为 animal 实例
// 这意味着 car 将继承 animal 实例的所有属性和方法
Object.setPrototypeOf(car, animal);

// 验证原型链是否已改变
console.log(Object.getPrototypeOf(car) === animal); // 输出: true

// 由于 car 的原型现在是 animal,它继承了 speak 方法
car.speak(); // 输出: Generic animal makes a sound.

// 为了演示,我们也可以将 car 的原型设置为 null,这样它就不再有原型
Object.setPrototypeOf(car, null);

// 再次验证原型链
console.log(Object.getPrototypeOf(car) === null); // 输出: true

// 现在 car 没有原型,因此不能访问 speak 方法
car.speak(); // 输出: TypeError: car.speak is not a function

在这个例子中,我们首先定义了两个构造函数 AnimalVehicle,并分别为它们的原型添加了方法 speakdescribe。然后,我们分别使用这两个构造函数创建了 animalcar 实例。

接下来,我们使用 Object.setPrototypeOf 方法将 car 实例的原型设置为 animal 实例。这意味着 car 现在继承了 animal 实例的所有属性和方法。我们通过调用 car.speak() 来验证这一点,它输出了 animal 实例的名字和声音。

最后,我们再次使用 Object.setPrototypeOf 方法将 car 的原型设置为 null,这意味着 car 将不再继承任何属性和方法。当我们尝试调用 car.speak() 时,由于 car 没有 speak 方法,因此抛出了一个类型错误。

这个例子展示了 Object.setPrototypeOf 方法如何用于修改对象的原型链,以及如何通过改变原型来控制对象继承的属性和方法

 3.获取所有当前对象属性

Object.getOwnPropertyNames

Object.getOwnPropertyNames 方法在 JavaScript 中用于获取一个对象的所有可枚举的自有属性的名称,包括那些不可枚举的属性。这个方法返回一个字符串数组,数组中的每个元素都是对象的一个属性名。

这个方法对于理解对象的内部属性非常有用,尤其是当你想要获取对象所有的属性,包括那些可能在常规枚举中被忽略的属性时。

举例:

// 定义一个对象
const myObject = {
  enumerableProperty: 'I am enumerable',
  nonEnumerableProperty: 'I am not enumerable'
};

// 添加一个不可枚举的属性
Object.defineProperty(myObject, 'anotherNonEnumerableProperty', {
  value: 'Another non-enumerable property',
  enumerable: false
});

// 使用 Object.keys 获取对象的键(只能获取可枚举的键)
const keys = Object.keys(myObject);
console.log(keys); // 输出: ["enumerableProperty"]

// 使用 Object.getOwnPropertyNames 获取对象的所有自有属性名称
const allPropertyNames = Object.getOwnPropertyNames(myObject);
console.log(allPropertyNames); // 输出: ["enumerableProperty", "nonEnumerableProperty", "anotherNonEnumerableProperty"]

在这个例子中,我们首先定义了一个对象 myObject,它包含一个可枚举属性 enumerableProperty 和一个不可枚举属性 nonEnumerableProperty。然后,我们使用 Object.defineProperty 方法添加了另一个不可枚举属性 anotherNonEnumerableProperty

当我们使用 Object.keys 方法时,它只返回了可枚举的属性 enumerableProperty。然而,当我们使用 Object.getOwnPropertyNames 方法时,它返回了所有的自有属性名称,包括那些不可枚举的属性 nonEnumerablePropertyanotherNonEnumerableProperty

这个方法在调试或者当你需要分析一个对象的所有属性时非常有用,尤其是当你不确定对象中是否存在某些属性时。

4. 获取特殊属性

Object.getOwnPropertySymbols

在 JavaScript 中用于获取一个对象的所有自有符号属性(Symbol properties)。符号属性是一种特殊的属性,它通过 Symbol 函数创建,具有唯一性,即使两个不同的 Symbol 看起来可能相同,它们在技术上也是不同的。

这个方法对于处理那些希望避免属性名冲突的高级用例非常有用,因为 Symbol 值是唯一的,不会与其他属性名或 Symbol 值冲突。

// 创建一个对象
const myObject = {};

// 定义两个符号属性并添加到对象中
const sym1 = Symbol('uniqueDescription1');
const sym2 = Symbol('uniqueDescription2');

myObject[sym1] = 'This is a symbol property with description 1';
myObject[sym2] = 'This is another symbol property with description 2';

// 使用 Object.getOwnPropertyNames 获取对象的所有自有字符串属性名称
const stringPropertyNames = Object.getOwnPropertyNames(myObject);
console.log(stringPropertyNames); // 输出: []

// 使用 Object.getOwnPropertySymbols 获取对象的所有自有符号属性
const symbolProperties = Object.getOwnPropertySymbols(myObject);
console.log(symbolProperties); // 输出: [Symbol(uniqueDescription1), Symbol(uniqueDescription2)]

// 打印符号属性的描述和值
symbolProperties.forEach(sym => {
  console.log(`Symbol description: ${sym.description}, Symbol value: ${myObject[sym]}`);
});
// 输出:
// Symbol description: uniqueDescription1, Symbol value: This is a symbol property with description 1
// Symbol description: uniqueDescription2, Symbol value: This is another symbol property with description 2

在这个例子中,我们首先创建了一个空对象 myObject,然后定义了两个具有不同描述的符号,并使用这些符号作为属性名将它们添加到 myObject 中。我们还为这些符号属性赋予了字符串值。

当我们使用 Object.getOwnPropertyNames 方法时,它没有返回任何属性名称,因为 myObject 中没有可枚举的字符串属性。然而,当我们使用 Object.getOwnPropertySymbols 方法时,它返回了一个包含所有自有符号属性的数组。

最后,我们遍历了符号属性数组,并打印了每个符号的描述和对应的值。这展示了如何访问和使用符号属性,尽管在大多数情况下,我们更倾向于使用 Object.keysObject.getOwnPropertyNames 来处理常规属性。

5.获取可枚举属性 

Object.keys

Object.keys 方法在 JavaScript 中用于获取一个对象自身的所有可枚举属性的键名(属性名),并以数组的形式返回。这个方法通常用于遍历对象的属性,尤其是当你需要对对象的属性进行操作时。

// 创建一个对象,包含多个属性,其中一些属性是不可枚举的
const myObject = {
  enumerableProperty: 'This property is enumerable',
  nonEnumerableProperty: 'This property is not enumerable',
  anotherEnumerableProperty: 'Another enumerable property'
};

// 使用 Object.defineProperty 为对象添加一个不可枚举的属性
Object.defineProperty(myObject, 'anotherNonEnumerableProperty', {
  value: 'This is another non-enumerable property',
  writable: true,
  enumerable: false,
  configurable: true
});

// 使用 Object.keys 获取对象的可枚举属性键名
const keys = Object.keys(myObject);
console.log(keys); // 输出: ["enumerableProperty", "anotherEnumerableProperty"]

// 尝试使用 for...in 循环遍历对象的所有属性
for (const key in myObject) {
  console.log(key + ': ' + myObject[key]);
}
// 输出:
// enumerableProperty: This property is enumerable
// nonEnumerableProperty: This property is not enumerable
// anotherEnumerableProperty: Another enumerable property
// anotherNonEnumerableProperty: This is another non-enumerable property

// 说明:for...in 循环会枚举对象自身的属性以及其原型链上可枚举的属性

在这个例子中,我们首先创建了一个 myObject 对象,它包含一个可枚举属性 enumerableProperty,一个不可枚举属性 nonEnumerableProperty,以及一个通过 Object.defineProperty 添加的不可枚举属性 anotherNonEnumerableProperty

当我们使用 Object.keys 方法时,它只返回了可枚举的属性键名,即 enumerablePropertyanotherEnumerableProperty。不可枚举的属性 nonEnumerablePropertyanotherNonEnumerableProperty 没有被包含在内。

随后,我们使用 for...in 循环遍历对象的所有属性,包括可枚举和不可枚举的属性。这是因为 for...in 循环不仅枚举对象自身的属性,还会枚举其原型链上可枚举的属性。

通过这个例子,我们可以看到 Object.keys 方法如何用于获取对象的可枚举属性键名,这对于编写只处理可枚举属性的代码非常有用。同时,我们也了解了 for...in 循环与 Object.keys 方法在枚举属性方面的差异。

 6.冻结对象

Object.preventExtensions

Object.preventExtensions 方法在 JavaScript 中用于阻止一个对象被添加新的属性,即冻结对象。如果操作成功,该方法返回 true;如果对象已经是不可扩展的,或者操作失败(例如,对象是密封的),则返回 false

这个方法对于确保对象的完整性和安全性非常有用,尤其是在你不希望对象的结构在运行时被改变的情况下。

// 创建一个对象
const myObject = {
  property1: 42
};

// 检查对象是否可扩展
console.log(Object.isExtensible(myObject)); // 输出: true

// 阻止对象扩展新的属性
const success = Object.preventExtensions(myObject);
console.log(success); // 输出: true

// 尝试添加新的属性
myObject.property2 = 'Hello';
console.log(myObject.property2); // 输出: undefined

// 再次检查对象是否可扩展
console.log(Object.isExtensible(myObject)); // 输出: false

// 修改现有属性的值
myObject.property1 = 100;
console.log(myObject.property1); // 输出: 100

// 删除现有属性
delete myObject.property1; // 返回 true,表示删除成功
console.log(myObject.property1); // 输出: undefined

在这个例子中,我们首先创建了一个对象 myObject 并给它定义了一个属性 property1。使用 Object.isExtensible 方法检查对象是否可扩展,此时输出为 true,表示对象可以添加新属性。

接着,我们调用 Object.preventExtensions 方法来防止对象扩展新属性。尝试给对象添加一个新属性 property2,但是操作失败,新属性没有被添加到对象中,且 property2 的值为 undefined

尽管我们阻止了对象扩展新属性,但是现有的属性仍然可以被修改或删除。我们修改了 property1 的值,并成功将其删除。

这个例子展示了 Object.preventExtensions 方法如何用于控制对象的结构,防止对象被意外地修改,同时保持对现有属性的灵活性。

7.js常见语法 

>>>

JavaScript 中,三个大于号( >>> )是一种无符号右移位运算符。它将操作数的二进制表示向右移动指定的位数,右侧用零填充,然后返回结果。
let num = 16 >>> 2 ;
console . log ( num );
在这个例子中, 16 被右移两位,因此它的二进制表示变为了 100 (即 4 ),然后它被转换为十进制并赋给num变量。因此,这段代码将打印出 4
请注意,这个运算符与另一个右移位运算符( >> )不同,后者是有符号右移,它将在右侧使用符号位进行填充,这可能会导致负数的出现。

>>

JavaScript 中,双大于号( >> )是带符号的右位移运算符。这个运算符将一个数的二进制表示向右移动指定的位数,并用符号位(即正负号位)填充左侧空缺的位数。例如,对于二进制数101101 ,右移两位变为001011 ,因为符号位是 0 ,所以结果是正数 11 。如果原数是负数,则填充左侧空缺位数的符号位都是1 ,以保持负数的符号不变。
另外,双大于号( >> )也可用于将数字强制转换为 32 位有符号整数,因为 JavaScript 中的所有数字都是以双精度浮点数的形式存储的。在进行位移运算之前,双大于号(>> )会将数字强制转换为 32 位有符号整数。如果数字大于等于2 31 次方,则结果为负数,否则为正数。

 

var i = e + (x & _ | ~x & t) + (n >>> 0) + a;
这行代码是一个简单的加法表达式,其中包含了四个变量:
\1. e :一个数值类型的变量;
\2. x _ t :三个数值类型的变量,采用了位运算;
\3. n :一个无符号数值类型的变量,通过无符号右移运算符进行位移;
\4. a :一个数值类型的变量。
整个表达式可以拆成三个操作,分别是:
\1. x & _ ~x & t 的位运算结果,再相加;
\2. e 和表达式 1 的结果相加;
\3. 表达式 2 的结果和 n >>> 0 相加,再和 a 相加。
其中第 1 步中, & ~ 都是位运算符。 & 是按位与运算, ~ 是按位取反运算。这两个运算符通常用来进行位操作,比如对二进制值的某些位进行控制。
2 步简单的将 e 加上第 1 步的结果。第 3 步中, >>> 是无符号右移运算符,在进行右移运算时,系统首先给该数值添加了一个符号位,然后将其右移,最后将符号位去掉。使用 >>> 可以让位移得到无符号的结果值,即在高位总是填充零。
总之,这行代码是一条比较复杂的加法表达式,包含了位运算、无符号位移等操作。
JavaScript | || 是不同的操作符,它们的作用也不同。
| 是按位或运算符,用于对两个数的二进制位进行或运算,结果的每一位都是对应位置两数值为 1 时结果为 1 ,否则为 0
例如: 3 | 5 的结果为 7 ,因为 3 的二进制为 0011 5 的二进制为 0101 ,它们进行按位或运算后,
得到 0111 ,即 7
|| 是逻辑或运算符,用于进行逻辑判断。如果其中一个操作数是真值(除 false undefined null 0 、NaN、 '' 外的值),则结果为 true ;如果都不是真值,则结果为 false
例如: true || false 的结果为 true ,因为其中一个操作数为真值;而 0 || '' 的结果为 ''
因为两个操作数都不是真值。
JavaScript | || 是不同的操作符,它们的作用也不同。
| 是按位或运算符,用于对两个数的二进制位进行或运算,结果的每一位都是对应位置两数值为 1 时结果为 1 ,否则为 0
例如: 3 | 5 的结果为 7 ,因为 3 的二进制为 0011 5 的二进制为 0101 ,它们进行按位或运算后,
得到 0111 ,即 7
|| 是逻辑或运算符,用于进行逻辑判断。如果其中一个操作数是真值(除 false undefined null 0 、NaN、 '' 外的值),则结果为 true ;如果都不是真值,则结果为 false
例如: true || false 的结果为 true ,因为其中一个操作数为真值;而 0 || '' 的结果为 ''
因为两个操作数都不是真值。

特殊函数调用方式 

(0, a[i(_0x58e7("0x171"))])(u + f)
这里 a[i(_0x58e7("0x171"))] 是个混淆后的函数,这里实际上就是调用了这个函数,参数是 u+f 
e.headers = e.headers || {},
这段代码是在 JavaScript 中常用的一种写法,它的作用是创建一个空的对象,如果 e.headers 对象已经存在,则保留原有的对象;如果e.headers 对象不存在,则将一个新的空对象赋值给它。

 

function s ( n ) {
                    var n = new z [ M1 ]( T1 + n + C1 );
                    return ( n = z [ I ][ P1 ][ A1 ]( n )) ? z [ g2 ]( n [ 2 ]) : F
}
这段代码是一个 JavaScript 函数,它接受一个参数 n ,并对参数进行加密。具体来说,它将一个字符串"T1 + n + C1" 作为参数传递给 z 构造函数,创建一个新的 z 对象,并将其作为参数传递给 z 对象的 P1 方法。然后将结果赋值给变量n ,并检查变量 n 是否存在。如果 n 存在,则使用 z 对象的 g2 方法将 n 数组的第二个元素解密为一个字符串,并返回该字符串。如果n 不存在,则返回 F

 

s = b = 123
JavaScript 中,这行代码的含义是将数字 123 分别赋值给变量 b s 。换句话说,变量 b s 都被赋值为123。这种赋值方式称为链式赋值,其中多个变量可以在一行代码中同时进行赋值

 

一元正号运算符
一元正号运算符是一种用于将值转换为数值类型的 JavaScript 运算符。在 JavaScript 中,一元正号运算符使用加号 + 表示,其作用是将表达式转换为一个数值,例如:
let str = "123" ;
let num = + str ;
console . log ( num ); // 输出 123
在这个例子中,将字符串 "123" 赋值给变量 str ,然后使用一元正号运算符将其转换为一个数值,并将结果赋值给变量 num 。因为加号 + 可以将字符串解析为数值,所以变量 num 的值为 123
var e, r = +new Date() - (m || 0) - 1661224081041
这里 + 将时间转成数字可以用来计算,如果 m 存在就减 m 如果不存在就减 0

... 

8.例子 

福建资源平台

//npm install crypto-js 
const CryptoJS = require('crypto-js')

function bbb(t) {
    //"EB444973714E4A40876CE66BE45D5930"
    //"B5A8904209931867"
    var e = CryptoJS.enc.Utf8.parse("EB444973714E4A40876CE66BE45D5930")
      , n = CryptoJS.enc.Utf8.parse("B5A8904209931867")
      , a = CryptoJS.AES.decrypt(t, e, {
        iv: n,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return a.toString(CryptoJS.enc.Utf8)
}
function s(t, e) {
    return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() == e.toString().toUpperCase() ? 0 : -1
}
function l(t) {
    for (var e = Object.keys(t).sort(s), n = "", a = 0; a < e.length; a++)
        if (void 0 !== t[e[a]])
            if (t[e[a]] && t[e[a]]instanceof Object || t[e[a]]instanceof Array) {
                var i = JSON.stringify(t[e[a]]);
                n += e[a] + i
            } else
                n += e[a] + t[e[a]];
    return n
}
function ddd(t) {
    for (var e in t)
        "" !== t[e] && void 0 !== t[e] || delete t[e];
    var n = "B3978D054A72A7002063637CCDF6B2E5" + l(t);
    return CryptoJS.MD5(n).toString().toLowerCase();
}
// var ttt = {
//     "ts": 1714308237360,
//     "pageNo": 1,
//     "pageSize": 20,
//     "total": 3677,
//     "KIND": "GCJS",
//     "GGTYPE": "1",
//     "timeType": "6",
//     "BeginTime": "2023-10-28 00:00:00",
//     "EndTime": "2024-04-28 23:59:59",
//     "createTime": []
// }
// console.log(ddd(ttt))
import requests
import json
import zlib
# pip install PyExecJS
import execjs
import time
import hashlib
import base64
import os
import json
current_time = int(time.time()*1000)
def get_js():
    f = open("fjj.js", 'r',encoding='utf8')
    line = f.readline()
    htmlstr = ''
    while line:
        htmlstr = htmlstr+line
        line = f.readline()
    return htmlstr


def get_des_psswd(function_name,e):
    js_str = get_js()
    ctx = execjs.compile(js_str)
    #这里hello为js文件里的函数,e为向hello这个函数里传递的参数
    #这里我们的e这个形参主要用来传递歌曲的id,这样我们只需要向该函数传递不同的歌曲Id,即可返回不同的下载链接
    return (ctx.call(function_name,e))

for page in range(1,4):
    data = {
        "pageNo": page,
        "pageSize": 20,
        "total": 3679,
        "KIND": "GCJS",
        "GGTYPE": "1",
        "timeType": "6",
        "BeginTime": "2023-10-28 00:00:00",
        "EndTime": "2024-04-28 23:59:59",
        "createTime": [],
        "ts": current_time

    }
    data2 = {
        "pageNo": page,
        "pageSize": 20,
        "total": 3679,
        "AREACODE": "",
        "M_PROJECT_TYPE": "",
        "KIND": "GCJS",
        "GGTYPE": "1",
        "PROTYPE": "",
        "timeType": "6",
        "BeginTime": "2023-10-28 00:00:00",
        "EndTime": "2024-04-28 23:59:59",
        "createTime": [],
        "ts": current_time

    }
    sign = get_des_psswd('ddd',data2)
    print(sign)

    headers = {
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "Content-Type": "application/json;charset=UTF-8",
        "Origin": "https://ggzyfw.fj.gov.cn",
        "Pragma": "no-cache",
        "Referer": "https://ggzyfw.fj.gov.cn/business/list/",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
        "portal-sign": sign,
        "sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\""
    }
    print(headers)
    url = "https://ggzyfw.fj.gov.cn/FwPortalApi/Trade/TradeInfo"

    data2 = json.dumps(data2, separators=(',', ':'))
    response = requests.post(url, headers=headers, data=data2).json()
    print(response)
    response_text = response['Data']
    result = get_des_psswd('bbb',response_text)
    print(result)

  • 46
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值