JS进阶-面向对象-搭建网站-HTML与JS交互

JS进阶

文章目录

大纲

  • js逆向开篇
  • js基础
  • js进阶
  • js调试
  • 加密算法
  • 反调试
  • js混淆
  • 抠代码
  • webpack模块打包
  • cookie反爬
  • 浏览器环境补充
  • RPC远程调用
  • 常见协议
  • 验证码
  • 案例

作用域和闭包

  • 作用域
    • 全局作用域
    • 语句作用域
    • 块级作用域
  • 闭包

作用域

指的变量的作用范围

一般来说:块级作用域>语句作用域>全局作用域

块级作用域

a = 3

function test(a){
    var a = 1
    console.log(a)
}

test(2)

在这段代码中,一共有三个变量a

一个是块级变量a=1,也就是函数内部的变量,在大括号里面,是块级变量

一个是语句变量a=2,是函数被调用的时候传进去的参数a=2,这个是语句变量

一个是全局变量a=3,这个既不是在函数内部,也不是参数的变量,就是全局变量。在js里面不使用var关键字所创建的变量,默认是全局变量

这里发现输出的结果是1,就说明了块级作用域是最优先选择的

语句作用域

在调用test函数的时候传进去的参数,就是语句变量

var a = 3

function test(a){
    // var a = 1
    console.log(a)
}

test(2)

这里如果把块级变量a=1注释掉,就会发现输出的结果是2.

也说明了语句作用域优先于全局作用域

全局作用域

除了上面两种变量,就是全局变量了

a = 3

function test(){
    // var a = 1
    console.log(a)
}

test()

把语句变量去掉之后,就剩下全局变量a=3了

发现全局变量是最后被采用的

如果在块级作用域里面修改了全局变量,这个全局变量是真的会被改变

闭包

就是js 的局部作用域,也是c语言的类class的私有变量啥的

调用闭包的方法

全局变量

就是如何在外部调用闭包内部的方法

function test(){
    function num(){
        console.log("num");
    }
    a = num
}

test()
a()

这里是把闭包内部的函数名称赋值给全局变量a,我们需要先执行一次函数,在函数内部,把闭包内部的函数名传给全局变量,然后调用

一把这个调用test函数是写成自执行函数

(function test(){
    function num(){
        console.log("num");
    }
    a = num
})()

a()

就可以隐藏我们的一些操作

返回值

还可以是返回值的形式把内部的函数返回出来

b = function test(){
        function num(){
            console.log("num");
        }
        return num
    }

b()()

这里把test方法赋值给b,然后b执行一次得到内部的返回值num,再调用一次,就可以使用内部的函数num了

面向对象

就是用class创建的类,叫对象

首先是回忆一下python的class类

class Teach():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def getName(self):
        print(self.name,self.age)

xiaoming = Teach("xiaoming", 18)
xiaoming.getName()

使用init初始化类的成员变量,通过类函数getName输出成员变量的值

然后是js的对象

实例化构造函数

function Teach(name,age){
    this.name=name;
    this.age=age
    this.getName = function(){
        console.log(this.name,this.age)
    }
}
    
xiaoming = new Teach('XiaoMing',18)
xiaoming.getName()

在js里面,使用function关键字像创建函数一样创建对象的构造函数。不需要使用init就可以初始化成员变量。在js里面构造一个方法,也是使用函数名赋值匿名函数的方式构造出来。

跟c++里面的this指针一模一样。和python里面的self的用法一样

不同的是js里面实例化对象需要使用关键字new。

这个代码不是一个完整的对象,但是可以当成完整的对象去使用。他是对象的构造函数。和对象是有一定的区别。构造函数的prototype才是对象原型

添加对象的成员prototype

就是在已有的对象里面,再添加成员函数

function Teach(name,age){
    this.name=name;
    this.age=age
    this.getName = function(){
        console.log(this.name,this.age)
    }
}

Teach.prototype.major = function(hobby){
    this.hobby=hobby;
    console.log(`${this.name} like ${this.hobby}`);
}

xiaoming = new Teach('XiaoMing',18)
xiaoming.getName()
xiaoming.major('CT')

在这段代码9行,我们给原有的Teach对象原型增加了一个成员方法major,功能就是输出一下信息

proto和prototype

这里是对象原型,跟继承类的知识一样。

就是子类父类这些知识。

一般来说,对象有三层关系:实例化对象,对象原型,object对象

意思就是,我们自己设计了一个对象,这个设计的对象就是对象原型。我们中途增加对象的变量或者方法,都是在对象原型里面完成。

我们可以使用new方法,用对象原型创建一个对象。

因为是用对象原型创建的对象,所以,对象原型是对象的父类。

因此,再扩展一个概念object对象。这个是js所有变量或者对象的祖先。

比如,我们随便创建一个变量,或者创建一个数组,函数,对象。

都可以调用toString函数等等。这个toString函数就是object对象里面的函数。是js环境给你创建好的。

function test(){
    console.log("test");
}

console.log(test.toString())

不难发现,object是所有引用类型的父类。

使用__proto__函数,可以看到对象的父类

代码中:

  • Teach.prototypeTeach 构造函数的原型对象。
  • xiaomingTeach 构造函数的一个实例。
  • 由于 xiaoming 是通过 Teach 构造函数创建的,因此 xiaoming.__proto__Teach.prototype

这就是为什么我们可以说 Teach.prototype === xiaoming.__proto__。而 Teach 是构造函数的引用,它不是一个原型实例,所以 Teach === xiaoming.__proto__ 这个表达式是错误的。

