逆向爬虫22 Javascript快速入门
一. Javascript的由来
Javascript是一门能够运行在浏览器上的脚本语言,简称JS。首先, Javascript这个名字的由来就很有意思,不少人认为Javascript和Java貌似很像,容易想象成Java的脚本。但其实不然, 两者之间没有任何关系,纯粹是商业碰瓷。
其次,当初为什么要引入Javascript?早先互联网网速没有现在那么快,用户从浏览器上提交表单时,浏览器将数据发送给服务器,服务器对表单内容进行校验,通过后才返回用户想要的数据,如果校验失败,则返回一个错误结果给用户,用户根据错误结果重新提交表单,重复前面的过程。由于早期网速很慢,因此这个过程就特别费时,导致用户体验很差,因此人们想在浏览器这里就对用户提交的表单先进行本地校验,本地校验通过后再通过互联网发送出去,以免用户经过长时间等待后,才被告知自己内容输错了。由此Javascript就被人们发明出来了,Javascript最早就是用来解决上述这个问题的,在浏览器这对用户输入的表单进行校验,通过后才允许浏览器将表单发送出去。
后来Javascript愈发强大,人们用它来控制浏览器运行时的整个工作流程,这也是我们爬虫为什么要学习Javascript的原因。
二. 如何在HTML中引入Javascript
那么既然JS是可以运行在浏览器上的脚本。并且,我们知道本质上,浏览器是执行HTML程序的,那么如何在HTML中引入JS呢?
方案一,直接在
<!-- 第一个.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>周杰伦</title> <!-- 页面标题 -->
<!--
html的注释
-->
<script>
// 单行注释
/*
多行注释
*/
alert("哈哈") // 一个警告窗口
</script>
</head>
<body>
用户展示的内容 <!-- 用户可见内容 -->
</body>
<script src="./第一个.js"></script>
</html>
方案二,将js代码写在js文件中,然后通过
<!-- 第一个.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>周杰伦</title>
<!-- <script src="第一个.js"></script> --> <!-- 可以放在body前面 -->
</head>
<body>
用户展示的内容
</body>
<script src="./第一个.js"></script> <!-- 也可以放在body后面 -->
</html>
/* 第一个.js */
alert("哈你个头")
由于html是从上往下加载的,因此如果把javascript放在body前面的话,就会先执行javascript中的代码,如果代码中需要对body中的内容进行处理,则网页就会报错,因此这种情况可以把javascript放在body后面。
三. Javascript基本数据类型
JS虽然是一个脚本语言。麻雀虽小,五脏俱全。在js中也是可以像其他编程语言一样,声明变量,条件判断,流程控制等等。我们先看一下JS中的数据类型
在js中主要有这么几种数据类型(基本)
number 数字, 不论是整数还是小数, 数据类型都是number
string 字符串, 这个没啥可聊的. 就是很单纯的字符串
boolean 布尔值, 只有两个, true和false. 注意不是大写T和F.
object 对象, 这个比较特殊. 你可以理解为所有被new出来的东西都是对象
undefined 这个表示未定义. 所有没有被定义过的东西默认都是该类型
在js中声明变量用var来声明
3.1 number类型变量
/* number类型变量 */
var a, b, c; // 如果一个变量还没有被赋值,此时该变量不应该被使用,否则是很危险的
var d = 10.01; // 创建一个变量的正确方案
console.log(d); // 调用控制台,并输出 ---> print
console.log(typeof(d)) // 查看数据类型
var e, f = 18;
console.log(typeof(e)) // 类型是undefined
console.log(typeof(f)) // 类型是number
3.2 string类型变量
/* string类型变量 */
var a = "alex"
console.log(typeof(a))
var b = "10086"
console.log(b + 10) // 如果+左右两端任意数据是字符串,结局就是字符串拼接
console.log(10 + b) // 如果+左右两端任意数据是字符串,结局就是字符串拼接
console.log(5 + 5)
var c = parseInt(b) // int(b) 将字符串转换成数字(整数),parseFloat()
console.log(c + 5)
d = 10086
// 方案一,官方
f = d.toString() // 数字转字符串
// 方案二,野路子
f = d + "" // 数字转字符串
console.log(d + 1)
console.log(typeof(f))
console.log(f)
3.3 boolean类型变量
/* boolean类型变量 */
var a = 3 < 2;
console.log(a); // true, false
// 坑
// and, or, not
// &&, ||, !
console.log((3>2) && (1<5));
// 等于比较
a = "10086";
b = 10086;
console.log(a == b); // == 表示判断两端的值
console.log(a === b); // === 表示判断两端的值,同时还要判断数据类型
// 三元表达式
// a if a > b else b ---> python的
a = 10;
b = 20;
c = a > b ? a : b // 表达式1 ? 结果1 : 结果2
console.log(c)
3.4 object类型变量
/* object类型变量 */
var arr = new Array()
console.log(typeof(arr))
3.5 自增自减表达式
var i = 10;
i ++; // 让i自增1 i += 1
++ i; // 让i自增1 i += 1
console.log(i)
i --; // 让i自减1 i -= 1
-- i; // 让i自减1 i -= 1
console.log(i)
var j = i ++; // i++表达式的结果是i自增前的值
console.log(i) // 11
console.log(j) // 10
var j = ++ i; // ++i表达式的结果是i自增后的值
console.log(i) // 11
console.log(j) // 11
var i = i ++; // 任何编程语言的赋值符号的优先级都是最低的
console.log(i) // 10
3.6 字符串操作
s.split() 字符串切割
s.substr(start, len) 字符串切割, 从start开始切, 切len个字符
s.substring(start, end) 字符串切割, 从start切割到end
s.length 字符串长度
s.charAt(i) 第i索引位置的字符
s.indexOf('xxx') 返回xxx的索引位置, 如果没有xxx. 则返回-1
s.includes("xxx") 判断xxx是否出现在s中.
s.toUpperCase() 转换成大写字母
s.startsWith("xxx") 判断是否以xxx开头
var s = "alex_wusir_sb"
// 切割
console.log(s.split("_"))
console.log(s.substr(3, 4)) // substr(start, len)
console.log(s.substring(3, 4)) // s[3:4] substring(start, end)
console.log(s.length) // len(s)
console.log(s.charAt(3)) // s[3]
console.log(s.indexOf("wusirdebaba")) // 如果返回的是-1没有,如果不是-1,就有
console.log(s.includes("wusirdebaba")) // 判断xxx是否存在,用得少
console.log(s.startsWith("alexdebaba"))
四. JS条件分支
除了HTML以外,几乎所有的编程语言都有条件判断的功能。比如,python,我们用if语句来做条件判断。到了javascript中也是一样的,也使用javascript来做条件上的判断。
4.1 if条件
// 语法
if(条件1){
代码块1
}
// 解读: 当`条件1`成立时, 执行代`码块1`中的内容, 如果`条件1`不成立. 则不执行该`代码块1`中的内容
// 注, 如果代`码块1`中的内容只有一行. 则可以省略外面的大括号(一些逆向工程里会有)
// 语法
if(条件1){
代码块1
} else {
代码块2
}
// 解读: 当`条件1`成立时, 执行`代码块1`中的内容, 如果`条件1`不成立. 则执行`代码块2`中的内容
// 语法
if(条件1){
代码块1
} else if(条件2) {
代码块2
} else if(条件3) {
代码块3
} ... {
代码块n
} else {
代码块else
}
// 解读: 当`条件1`成立时, 执行`代码块1`中的内容, 如果`条件2`不成立. 则执行`代码块2`中的内容...如果都不成立, 最终执行`代码块else`中的内容.
4.2 switch - case条件
switch语句,该语句是python中不存在的,但是在Java和C,以及JS中依然会有使用
switch(变量){
case 值1:
代码块1
break // 可选
case 值2:
代码块2
break // 可选
case 值3:
代码块3
break // 可选
default: // 可选
default代码块
}
/*
解读:
执行时,
switch会判断变量的值是否是`值1`,
如果是, 则执行代码块1以及代码块1中的break,
如果不是, 则继续判断`值2`...
如果前面的`值`都没有和`变量`相等的.则执行`default代码块`.
注意,
每一个`case`中都可以选择`break`, 也可以不选择`break`, 需要注意的是, 如果不写`break`.
那么就会形成`case穿透`现象.
例, `变量`的值如果和`值1` 相等. 并且case1中没有写`break`,
则在执行的时候. 会执行完`case1`中的代码.
然后会自动穿透到`case2`中去执行里面的代码, 而不经过case2中的数据的验证.
*/
五. JS中的循环语句
5.1 while循环
在js中有三种循环语句,首先是while循环。它的逻辑和咱们python中的while几乎一模一样,就是符号上有些许的区别。
// 语法
while (条件) {
循环体 -> 里面可以有break和continue等关键字
}
/*
判断`条件`是否为真, 如果`真`, 则执行`循环体`.执行完`循环体`, 会再次判断`条件`....
并且在循环中也可以使用`break`和`continue`等关键字来控制循环的走向.
*/
5.2 do - while循环
// 语法
do{
循环体
} while(条件);
/*
解读:
先执行`循环体`, 然后判断`条件`是否成立, 如果成立.在来一次.
注意, 由于do..while是先执行的`循环体`. 所以, 不论条件如何, 至少执行一次`循环体`
*/
5.3 for循环 (两种方式)
// 语法: for的第一种语法
for(表达式1; 表达式2; 表达式3){
循环体
}
/*
解读:
for循环和我们python中的循环是完全不一样的. 解读起来会有点儿麻烦.
首先, 在执行的时候, 先执行`表达式1`,
然后, 判断`表达式2`得到的结果是否真, 如果`真`, 则执行循环体,
再然后, 执行`表达式3`,
再然后, 判断`表达式2`执行的结果是否为`真`, 如果`真`, 则执行`循环体`
再然后, 执行`表达式3`
.....
直到, `表达式2`得到的结果是`假`, 则跳出循环
*/
// 看起来很绕. 我们用for循环来跑一个1~99
for(var i = 1; i < 100; i++){
console.log(i);
}
/*
首先, i = 1,
然后, 判断 i < 100 成立
打印i
在然后, i++, i变成2
再然后, 判断 i < 100 还是成立
打印i
再然后, i++, i变成3
再然后, 判断 i< 100 还是成立
打印3....
....
当i = 100了. i < 100不成立. 程序结束
*/
// for循环的固定逻辑也就这样了
for(变量声明; 条件判断; 改变变量){
循环体
}
// for的第二种用法
var a = [11,22,33,44,55,66]
for(let i in a){
console.log(i + "_" + a[i])
}
// 这种写法非常类似python中的for循环. 但是要注意. 这里的`i`拿到的仅仅是 `数组a`的索引信息.
// 如果需要数据 a[i]
// 列表特有的一个方案
var arr = [11, 22, 33, 44]
arr.forEach(function(item) { // 让arr中的每一个去执行一次里面的函数. 把被循环的内容一个一个的传递给函数
console.log(item);
}) // map() ?????
六. JS中的数组和对象
6.1 数组
在JS中创建数组非常简单,直接[ ]即可。也可以用正规军的new Array(),不过效果都是一样的。
var as = [11,22,33,44,55];
var bs = new Array(11,22,33,44,55)
数组的常用操作
arr.length; // 数组长度
arr.push(data); // 添加数据
arr.pop(); // 删除数据
// arr中的每一项循环出来. 分别去调用function函数, 会自动的将`数据`传递给函数的第一个参数
arr.forEach(function(e, i){ // 第二个参数是可选的
console.log(i+"__"+e);
});
arr.join("连接符"); // 使用`连接符`将arr中的每一项拼接起来. 和python中的 "".join()雷同
6.2 对象
在JS中创建一个对象非常容易,和python中的字典几乎一样{ }
var p = {
name: "汪峰",
age: 18,
wife: "章子怡",
chi: function(){
console.log("吃饭")
}
};
使用对象
p.name
p.age
p['wife']
p.chi()
p['chi']()
从上述内容中几乎可以看到,JS对象的使用几乎是没有门槛的,十分灵活
for(var n in p){
if(typeof(p[n]) != 'function'){
console.log(p[n])
}
}
七. JS中的函数(重点)
在JS中声明函数和python差不多,也要有一个关键字顶在前面。python是def
,到了JS里换成了function
,只不过在JS中没有像python那么死板,必须def
后面必须跟上函数名,这也为我们未来做逆向提供了第一个超大的伏笔。
// 语法
// 声明函数
function 函数名(形参1, 形参2, 形参3....){
函数体
return 返回值
}
// 调用函数
函数名(实参1, 实参2, 实参3....)
// 除了写法换了一丢丢. 其他的东西和python完全一致,
简单来个案例看看
function an(a, b){
return a + b
}
ret = an(1, 2)
console.log(ret); // 3
很简单不是么?那为什么我们在网页上看到一些网站的JS是如此的复杂呢?
注意了,JS其实没有一个非常非常严格的语法规则。只要能够形成 xxx() 并且xxx是一个函数的话就可以执行.
例如:
var an = function(a, b){
return a + b
}
an(1, 2) // an虽然是var声明的, 但是它的指向是一个函数. 那就可以执行
var $ = function(a, b){
}
$(1, 2) // $没有被JS使用. 我就可以用啊. _也OK
// 这个也很过分. 这个东东要拆开来看 第一个括号里面放的就是一个函数啊. 所以依然可以执行.
(function(a, b){
return a + b}
)(1, 2)
c = (function(){
var m = {
name:'alex',
age:'18',
xijiao: function(a){
console.log(a+"来帮我洗脚");
}
}
return m
})
// 还有最后一个问题. 未来我们也会遇到的. 就是它这个return
var an = function(){
return console.log("我爱你"), console.log("爱你妹"), "哈哈"
}
// 注意我们发现js会把return后的每一个,都执行一次. 但是最终真正的返回值其实是最后的那个"哈哈"