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.prototype
是Teach
构造函数的原型对象。 -
xiaoming
是Teach
构造函数的一个实例。 - 由于
xiaoming
是通过Teach
构造函数创建的,因此xiaoming.__proto__
是Teach.prototype
。
这就是为什么我们可以说 Teach.prototype === xiaoming.__proto__
。而 Teach
是构造函数的引用,它不是一个原型实例,所以 Teach === xiaoming.__proto__
这个表达式是错误的。
理解原型和实例
-
构造函数与原型:
当我们定义一个构造函数(例如Teach
),JavaScript 会为这个构造函数生成一个原型对象,这个对象是Teach.prototype
。所有通过这个构造函数创建的实例(例如xiaoming
)会自动通过其内部的[[Prototype]]
属性(在现代 JavaScript 中被称为__proto__
)链接到这个原型对象。 -
实例与原型的关系:
当你创建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
的用法
-
获取内容:
- 当你使用
innerHTML
来访问一个元素时,它会返回该元素中所有的 HTML 代码,包括其子元素。
var element = document.getElementById('myElement'); var content = element.innerHTML; // 获取元素的 HTML 内容
- 当你使用
-
设置内容:
- 你可以通过赋值来改变一个元素的 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标签闭合
$的作用
- 是一个函数名。jQuery的缩写
- 也是一个选择器,默认是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函数进行封装的
这一大串是一个函数名
使用方法
- 实例化一个xhr的对象
- var xhr = new XMLHttpRequest()
- 使用xhr的open方法发送请求
- xhr.open(‘GET’,‘http://www.example.com/page.php’,‘true’)
- 就是向指定的服务器发送请求,建立连接,等待服务器响应
- 等待服务器响应
- xhr.onreadystatechange
- 这个值是ok,就是链接成功
- 使用send发送数据
这里面也可以进行加密操作,具体内容以后再说