理解原型和实例

  1. 构造函数与原型:
    当我们定义一个构造函数(例如 Teach),JavaScript 会为这个构造函数生成一个原型对象,这个对象是 Teach.prototype。所有通过这个构造函数创建的实例(例如 xiaoming)会自动通过其内部的 [[Prototype]] 属性(在现代 JavaScript 中被称为 __proto__)链接到这个原型对象。
  2. 实例与原型的关系:
    当你创建 xiaoming 的实例时,xiaoming__proto__ 会指向 Teach.prototype。所以可以说 xiaoming.__proto__ === Teach.prototype

对象原型的误解

我之前以为使用function创建的就是对象原型,问了才知道,使用function是创建了对象的构造函数,这个构造函数在使用的时候,js会偷偷的变成一个对象。也就是Teach.prototype才是对象原型。

我们能在对象原型里面直接添加方法。

所以使用构造函数实例化的对象的父类是对象原型,不是构造函数。

对象原型的父类是object类。object的父类是null空。object就是一切的根源了

console.log(Teach.prototype === xiaoming.__proto__)

原型链

通过proto的关系,连接而成的关系链,子类父类啥的关系。

proto是找父类。

this指向

在不同的环境有一定的区别

  • 浏览器环境
  • NodeJS环境

浏览器环境

  • window全局变量
    • innerHeight 显示浏览器的高度
    • location 请求头啥的信息
    • window.x === x 任何在浏览器里面创建的变量,都属于window下面
    • this 默认指向window,也是window变量的引用

我们在浏览器里面创建的任何变量任意的方法,都是在window之下

浏览器环境特有全局变量window,主要是浏览器的各种参数,窗口大小等等

在js环境里面没有window,Nodejs是后端,不涉及前端的页面,没有window全局变量

如果是浏览器环境里面创建了一个对象,在对象里面再创建一个方法,然后在方法里面调用this指针,这个this指针就指向对象了,不会指向window了

Node JS环境

在NodeJS环境里面全局变量是global,和浏览器里面的window一个等级

  • global全局变量
    • global.name === name 任意变量都是在global底下创建的
    • this 指向空
    • 在变量前面加关键字var 就不属于global了
    • 在任意函数里面调用this指针,指向global

就是说,在NodeJS环境里面调用this,是没有结果的,但是,随便创建一个函数,在函数里面调用this指针,this就指向global了

在NodeJS环境里面创建一个对象,在对象的方法里面调用this指针是指向对象自己的,不指向global

证明了this指向的是一个对象,如果不创建对象调用this,指向的就是global,如果创建对象,在对象里面调用this,指向的就是对象自己

JS逆向常见方法

  • call和apply
  • 箭头函数
  • 定时器
  • eval

call和apply

function Teach(name,age){
    this.name=name;
    this.age=age
    this.getName = function(){
        console.log(this.name,this.age)
    }
    this.major = function(hobby){
        this.hobby=hobby;
        console.log(`${this.name} like ${this.hobby}`);
    }
}

xiaoming = new Teach('XiaoMing',18)
xiaoming.major('python')
xiaoming.major.call(xiaoming,'[python]')
xiaoming.major.apply(xiaoming,['python'])

call和apply就是调用函数方法

正常来说,是对象调用方法,直接写参数。

call是,对象调用方法,再调用call方法,第一个参数,写this指向的对象,可以是对象自己,也可以是别的对象,后面依次写参数

apply,和call一样,就是参数不一样,apply第一个参数写this指向的对象,给对象方法里面传的参数要放在数组里面,一个参数也要写成数组的形式。

主要是第一个参数,可以自定义,不一定指向对象自己,也可以调用别的对象。这个就是call和apply与之前那种直接调用方法的不同

function test(){
    console.log("test");
}

// test();
test.apply(this)

也可以代替直接对函数的调用,就是函数名加apply方法,参数写this指针,这个指针没用,但是得有。

没用参数可以不写

箭头函数 ES6语法

箭头函数的创建

  • 无参数的箭头函数
fun1 = function() {
    console.log('f1')
}

fun2 = ()=> {
    console.log('f2')
}

fun1()
fun2()

对比fun1函数,发现,可以省略function关键字,需要在参数后面用等号加大于号代替

  • 一个参数的箭头函数
fun1 = function(data) {
    console.log(`${data}`);
}

fun2 = data => {
    console.log(`${data}`)
}

fun1(5)
fun2(6)

一个参数的箭头函数可以省略填写参数的括号,多个参数的箭头函数不可以省略括号,也就是2个及以上不可以

  • 箭头函数的this指针,指向的是this所在对象的父类
ts = {
    name:15,
    fun1:function(){
        console.log(this.name)
    },
    fun2:()=>{
        console.log(this.name)
    }
}
ts.fun1()
ts.fun2()

fun1可以找到name,fun2无法找到

就是说,箭头函数里面调用this指针,this指针指向的是箭头函数的父级对象

这里的ts对象的父级是global,也就是空对象。所以打印的结果是undefined。在浏览器里面就是window了。

因为这个name是在ts对象里面创建的,不是在全局变量里面。所以在浏览器里面的箭头函数下的this指向也是空undefined

  • 箭头函数的语句是只有返回值的语句,可以省略大括号,且不能使用return关键字
ts = (a , b)=> a + b

console.log(ts(5,6))

ts2 = (a, b) => {
    return a + b
}

这里面的 ts 函数和 ts2 函数是等效的

ts省略了大括号和返回值关键字

定时器

setTimeout/setInterval

setTimeout(function (){
    console.log('hello world!');
},5000)

就是直接调用方法setTimeout()

在参数里面可以创建一个函数,这个函数就是定时器结束所要执行的任务

第二个参数,就是以毫秒为单位的时间。

setTimeout只能执行一次函数,就是在你规定的时间达到之后,就结束了

setInterval(function (){
    console.log('hello world!');
},2000)

这个函数setInterval,可以无限次重复执行函数,第二个参数是间隔时间

中断定时器cleanTimeout/cleanInterval

在定时器结束之前,拦截函数的执行。

就是说,定时器的间隔是10秒,你可以在10秒内,也就是这一回合内,使用cleanTimeout(定时器的编号)方法,取消定时器

浏览器会给这些定时器分编号的,只需要在参数里面写上就行

一般网站是使用debugger模式,当你按下F12 的时候,整个页面卡死,不能点击,就是debugger模式,也算是反爬吧

debugger是打一个断点,你如果点击跳过,配合setinterval定时器,还是会进去debugger模式。所以休要中断定时器

eval加密

eval("console.log('hello world')")

这个函数是可以把字符串格式的函数执行

一般用于网站的函数加密

就是把部分函数,使用eval加密。你自己看着是一堆乱码

eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0.2(\'1 3\')',62,4,'console|hello|log|world'.split('|'),0,{}))

还原eval加密

把eval关键字去掉,就是一个大括号,里面是一段字符串格式的代码,把字符串的引号去掉,就是原来的代码了

在NodeJS环境里面,把上面这段代码复制一下

console.log(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0.2(\'1 3\')',62,4,'console|hello|log|world'.split('|'),0,{}))

然后去掉eval,替换成console.log,后面不用动,直接允许代码,输出的就是解密之后的源代码了console.log('hello world')

反正解密挺简单的,eval加密,就是把原有的代码,给你打乱顺序,你不用研究打乱方法,直接运行就能看到输出内容,也就是源代码执行的结果,想要看到源代码的具体内容,就以文本的形式打印一下,就好了

HTML和JS交互

  • 在HTML中引入js
  • 绑定事件
  • 动态修改HTML

在HTML中引入js

首先是创建HTML文件

在pycharm里面,新建文件选择html即可

下面介绍一些html的基本常识

  • <html lang="en">
    

意思是网页语言是英文

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>

大部分的头部信息放在head里面

大部分的内容放在body里面

/html是网页源代码结束的标志

在head里面的title标签里面就是网站的标题

在body写的内容,就是显示在网页上的信息

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
</body>
</html>

请添加图片描述

运行之后,pycharm就会利用你本地的浏览器显示html的结构

在body里面使用script标签创建函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
    <script>
        function btn_clicked(){
            alert('这是一个弹窗')
        }
    </script>
    <button id="b1" onclick="btn_clicked()">
        点击这里触发弹窗一
    </button>
</body>
</html>

我们可以在body里面使用script标签,在script标签里面创建函数,也就是点击事件啥的

这里的点击事件是btn_clicked()函数,他里面实现的功能是alert

alert浏览器自带的弹窗

这个是浏览器自带的弹窗创建。

我们只需要调用alert就可以创建一个弹窗

请添加图片描述

右边这个灰色的就是浏览器自带的弹窗。

body里面的button

不用多说,就是在body里面创建一个button,就可以生成一个按钮。

在button标签里面写的内容,就会显示在button按钮里面。

onclick 按钮点击

onclick就是按钮点击的意思,类似qt里面的clicked

然后后面写上可以触发的事件

<script>
    function btn_clicked(){
        alert('这是一个弹窗')
    }
</script>
<button id="b1" onclick="btn_clicked()">
    点击这里触发弹窗一
</button>

外部引入js代码

请添加图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
<!--    <script>-->
<!--        function btn_clicked(){-->
<!--            alert('这是一个弹窗')-->
<!--        }-->
<!--    </script>-->
    <script src="button_click.js"></script>
    <button id="b1" onclick="btn_clicked()">
        点击这里触发弹窗一
    </button>
</body>
</html>

就是我们想要设计的控件非常多,想要实现的功能也非常多,如果都写在网页html文件里面,就会显得代码非常的庞大

不方便以后日常的维护,所以,就把想要实现的控件js代码部分独立出来

这里就是把按钮点击事件,放在了别的js文件里面。

只需要在body里面引入一个script的标签,使用src属性调用一下js文件,就可以实现功能了

function btn_clicked(){
            alert('这是外部引入的js代码')
}

这是js文件的代码

就是把刚刚的html代码里面js代码独立出来了

事件绑定

刚刚是通过按钮的onclick属性来完成事件绑定

页面加载绑定JS
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息

    <script>
        window.onload = function (){
            document.getElementById('b1').onclick = function (){
                alert('页面加载之后,自动绑定JS代码')
            }
        }
    </script>
    <button id="b1">
        点击这里触发弹窗一
    </button>
</body>
</html>

就是说,我们可以根据按钮的属性进行代码绑定。

比如我们之前创建的按钮id值是b1.我们创建一个script标签,写页面加载代码

上面代码里面的11行,就是页面加载代码

其中window.onload 意思是整个网页完成了页面的加载,一般是不再刷新啥的。

后面,我们跟了一个函数,等网页加载完成之后,就会自动执行后面的函数

在函数体里面,我们使用了document,这是浏览器里面操作网页的对象。

我们使用了document里面的getElementById的方法,意思就是字面意思,找标签的id。后面的参数写我们想要操作的标签buttonb1

然后绑定点击事件onclick,接着在后面写点击点击事件执行的操作,就是alert一个弹窗。

请添加图片描述

通过事件监听绑定JS addEventListener
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息

    <script>
        window.onload = function (){
            document.getElementById('b1').addEventListener('click',function (){
                alert('通过事件监听绑定JS')
            })
        }
    </script>
    <button id="b1">
        点击这里触发弹窗一
    </button>
</body>
</html>

和上面的操作差不多,就是在页面加载完成之后,使用document绑定b1按钮,使用事件监听方法addEventListener

第一个参数写发生的事件,我们这里写的是click,也就是按钮b1发生了点击。第二个参数写所要执行的函数

这里说的函数都是完整的匿名函数

请添加图片描述

通过HTML的顺序执行绑定JS

我们之前使用window.onload这个函数,就是使用了页面加载

也可以不使用页面加载函数,完成一样的绑定功能

请添加图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
    <button id="b1">
        点击这里触发弹窗一
    </button>
    <script>
        document.getElementById('b1').addEventListener('click',function (){
                alert('通过html的顺序执行绑定JS')
            })
    </script>

</body>
</html>

因为html环境下,是按从上到下的顺序,依次执行代码的

因此,可以趁着执行的顺序,完成代码的绑定

这里需要注意的是,如果不使用页面加载方法,就只能把script标签函数放在html代码的最后面。

因为是等html完成所有的控件加载之后,再执行script里面的JS代码。

这样我们只需要使用document就可以完成事件绑定了

  • 如果不使用window.onload,就必须把控件放前面,把script放后面,不然document找不到控件
  • 使用了window.onload就可以不在意先后顺序了,完成了页面加载肯定能找到控件的
  • 习惯上JS代码放在靠前的位置,所以都使用页面加载方法

输入框

请添加图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
    <script>
        window.onload = function (){
            document.getElementById('b1').addEventListener('click',function (){
                var test1 = document.getElementById('t1').value
                console.log(test1)
            })
        }
    </script>
    <input id="t1">

    <button id="b1">
        输入密码
    </button>
</body>
</html>

我们使用了input标签,创建了一个输入框,并且在按钮点击事件里面绑定了读取输入框的函数

在浏览器里面输出的信息都在控制台里面

读取输入框的内容

输入框的内容也就是输入框的值value

先是通过document里面的getid的方法找到输入框t1,然后读取内容,赋值给我们自己创建的局部变量test1,最后打印一下,证明了输入框的内容读取成功

输入框的提示

就是没有输入任何内容的时候,里面显示的灰色信息

需要在input元素里面,使用placeholder属性,在这个属性的值里面,写提示的信息

div标签

  • 块级元素<div> 是一个块级元素,这意味着它会在页面中独占一行,并且可以容纳其他块级元素或行内元素。

  • 用途:通常用于创建文档的结构或布局,方便地对一组内容进行分组。可以将多个相关的内容包裹在一个 <div> 中,便于CSS样式的应用和JavaScript的操作。

  • 典型用法

    <div>
        <h1>标题</h1>
        <p>这是一个段落。</p>
    </div>
    

span标签

  • 行内元素<span> 是一个行内元素,这意味着它不会在页面中占据整行,而是和其他行内元素同处于一行。

  • 用途:通常用于对文本或文档中的小部分进行样式化或强调处理。它不引入任何视觉上的分隔,因此通常被用作包含在其他块级元素或行内元素中的内容。

  • 典型用法

    <p>这是一个 <span style="color: red;">突出显示的文本</span></p>
    

就是说白了,我们在html里面写个div标签,里面可以嵌套一个span标签,然后在span里面可以显示我们展示的内容

动态加载网页代码innerHTML

首先要知道div标签的作用,是容纳其他的标签,我们定位到div标签之后,使用innerHTML 就可以写我们想要让div显示的内容了

这里是 “<span>” + test1 + “<span>”

通过动态加载的方式,可以不断的完善内容,也可以避免一次性把所有代码给别人看,导致轻易的被破解

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    这里是网页的信息
    <script>
        window.onload = function (){
            document.getElementById('b1').addEventListener('click',function (){
                var test1 = document.getElementById('t1').value
                document.getElementById('d1').innerHTML = "<span>" + test1 + "<span>"
            })
        }
    </script>
    <input id="t1">

    <button id="b1">
        输入密码
    </button>
    <div id="d1">

    </div>
</body>
</html>

请添加图片描述

我们在按钮点击事件里面,使用document找到了div标签里id是d1的那个

然后,我们使用了innnerHTML方法,在div里面增加代码,增加的就是下面的内容

"<span>" + test1 + "<span>"

innerHTML 是一个用于访问和修改 HTML 元素内容的 JavaScript 属性,而不是一个 HTML 方法。它允许你获取或设置某个元素的 HTML 内容。通过 innerHTML,你可以添加、修改或删除 HTML 代码。

innerHTML 的用法
  1. 获取内容

    • 当你使用 innerHTML 来访问一个元素时,它会返回该元素中所有的 HTML 代码,包括其子元素。
    var element = document.getElementById('myElement');
    var content = element.innerHTML; // 获取元素的 HTML 内容
    
  2. 设置内容

    • 你可以通过赋值来改变一个元素的 HTML 内容。这会替换该元素中现有的所有 HTML。
    var element = document.getElementById('myElement');
    element.innerHTML = '<p>这是一段新的内容</p>'; // 替换掉原有内容
    
  • 示例

以下是使用 innerHTML 的一个简单示例:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>innerHTML 示例</title>
</head>
<body>
    <div id="myElement">原有内容</div>
    <button onclick="changeContent()">点击我改变内容</button>

    <script>
        function changeContent() {
            var element = document.getElementById('myElement');
            element.innerHTML = '<p>这是新的内容!</p>'; // 替换内容
        }
    </script>
</body>
</html>

在这个示例中,点击按钮将会调用 changeContent 函数,通过设置 innerHTML<div> 中的内容替换为新内容。

重要注意事项
  • 使用 innerHTML 时要小心,尤其是当内容来源不受信任时,可能会导致 JavaScript 注入攻击(XSS 攻击),因为外部输入的内容会被作为 HTML 直接执行。
  • 对于动态更新的高频内容,考虑使用 Element.innerText 或者 textContent 属性添加纯文本,而不涉及 HTML 代码,这样可以避免一些安全问题。
总结

innerHTML 是一个非常方便的属性,用于动态地访问和修改 HTML 内容,但在使用时应考虑安全性和性能方面的因素。

JQuery

一般看到源代码里面有美元符号,能意识到是jQuery写的就行,不懂的地方自己去网上查查。能读懂大概就行

是一个比较早的开发人员搭建网站的库,现在又更好的库可以替代,但是仍有大量的网站依旧使用jQuery开发,所以为了逆向这些网站,需要了解jQuery。

是第三方库,需要自己从官网上下载,下载的时候每一版本都有两种状态,一种是完整的源代码,一种是压缩后的源代码,就是去掉空格一系列框架的源代码。

因为优化了源代码的框架,加载速度很快,一般使用min版本

https://cdn.bootcdn.net/ajax/libs/jquery/1.9.1/jquery.min.js

下载1.9版本是因为在当年最火热的时候,正是该版本

使用jQuery

保存到本地,然后添加到项目里面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    <script src="jquery.min.js"></script>
</body>
</html>

需要使用script标签,使用src属性填写jquery文件的路径,最后把script标签闭合

$的作用

  1. 是一个函数名。jQuery的缩写
  2. 也是一个选择器,默认是CSS

比如找id是11的标签,就是(‘#11’)

常用方法$(*selector*).*action*()

意思就是美元符号,加选择器,加方法

  • index 获取元素索引
  • val 获取元素的值,也就是内容
  • css 获取元素样式,也可以设置元素样式
  • text 获取和设置元素文本
  • arrt 获取和设置元素的属性值,拿到src等等

设置内容,就在方法后面的参数里面直接写就可以。如果不写参数,就是获取元素已有的内容

找到元素

请添加图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>这里是网页的标题</title>
</head>
<body>
    <script src="jquery.min.js"></script>
    <script>
        $(function (){
            $('#b1').click(function (){
                var text1 = $('#t1').val()
                $('#d1').append("这里是输入的内容》》》" + text1)
            })
        })
    </script>

    <input id="t1">
    <button id="b1">
        jquery按钮
    </button>
    <div id="d1">

    </div>

</body>
</html>

和之前实现的功能是一样的,都是用一个按钮,点击触发事件,显示输入框的内容

不同的是在script里面js的部分:

首先我们用一个美元符号加括号,构成了之前的window.onload的功能

然后我们在括号里面写逻辑部分,我们需要找到按钮b1,需要使用美元符号跟括号,括号就是一个CSS选择器,我们需要使用CSS语法,也就是之前学习selenium的时候定位网页标签的时候的CSS语法,比如#号就是id的意思

这里使用#b1找到了按钮1,后面跟着事件click,在后面跟着事件function,在函数里面我们需要读取输入框input的值,拿到这个值之后,再把内容输入到div标签里面

定位到div标签之后,使用append方法,就可以把内容添加到网页里面了

主要是学习使用jQuery语法定位元素

搭建简单的网站

实现了一个天气查询的网站,了解爬虫原理

  • 前后端交互
  • 后端渲染
  • Ajax请求
  • Axios
  • XMLHTMLRequests对象

一个网站,有前端和后端两个部分,前端负责页面展示,后端是服务器负责信息处理,从搭建服务器开始一个网站

前后端交互

搭建服务器

使用python的第三方库Flask,搭建服务器

使用pip命令安装Flask

搭建本地服务器

我们使用python里面的flask完成服务器的搭建

from flask import Flask
app = Flask('__name__')
@app.route('/')  # URL路径,就是网址后面加个斜杠,就执行下面的函数
def index():
    return '<h1>Hello World!</h1>'

if __name__ == '__main__':
    app.run(host='127.1.1.0', port=8888)
  • app = Flask(‘_name_’) 意思就是Flask库的入库是name,也就是我们通常创建的主函数入口,实例化服务器入口
  • app.run(host=‘127.1.1.0’, port=8888) 就是运行实例化的服务器,第一个参数是网址/域名,第二个参数是端口号

请添加图片描述

服务器返回静态HTML文件

from flask import Flask
app = Flask(__name__)
@app.route('/')  # URL路径,就是网址后面加个斜杠,就执行下面的函数
def index():
    with open('pythonProject8888/index.html', 'r', encoding='utf-8') as f:
        index_html = f.read()
    return index_html

if __name__ == '__main__':
    app.run(host='127.1.1.1', port=8888)

上面写的服务器返回的就是一个网页结构

全写在返回值里面太挫了

我们需要新建HTML文件,在这个文件里面写网页的前端框架

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>北京的气温是23度,当前小雨</h1>
</body>
</html>

请添加图片描述

我们在服务器文件里面读取了本地静态html文件,然后返回到了前端页面上

后端渲染

服务器渲染

from flask import Flask
app = Flask(__name__)
@app.route('/')  # URL路径,就是网址后面加个斜杠,就执行下面的函数
def index():
    location = 'shanghai'
    temp = '23'
    desc = '晴'
    with open('pythonProject8888/index.html', 'r', encoding='utf-8') as f:
        index_html = f.read()
    return index_html.replace('location',location).replace('temp',temp).replace('desc',desc)

if __name__ == '__main__':
    app.run(host='127.1.1.1', port=8888)

我们在本地的HTML里面写一下被替换的位置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>location的气温是temp度,当前desc</h1>
</body>
</html>

上面的代码主要展示了在服务器完成字符串替换的工作

因为我们使用的是wtihopen读取文件的形式,把整个html文件读成了字符串,所以使用了replace完成内容的替换

前端负责展示,服务器负责渲染。

很明显如果一个网页的所有内容都交给服务器渲染,服务器的工作量太过庞大了

渲染模板

引入flask库里面的render_template模块

需要新建文件夹,名称为templates。必须指定名字。

from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')  # URL路径,就是网址后面加个斜杠,就执行下面的函数
def index():
    location = '北京'
    temp = '23'
    desc = '晴'
    return render_template('index.html', location=location, temp=temp, desc=desc)

if __name__ == '__main__':
    app.run(host='127.1.1.1', port=8888)

这里就不需要使用读文件的方式打开html了

我们使用了render_template库,直接在return返回值填充进去即可

render_template的第一个参数写静态HTML模板,后面写我们要替换的参数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
</body>
</html>

在html文件里面,把想要替换的参数使用双层大括号包住,就可以完成替换了

动态渲染模板

from flask import Flask,render_template
import requests,json
app = Flask(__name__)
@app.route('/')  # URL路径,就是网址后面加个斜杠,就执行下面的函数
def index():
    location = 'beijing'
    data = get_data(location)
    temp = data['temp']
    desc = data['weather']
    return render_template('index.html', location=location, temp=temp, desc=desc)

def get_data(location):
    url = f'http://api.openweathermap.org/data/2.5/weather?q={location}&mode=json&units=metric&lang=zh_cn&APPID=f0c3eda7aa680434e17ae00aa2fba619'
    data = requests.get(url)
    data_json = json.loads(data.text)
    temp = data_json['main']['temp']  # 挑出温度
    city = data_json['name']
    weather = data_json['weather'][0]['description']
    weather_data = {
        'city': city,
        'temp': temp,
        'weather': weather,
    }
    return weather_data

if __name__ == '__main__':
    app.run(host='127.1.1.1', port=8888)
    get_data('cangzhou')

这里我们使用了一个爬虫函数,通过输入地址,返回官方网站上的实时温度,天气状况

我们在主页里面index函数,调用了这个爬虫函数,并得到了返回值,最后返回给了前端

在模板里面导入jQuery库

在静态的模板里面导入这个静态的jQuery文件,需要先建一个static文件夹,然后把jQuery放在这个文件夹里面

在html模板里面跟之前学习的导入库的方法一样,使用script里面的src参数导入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        $(function (){
            alert('成功加载jQuery')
        })
    </script>
</body>
</html>

通过弹窗提醒,我们成功的导入了jQuery

Ajax发送请求

AJAX = Asynchronous Javascript and XML(异步的JS和XML)

优点是不重新加载整个页面,就可以与服务器交换数据,完成局部刷新

模块里面使用ajax请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        $(function (){
            $('#b1').click(function (){
                $.ajax({
                    url:'/get_data',
                    method:'get',
                    success:function (data){
                        console.log(data)
                    }
                })
            })
        })
    </script>
    <input id="test1">
    <button id="b1">查询实时天气</button>
</body>
</html>

我们在点击事件里面,使用了$.ajax方法,一般后面的参数,我们写成字典的格式,第一个参数写url,第二个参数是method写请求方式。后面是如果请求成功,就执行什么函数success

@app.route('/get_data')
def get_data():
    return '这是ajax请求的get数据'

服务器需要新增url路由,处理前端发送的请求

flask的request类

这个类和爬虫的类不一样

这个类是服务器处理前端传回的连接里面的参数内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        $(function (){
            $('#b1').click(function (){
                var loc = $('#test1').val()
                $.ajax({
                    url:`/get_data?loc=${loc}`,
                    method:'get',
                    success:function (data){
                        console.log(data)
                    }
                })
            })
        })
    </script>
    <input id="test1" placeholder="请输入查询天气的城市">
    <button id="b1">查询实时天气</button>
</body>
</html>

这里面,我们在ajax请求部分,在url里面增加了一个参数,这个增加的参数一般写在网址的后面,跟一个问号,然后写参数`/get_data?loc=${location}

在服务器里面也要获取这个参数

@app.route('/get_data')
def get_data():
    loc = request.args.get('loc')
    data = get_weather(loc)
    msg = '请求正常'
    sender_data = {
        'msg':msg,
        'data':data
    }
    sender_data_str = json.dumps(sender_data) # 以字符串的形式把数据传回去
    return sender_data_str

我们需要在服务器里面导入flask库的request类

使用request类里面的args方法的get方式,得到参数location

然后我们模拟了一下正常的服务器处理数据的方式,首先是msg,也就是服务器的资格验证,msg显示资格验证是否通过

然后我们把msg和请求到的天气data数据一起打包,再变成字符串的格式返回出去

前端渲染

前端处理返回的字符串格式的数据json.parse

类似python里面的json.loads,就是把json格式的字符串在解析回json格式的数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        $(function (){
            $('#b1').click(function (){
                var loc = $('#test1').val()
                $.ajax({
                    url:`/get_data?loc=${loc}`,
                    method:'get',
                    success:function (data){
                        weather_data = JSON.parse(data)
                        loca = weather_data['data']['city']
                        temp = weather_data['data']['temp']
                        desc = weather_data['data']['weather']
                        play_info = `${loca}的气温是${temp}度,当前是${desc}`
                        $('h1').text(play_info)
                    }
                })
            })
        })
    </script>
    <input id="test1" placeholder="请输入查询天气的城市">
    <button id="b1">查询实时天气</button>
</body>
</html>

我们在请求成功之后的函数里面,把服务器返回的数据进行json解析,然后获取里面的关键信息

使用字符串替换` l o c a 的气温是 {loca}的气温是 loca的气温是{temp}度,当前是${desc},把信息填充进去,最后把这些信息替换到h1元素里面

现在服务器只处理数据,前端负责渲染,就是前端渲染

这里对前端的源代码发生了内容替换,就是局部刷新了

爬虫的本质

import requests

url = 'http://127.1.1.1:8888/get_data?loc=beijing'

ts = requests.get(url)
print(ts.json())

我们对我们自己搭建的网址进行信息爬取

得到了和网站同样的json数据

服务器校验

  • 请求头校验
  • cookie校验
  • 参数校验

我们正常使用网站的时候,网站向服务器发送请求,浏览器会自己把一些请求参数写进去

因此我们可以在服务器进行参数校验

使用flask库的request类,就可以拿到相应的信息了

请求头校验
@app.route('/get_data')
def get_data():
    loc = request.args.get('loc')
    ua = request.headers.get('User-Agent')
    data = ''
    if 'python' in ua:
        msg = '是python的自动化程序'
    else:
        data = get_weather(loc)
        msg = '请求正常'
    sender_data = {
        'msg':msg,
        'data':data
    }
    sender_data_str = json.dumps(sender_data) # 以字符串的形式把数据传回去
    return sender_data_str

我们在服务器部分,使用了flask的request类,获取了访问服务器的用户的一些信息。

这里是获取了用户的请求头headers。

如果爬虫代码部分不添加请求头就会报错{‘msg’: ‘是python的自动化程序’, ‘data’: ‘’}

这也是为什么我们需要添加请求头才能爬取网站信息的原因

import requests

url = 'http://127.1.1.1:8888/get_data?loc=beijing'

header = {'user-agent':'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'}

ts = requests.get(url,headers=header)
print(ts.json())

爬虫代码做出相应的调整,就可以爬取数据了

cookie校验

这个是在前端里面完成的

我们使用document找到网页的cookie,然后进行修改,设置成想要的格式,这里就是设置了一个参数token=abc

<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        // 制作cookie
        let time = new Date() // 获取时间
        document.cookie = `token=abc`

        $(function (){
            $('#b1').click(function (){
                var loc = $('#test1').val()
                $.ajax({
                    url:`/get_data?loc=${loc}`,
                    method:'get',
                    success:function (data){
                        console.log(data)
                        weather_data = JSON.parse(data)
                        loca = weather_data['data']['city']
                        temp = weather_data['data']['temp']
                        desc = weather_data['data']['weather']
                        play_info = `${loca}的气温是${temp}度,当前是${desc}`
                        $('h1').text(play_info)
                    }
                })
            })
        })
    </script>
    <input id="test1" placeholder="请输入查询天气的城市">
    <button id="b1">查询实时天气</button>
</body>
  • 服务器校验cookie
@app.route('/get_data')
def get_data():
    loc = request.args.get('loc')
    ua = request.headers.get('User-Agent')
    token = request.cookies.get('token')
    data = ''
    if 'python' in ua:
        msg = '是python的自动化程序'
    elif token != 'abc':
        msg = 'cookie错误'
    else:
        data = get_weather(loc)
        msg = '请求正常'
    sender_data = {
        'msg':msg,
        'data':data
    }
    sender_data_str = json.dumps(sender_data) # 以字符串的形式把数据传回去
    return sender_data_str
  • 爬虫代码的调整

要是什么都不加,服务器肯定是报错了{‘msg’: ‘cookie错误’, ‘data’: ‘’}

import requests

url = 'http://127.1.1.1:8888/get_data?loc=beijing'

header = {'user-agent':'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'}
cookie = {'token':'abc'}

ts = requests.get(url,headers=header,cookies=cookie)
print(ts.json())

可以在爬虫里面设置一个cookie字典,也可以把cookie放在请求头headers里面

import requests

url = 'http://127.1.1.1:8888/get_data?loc=beijing'

header = {'user-agent':'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',
          'cookie':'token=abc'}

ts = requests.get(url,headers=header)
print(ts.json())

都可以完成爬取功能

服务器处理post请求

@app.route('/post_data',methods=['POST'])
def post_data():
    loc = request.form.get('loc') # 表单数据
    data = get_weather(loc)
    msg = '请求正常'
    sender_data = {
        'msg':msg,
        'data':data
    }
    sender_data_str = json.dumps(sender_data)
    return sender_data_str
  • 服务器里面,需要重新写一个路由处理信息

在app route参数里面还要添加一个参数methods值是post请求

从用户那里获取信息需要使用request里面的form方法!!!!!就是把get方法里面的args替换成form

才能正常使用,后面的信息处理一样

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/jquery.min.js"></script>
    <script>
        // 制作cookie
        let time = new Date() // 获取时间
        document.cookie = `token=abc`

        $(function (){
            $('#b1').click(function (){
                var loc = $('#test1').val()
                $.ajax({
                    // url:`/get_data?loc=${loc}`,
                    url:`/post_data`,
                    method:'post',
                    data:{
                        locat:loc
                    },
                    success:function (data){
                        console.log(data)
                        weather_data = JSON.parse(data)
                        loca = weather_data['data']['city']
                        temp = weather_data['data']['temp']
                        desc = weather_data['data']['weather']
                        play_info = `${loca}的气温是${temp}度,当前是${desc}`
                        $('h1').text(play_info)
                    }
                })
            })
        })
    </script>
    <input id="test1" placeholder="请输入查询天气的城市">
    <button id="b1">查询实时天气</button>
</body>
</html>

前端也需要做出相应的调整,url重新编写,method改为post

还需要添加data数据返回给服务器处理信息

  • 爬虫代码调整
import requests

url = 'http://127.1.1.1:8888/post_data'

header = {'user-agent':'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',
          'cookie':'token=abc'}

data = {'locat':'cangzhou'}

ts = requests.post(url,headers=header,data=data)
print(ts.json())

这里的data是一个表单数据,需要在requests请求里面,使用data参数,接收表单数据

post请求数据的格式 payload->json 表单->data

我们使用post请求的时候,默认是data表单格式,我们也可以设置成json格式

只需要在前端增加一个参数contentType,值是Application/json

  • 前端
$(function (){
    $('#b1').click(function (){
        var loc = $('#test1').val()
        $.ajax({
            // url:`/get_data?loc=${loc}`,
            url:`/post_data`,
            method:'post',
            data:JSON.stringify({
                locat:loc
            }),
            contentType:'Application/json',
            success:function (data){
                console.log(data)
                weather_data = JSON.parse(data)
                loca = weather_data['data']['city']
                temp = weather_data['data']['temp']
                desc = weather_data['data']['weather']
                play_info = `${loca}的气温是${temp}度,当前是${desc}`
                $('h1').text(play_info)
            }
        })
    })
})

我们把之前的data数据的值,使用了JSON方法的stringify方法,并把之前的数据放在参数里面,完成表单数据向json数据的转换

  • 服务器端
@app.route('/post_data',methods=['POST'])
def post_data():
    loc = request.json.get('loc')
    data = get_weather(loc)
    msg = '请求正常'
    sender_data = {
        'msg':msg,
        'data':data
    }
    sender_data_str = json.dumps(sender_data)
    return sender_data_str

需要调整一下数据接收的方式,改为request类的json方法

  • 爬虫
import requests

url = 'http://127.1.1.1:8888/post_data'

header = {'user-agent':'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',
          'cookie':'token=abc'}

data = {'locat':'cangzhou'}

ts = requests.post(url,headers=header,json=data)
print(ts.json())

只需要修改requests里面的参数部分,把data类型改成json,就可以正常访问了

json格式就是前端的payload格式,data对应前端的表单格式

Axios请求

和Ajax类似,也是一种请求方式

cdn.jsdelivr.net/npm/axios@1.6.7/dist/axios.min.js

这个是axios的js文件,也需要下载

拦截器

有两种拦截器:发送数据,响应数据

  • 我们输入数据之后。点击发送。在数据从前端发送到后端的过程有一个拦截器,也叫发送数据
  • 在服务器返还数据给前端的过程,也有一个拦截器,也叫响应数据

在第一个拦截器里面进行加密处理

在第二个拦截器里面进行解密处理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>本地的网站-查询天气预报</title>
</head>
<body>
    <h1>{{location}}的气温是{{temp}}度,当前是{{desc}}</h1>
    <script src="static/axios.min.js"></script>
    <script>
        axios.interceptors.request.use(function (config){
            console.log('拦截发送数据',config)
            // 加密处理
            return config
        })

        axios.interceptors.response.use(function (config){
            console.log('拦截响应数据',config)
            //解密处理
            return config
        })

        window.onload = function (){
            document.getElementById('b1').addEventListener('click',function (){
                axios.get('/axios').then(function (res){
                    console.log(res.data)
                })
            })
        }
    </script>
    <input id="test1" placeholder="请输入查询天气的城市">
    <button id="b1">查询实时天气</button>
</body>
</html>

我们在script里面使用了axios的两种拦截器

使用axios函数的interceptors类的request方法,就是拦截发送数据,config就是浏览器发送的全部数据

然后在函数里面进行加密处理

把上面的request替换成responose,就是响应数据拦截器,就是把服务器发送给浏览器的全部数据进行拦截,这里赋值给了config

如果发送的时候进行了加密处理,响应的时候就需要解密处理

如果位置藏得太深,可以使用查找关键字interceptors定位这两种拦截器

XMLHttpRequests对象

ajax和axios都是基于XMLHttpRequests函数进行封装的

这一大串是一个函数名

使用方法

  1. 实例化一个xhr的对象
    1. var xhr = new XMLHttpRequest()
  2. 使用xhr的open方法发送请求
    1. xhr.open(‘GET’,‘http://www.example.com/page.php’,‘true’)
    2. 就是向指定的服务器发送请求,建立连接,等待服务器响应
  3. 等待服务器响应
    1. xhr.onreadystatechange
    2. 这个值是ok,就是链接成功
  4. 使用send发送数据

这里面也可以进行加密操作,具体内容以后再说