关于 js:5. 浏览器环境基础

一、window 对象

window 是浏览器中最顶层的全局对象,所有变量、函数、DOM、BOM 等都归属于它。

可理解为:

// 这两句是等价的
var a = 123;
window.a === 123; // true

1. window 的核心作用

分类说明
全局作用域JS 中所有全局变量、函数、对象都挂在 window
浏览器控制可以操作地址栏、历史记录、弹窗、窗口等
定时器控制提供 setTimeout、setInterval 等方法
页面信息获取提供 locationnavigatorscreen
事件注册支持绑定事件、监听页面加载
检测 webdriver是反爬虫中常用于检测 puppeteer 等的目标对象

2. 常用属性和方法分类

1)全局变量、函数存储器

var a = 100;
console.log(window.a); // 100

function sayHi() {}
console.log(window.sayHi); // ƒ sayHi() {}

2)DOM 接口入口

window.document // 当前页面的 DOM 树
window.document.body // 获取 body 节点

3)地址栏与跳转

window.location.href // 当前 URL
window.location.reload() // 刷新
window.location.assign('https://baidu.com') // 跳转

4)浏览器信息

window.navigator.userAgent // 浏览器 UA
window.navigator.webdriver // true 表示是自动化环境(如 puppeteer)

5)定时器

window.setTimeout(() => console.log('1秒后执行'), 1000);
window.setInterval(() => console.log('循环执行'), 2000);
window.clearTimeout(timerId); // 清除 timeout
window.clearInterval(intervalId); // 清除 interval

6)弹窗函数

window.alert('提示');     // 弹窗提示
window.confirm('确认吗'); // 返回 true/false
window.prompt('请输入');  // 输入框

7)事件监听

window.addEventListener('load', () => {
  console.log('页面加载完成');
});

window.addEventListener('resize', () => {
  console.log('窗口大小变化');
});

8)本地存储

// 在浏览器的 localStorage 中设置一条永久(直到手动删除)的键值对。
window.localStorage.setItem('key', 'value');
// 在当前“标签页”的 sessionStorage 中设置一条临时键值对。
window.sessionStorage.setItem('sid', 'xyz');

9)浏览器窗口操作(弹窗、关闭)

let win = window.open('https://example.com');
win.close(); // 关闭窗口

3. 反爬虫/逆向中 window 的关键点

用法说明
window.navigator.webdriver检测是否为自动化浏览器环境
window.outerHeight / innerHeight检测是否为真实用户行为(Puppeteer 常伪装失败)
window.chromeChrome 环境特征,某些网站用来检测
window.performance.timing检测加载时间,识别脚本干扰行为
window.name某些网站用来存信息(登录 token、来源判断)

4. 常见提权手法(JS 逆向中)

在 JS 逆向中经常看到某些函数、对象挂在 window 上:

window._encrypt = function (s) {
  // 混淆过的加密函数
};

逆向流程中我们会这样做:

  • 控制台输入:window._encrypt.toString() 查看源码。

  • hook 它:

window._encrypt = new Proxy(window._encrypt, {
  apply(target, thisArg, args) {
    console.log('加密入参:', args);
    return target.apply(thisArg, args);
  }
});

总结

类别方法/属性用途
控制页面window.location跳转、刷新
页面信息window.navigator浏览器信息、反爬点
DOM 接口window.document页面结构分析入口
本地存储window.localStorage / sessionStorage获取登录态、参数
时间控制setTimeout / setInterval延时/周期任务
事件window.addEventListener页面加载、大小变化
自动化检测window.navigator.webdriver判断是否是爬虫环境

二、document 对象

documentwindow.document,表示当前网页的 DOM(文档对象模型)树

可以理解成:document 就是 JS 操作 HTML 的入口,是“页面”的 JS 表示。

1. DOM 树结构概念

以一个简单的 HTML 为例:

<html>
  <head><title>页面标题</title></head>
  <body>
    <div id="box">Hello</div>
  </body>
</html>

JS 中 DOM 树是由各种 节点对象(Node) 构成的,主要包括:

  • 元素节点(Element)

  • 文本节点(Text)

  • 注释节点(Comment)

  • 属性节点(Attr)

document 是 DOM 树的根节点,可以从它开始遍历、查询、修改整个页面内容。

2. 常用 API 分类讲解

1)元素选择器(核心)

单个选择器

document.getElementById("box") // 通过 id
document.querySelector("#box") // 支持 css 选择器

多个元素

document.getElementsByClassName("item") // HTMLCollection(类数组)
document.getElementsByTagName("div")
document.querySelectorAll(".item") // NodeList(可遍历)

2)节点属性获取

let el = document.getElementById("box");

el.innerText     // 只获取用户可见的文本
el.innerHTML     // 获取内部 HTML 内容
el.textContent   // 获取所有文本(包括隐藏的)
el.id            // 获取 id
el.className     // 获取类名
el.getAttribute("data-id") // 获取自定义属性

爬虫中经常用 innerText 提取内容、用 getAttribute 提取加密参数。

3)DOM 修改操作(动态分析重点)

设置文本/HTML

el.innerText = "修改后";
el.innerHTML = "<b>加粗</b>";

添加/删除子节点

let newEl = document.createElement("p");
newEl.innerText = "新段落";
document.body.appendChild(newEl); // 加到页面中

el.remove(); // 删除这个元素

4)事件绑定

document.getElementById("btn").addEventListener("click", function (e) {
  console.log("按钮点击了");
});

可以动态 hook 某些点击行为、输入行为,分析加密逻辑。

5)DOM 遍历与查找

el.parentNode      // 父节点
el.children        // 所有子节点
el.firstChild      // 第一个子节点(可能是文本)
el.nextElementSibling // 下一个元素

6)动态创建元素(JS 动态渲染网页)

let img = document.createElement("img");
img.src = "xxx.jpg";
document.body.appendChild(img);

在逆向过程中,很多 JS 会在 DOMContentLoaded 后再通过 JS 创建元素 —— 所以就得等它渲染完再提取。

7)常见写法总结

用法含义
document.body获取 <body> 元素
document.documentElement获取 <html> 元素
document.title设置/获取页面标题
document.cookie读写 Cookie
document.readyState页面加载状态:loading / interactive / complete

3. 爬虫/逆向常见使用场景

场景1:页面参数嵌在 HTML 中

<input type="hidden" id="w" value="加密参数123" />
let w = document.getElementById("w").value; // 获取参数

场景2:数据写在 DOM 中被加密 JS 使用

let token = document.querySelector('meta[name="csrf-token"]').getAttribute("content");

场景3:数据是 JS 动态渲染的

你可能需要等待 DOMContentLoaded 或手动触发渲染函数:

document.addEventListener("DOMContentLoaded", function () {
  // 等待 JS 动态填充数据后再提取
});

4. 特殊技巧与逆向用途

Hook DOM 方法

let old = document.createElement;
document.createElement = function(tag) {
  console.log("正在创建:", tag);
  return old.call(this, tag);
}

用于监控某些脚本动态创建 iframe、canvas 等可疑元素。

重写 document.write

document.write = function(str) {
  console.log("页面写入了", str);
}

总结

功能常用方法
获取元素getElementById, querySelector, getElementsByClassName
读取属性innerText, innerHTML, getAttribute()
修改内容innerText=, innerHTML=, setAttribute()
添加/删除节点createElement, appendChild, remove()
动态监听addEventListener, DOMContentLoaded
Cookie 操作document.cookie
页面状态document.readyState, document.title

三、navigator对象

  • navigatorwindow.navigator,表示浏览器的客户端信息对象

  • 它不控制页面,而是提供有关浏览器、设备、操作系统、网络、语言等信息。

 经常用于:

  • 浏览器特征识别(UA、语言、平台)

  • 判断是否为爬虫环境(webdriver 等)

  • 获取插件、硬件信息(mediaDevices)

  • 某些 JS 加密/验证逻辑也依赖其中字段

1. 常用属性分类整理

类型关键字段作用
浏览器信息userAgent, appName, appVersion判断浏览器
系统平台platform, language, languages判断系统和语言
自动化检测webdriver, permissions检测是否为 Puppeteer 等
硬件信息deviceMemory, hardwareConcurrency伪装真实设备参数
插件信息plugins, mimeTypes用于反爬虫检测插件差异
多媒体mediaDevices获取摄像头、麦克风设备
安全性credentials, cookieEnabled获取 cookie 支持信息等

2. 常用属性详解 + 示例

1)navigator.userAgent – 浏览器 UA

console.log(navigator.userAgent);
// eg: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...

用途:

  • 网站根据它判断系统、浏览器类型

  • 反爬中常校验其是否伪造(过于简洁/不一致就会被判定为爬虫)

  • puppeteer 默认 UA 非常明显,会被识别

2)navigator.webdriver – 自动化检测最重要字段!

navigator.webdriver // true 表示是 WebDriver 控制的浏览器(如 puppeteer)

这是反爬网站检测最常用的字段!默认使用 Puppeteer 启动的浏览器中该字段是 true

绕过方式:

  • 使用 Object.defineProperty 伪造:

Object.defineProperty(navigator, 'webdriver', {
  get: () => false,
});

3)navigator.language / languages

navigator.language       // 'zh-CN'
navigator.languages      // ['zh-CN', 'en-US']
  • 表示浏览器的语言偏好

  • 多用于防止国外 bot 爬取国内网站,或判断是否为国外用户

4)navigator.platform

navigator.platform  // 'Win32' / 'Linux x86_64' / 'MacIntel'
  • 真实操作系统平台信息

  • 与 UA 对不上,会被认为是伪造环境

5)navigator.plugins / navigator.mimeTypes

navigator.plugins.length   // 插件数量
navigator.plugins[0].name  // 插件名称
  • Chrome 插件大多数默认加载

  • 无插件、或插件结构不一致,很容易被识别为“伪造浏览器”或 headless 环境

6)navigator.deviceMemoryhardwareConcurrency

navigator.deviceMemory        // 8 (单位:GB)
navigator.hardwareConcurrency // 8 (逻辑核心数)
  • 表示设备的性能能力

  • 许多反爬虫系统通过检测这些字段判断是否为真实设备(虚拟机环境通常数值较低)

7)navigator.cookieEnabled

navigator.cookieEnabled // 是否允许使用 Cookie(true/false)
  • 通常是 true

  • 如果 false,一些验证逻辑或登录可能失败

8)navigator.mediaDevices(高级用法)

navigator.mediaDevices.enumerateDevices().then(devices => {
  console.log(devices);
});
  • 获取摄像头、麦克风等信息

  • 自动化环境经常获取不到设备,也会成为检测点

9)navigator.credentials

管理网站登录凭据(用于自动登录系统),基本不用但存在于浏览器标准中。

3. 反爬虫检测常用字段

字段说明默认值(真实浏览器)
navigator.webdriver是否为 WebDriver 控制false
navigator.plugins插件列表有一定数量
navigator.languages浏览器语言至少 1 个
navigator.deviceMemory设备内存通常为 4~16
navigator.hardwareConcurrency逻辑核心数通常为 4~16
navigator.userAgent用户代理字符串真实结构复杂

这些字段在自动化环境下常常出现“异常”,例如:

  • webdriver = true

  • plugins.length = 0

  • deviceMemory = undefined

  • languages.length = 0

5. 逆向/自动化中实用技巧

检测并 Hook navigator 字段(console 中调试):

// hook UA
Object.defineProperty(navigator, 'userAgent', {
  get: () => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
});

// hook webdriver
Object.defineProperty(navigator, 'webdriver', {
  get: () => false
});

自动化环境中手动设置 navigator 字段(绕过检测)

在 puppeteer 启动脚本中加:

await page.evaluateOnNewDocument(() => {
  Object.defineProperty(navigator, 'webdriver', {
    get: () => false,
  });
});

总结

功能类型常用属性/方法说明
浏览器识别userAgent, appName判断设备
自动化检测webdriver, plugins爬虫检测核心
系统语言language, languages区域识别
性能识别deviceMemory, hardwareConcurrency是否是低配虚拟机
多媒体检测mediaDevices检查摄像头、麦克风
安全性cookieEnabled, credentials基础检测

四、location 对象

  • locationwindow.location 的简写,表示当前窗口的地址栏 URL 信息

  • 它是一个可读可写的对象,可以:

    • 读取当前网址

    • 提取各部分 URL 参数

    • 实现跳转、重定向、刷新

1. 常用属性列表

假设当前 URL 为:

https://www.example.com:8080/path/page.html?token=abc123#section2

对应字段如下:

属性含义
location.href整个地址https://www.example.com:8080/path/page.html?token=abc123#section2
location.protocol协议https:
location.hostname主机名www.example.com
location.port端口8080
location.host主机 + 端口www.example.com:8080
location.pathname路径/path/page.html
location.searchURL 查询字符串(带 ?)?token=abc123
location.hash哈希值(# 之后)#section2

2. 实战用法与示例

1)获取当前 URL 各部分参数

location.href        // 获取整个地址
location.hostname    // www.example.com
location.pathname    // /path/page.html
location.search      // ?token=abc123
location.hash        // #section2

2)提取 URL 参数(token、id 等)

方法 1:原始方法

let params = new URLSearchParams(location.search);
let token = params.get('token'); // abc123

方法 2:手动拆解

let search = location.search.substring(1); // 去掉 ?
let obj = {};
search.split('&').forEach(pair => {
  let [k, v] = pair.split('=');
  obj[k] = v;
});
console.log(obj['token']);

3)页面跳转 / 重定向

// 跳转到新页面(可被后退按钮记录)
location.href = 'https://baidu.com';

// 跳转并替换历史记录(不可回退)
location.replace('https://baidu.com');

// 刷新页面(等价于 F5)
location.reload();

4)控制 hash 页面锚点跳转

location.hash = '#section3'; // 页面跳转到锚点位置

很多单页应用(SPA)利用 hash 控制前端路由。

5)动态构造请求 URL

当 JS 加密数据要提交到某接口(如 submit?callback=__jsonp123),可以:

let url = '/submit?callback=' + encodeURIComponent(callback);

3. 逆向与反爬虫常见用法

1)判断是否在正确页面环境

if (location.hostname !== 'www.realhost.com') {
  alert('非法访问!');
}

在模拟器或爬虫环境里访问,可能会触发这种逻辑。

2)根据参数生成 JS 加密字段

很多站会这样写:

let token = location.search.match(/token=(.*?)&/)[1];
let sign = md5(token + 'salt');

必须正确还原 token 参数才能逆向 sign 的生成。

3)重定向跳转验证机制

有的网站加密流程如下:

  • 页面 A 获取参数 → JS 加密生成跳转链接

  • 使用 location.href = 目标链接

  • 若参数不对,会被重定向回 A

可用 location.href Hook 或打印看跳转流程:

// 保存原始 setter
const originalHref = Object.getOwnPropertyDescriptor(window.location.__proto__, 'href');

// 重写 getter 和 setter
Object.defineProperty(window.location, 'href', {
  get() {
    console.log('[HOOK] 读取 location.href');
    return originalHref.get.call(location);
  },
  set(val) {
    console.log('[HOOK] 设置 location.href =', val);
    debugger; // 可以打断点看堆栈
    return originalHref.set.call(location, val);
  }
});

或更简单方式:

// Hook 写操作
Object.defineProperty(window.location, 'href', {
  set(val) {
    console.log('[跳转拦截] href 被设置为:', val);
    debugger;
  }
});

有些浏览器可能不允许直接修改 location.href 的原型链属性(安全限制)。

可以考虑改 window.location 为代理对象或使用 Proxy(进阶玩法):

const loc = window.location;
Object.defineProperty(window, 'location', {
  get() {
    return loc;
  },
  set(val) {
    console.log('location 被设置为:', val);
    debugger;
  }
});

4)利用 location 实现伪装跳转(绕过 Referer 检测)

let jumpUrl = 'https://target.com/api?refer=' + encodeURIComponent(document.referrer);
location.href = jumpUrl;

总结

任务属性/方法示例值
获取完整 URLlocation.hrefhttps://xx.com?a=1
获取主机名location.hostnamexx.com
获取路径location.pathname/page
获取查询参数location.search?a=1&b=2
获取锚点location.hash#part1
跳转新页面location.href = url页面跳走
替换页面location.replace(url)跳转但无法返回
刷新页面location.reload()页面刷新

五、DOM 操作

DOM 是浏览器提供的接口,让 JS 可以操作网页的结构、样式、内容

可以理解为:
网页加载后,HTML 被浏览器“转化”为一个对象模型 —— document,可以用 JS 访问、修改、删除、添加这些 HTML 元素和内容。

1. 常用 DOM 查询方式

方法说明示例
document.getElementById(id)通过 ID 获取元素document.getElementById('btn')
document.getElementsByClassName(cls)获取某类名的元素列表document.getElementsByClassName('item')[0]
document.getElementsByTagName(tag)获取某标签元素document.getElementsByTagName('a')
document.querySelector(sel)返回第一个匹配的元素(推荐)document.querySelector('.price')
document.querySelectorAll(sel)返回所有匹配元素 NodeListdocument.querySelectorAll('.list li')

querySelector 是逆向中最常用的,因为它支持 CSS 选择器,非常灵活。

2. DOM 节点操作(提取动态数据核心)

1)读取元素文本内容

document.querySelector('.price').innerText;   // 可见文本(常用于展示价格)
document.querySelector('.price').textContent; // 所有文本(包括隐藏的)

在加密或反爬网站中,价格、token、id 等常被隐藏在页面里。

2)获取 HTML 属性值

let link = document.querySelector('a');
console.log(link.href);            // 完整跳转链接
console.log(link.getAttribute('href')); // 原始 href 属性内容

这在处理跳转验证、提取 data-token 这类自定义属性时非常关键。

3)设置/修改内容

let el = document.querySelector('#username');
el.innerText = 'newText';
el.setAttribute('data-val', 'abc');

逆向调试时可以用这些方法修改页面中的 DOM 内容,测试结果是否影响加密逻辑。

4)创建/插入/删除元素

let div = document.createElement('div');
div.innerText = 'Hello';
document.body.appendChild(div);  // 插入到页面中
document.body.removeChild(div);  // 删除

自动化环境中经常利用此方法插入 DOM,用于模拟用户行为或 hook 页面 JS。

5)监听节点变化(检测某些动态加载元素)

let observer = new MutationObserver((mutations) => {
  console.log('DOM 发生变化');
});
observer.observe(document.body, { childList: true, subtree: true });

如果某些加密字段是通过 JS 动态加载后插入 DOM 的,可以用这个方式监听加载时机。

3. 逆向实战常见场景举例

场景 1:从隐藏字段提取 token

<input type="hidden" id="auth" value="abc123">
let token = document.querySelector('#auth').value;

场景 2:提取 JS 混淆中写入 DOM 的数据

<script>
  (function(){
    var a = 'abc';
    var b = a + '123';
    document.querySelector('#sign').setAttribute('data-sign', b);
  })();
</script>

就要:

document.querySelector('#sign').getAttribute('data-sign'); // 得到 'abc123'

场景 3:复杂网页中抓取列表数据

<ul class="list">
  <li><span class="price">¥199</span></li>
  <li><span class="price">¥299</span></li>
</ul>
document.querySelectorAll('.list .price').forEach(el => {
  console.log(el.innerText); // 循环打印所有价格
});

场景 4:网站用 DOM 节点储存加密参数

<div id="data" data-key="a1b2c3" data-sign="xxx123"></div>
let key = document.querySelector('#data').dataset.key; // a1b2c3
let sign = document.querySelector('#data').dataset.sign; // xxx123

dataset 是对所有 data-xxx 属性的封装,逆向中非常常见!

4. 爬虫自动化控制中的 DOM 操作

JS 中操作Selenium/Puppeteer 中实现
querySelectorfind_element(By.CSS_SELECTOR, 'xxx')
innerTextelement.text
valueelement.get_attribute('value')
click()element.click()
setAttributedriver.execute_script(...)
dataset.xxxelement.get_attribute('data-xxx')

总结

DOM 操作是 JS 逆向和自动化的“手眼”,通过它才能真正看到页面上 JS 动态写入了哪些加密参数、展示了哪些真实数据、触发了哪些事件


六、事件模型(捕获/冒泡、事件绑定、移除)

事件模型是 JS 浏览器中事件触发的传播机制,主要包括三个阶段:

三个阶段(重点)

 捕获阶段 →  目标阶段 →  冒泡阶段
  • 捕获阶段(capture phase):从 window -> document -> html -> ... 一层层向下传递,直到目标元素。

  • 目标阶段(target phase):事件到达目标元素本身。

  • 冒泡阶段(bubble phase):从目标元素向上传递,直到 window

1. 示例理解:捕获 VS 冒泡

HTML 示例:

<div id="outer">
  <button id="inner">Click Me</button>
</div>

JS 绑定:

document.getElementById('outer').addEventListener('click', () => {
  console.log('outer click');
}, true); // 捕获阶段

document.getElementById('inner').addEventListener('click', () => {
  console.log('inner click');
}, false); // 冒泡阶段(默认)

点击按钮后输出顺序是:

outer click   捕获
inner click   冒泡

如果都用冒泡阶段(false),就会输出:

inner click
outer click

事件监听函数中的 truefalse 是什么?

element.addEventListener(eventType, handler, useCapture);

这个第三个参数 useCapture

含义
true事件在 捕获阶段 执行这个监听函数
false(默认)事件在 冒泡阶段 执行这个监听函数

2. 事件绑定方法

1)addEventListener(推荐使用)

element.addEventListener('click', handler, useCapture);
  • useCapturetrue:绑定在捕获阶段

  • useCapturefalse(默认):绑定在冒泡阶段

示例:

button.addEventListener('click', function () {
  alert('clicked');
}, false); // 冒泡阶段绑定

2)DOM0 级绑定(不推荐)

element.onclick = function() {
  alert('clicked');
};

劣势:

  • 覆盖已有处理函数

  • 不支持多个监听器

3)移除事件监听

function handler(e) {
  console.log('clicked');
}
element.addEventListener('click', handler);

// 移除监听
element.removeEventListener('click', handler);

注意:

  • 必须是同一个函数对象才能移除

  • 所以不要写成匿名函数 () => {},否则无法移除

3. 事件对象和控制方法

事件处理函数会默认接收一个参数:event,它是事件对象。

button.addEventListener('click', function(event) {
  console.log(event.type); // click
});

常用控制方法:

方法作用
event.stopPropagation()阻止事件冒泡(不向父元素传递)
event.preventDefault()阻止默认行为(如点击 a 不跳转)
event.target触发事件的真实目标元素
event.currentTarget当前绑定监听器的元素
event.stopImmediatePropagation()阻止后续绑定的同类事件执行

案例:阻止冒泡和默认行为

document.querySelector('a').addEventListener('click', function(event) {
  event.preventDefault();      // 不跳转
  event.stopPropagation();     // 不冒泡
});

4. 逆向常见用途

1)抓事件中传参加密的处理逻辑

button.addEventListener('click', function() {
  let token = genToken();
  fetch(`/submit?token=${token}`);
});

用于抓取 点击时才生成的加密参数(如验证码、签名等)

2)Hook 事件阻止点击跳转/检测

document.querySelectorAll('a').forEach(a => {
  a.addEventListener('click', (e) => {
    e.preventDefault(); // 不跳转
    console.log('跳转拦截', a.href);
  });
});

3)模拟用户点击行为(自动化)

document.querySelector('#btn').click();

等价于用户真正点击按钮,事件处理器会触发。

4)Hook 所有点击事件(调试/逆向神器)

document.addEventListener('click', function(e) {
  console.log('点击了:', e.target);
}, true); // 捕获阶段,拦截所有点击

5)强行移除某个事件绑定

可以在控制台移除网站的跳转限制:

let btn = document.querySelector('#submit');
btn.onclick = null; // 清空 onclick

总结

概念描述
捕获阶段从最外层往目标元素传递事件
冒泡阶段从目标向外层回传事件
addEventListener推荐的事件绑定方式
removeEventListener移除事件监听器
stopPropagation阻止冒泡
preventDefault阻止默认行为
click()触发点击事件

七、cookie

Cookie 是浏览器为网站存储的小型文本信息,用来在客户端保存:

  • 登录凭证(如 token=abc123

  • 用户 ID / 登录状态

  • 一些认证或追踪参数(例如 captcha_token, sessionid

当浏览器再次请求该网站时,Cookie 会自动带到请求头中,帮助服务器识别用户状态。

1. Cookie 的组成结构

每条 Cookie 是一个键值对,还可以包含附加属性:

Set-Cookie: name=value; Path=/; Domain=xx.com; Expires=日期; Secure; HttpOnly; SameSite
属性作用
name=value键值对
Path哪个路径下有效(默认当前)
Domain哪个域名下有效(默认当前域)
Expires过期时间(持久 cookie)
Max-Age有效秒数(优先于 Expires)
Secure仅 HTTPS 传输
HttpOnlyJS 无法读取(防止 XSS)
SameSite限制跨站请求时是否携带 Cookie(防 CSRF)

2. JS 中操作 Cookie(核心)

浏览器端操作 Cookie 使用 document.cookie,注意它是一个字符串接口

1)读取 Cookie

console.log(document.cookie);

输出例子:

token=abc123; uid=10086; theme=dark

如果想获取某一个 cookie:

function getCookie(name) {
  let match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
  return match ? match[2] : null;
}

getCookie('token'); // 返回 abc123

2)设置 Cookie

document.cookie = "token=abc123; path=/; max-age=3600";

说明:

  • 设置的是一条 cookie

  • 多次设置会追加,而不是覆盖全部

  • path=/:整个网站都有效

  • max-age=3600:保存 1 小时

只要 path、domain 不同,浏览器允许存在多个同名 Cookie!

3)删除 Cookie

document.cookie = "token=; path=/; max-age=0";

实质上是通过设置过期时间让浏览器自动清除。

3. Cookie 的 JS 限制

限制点说明
不能读写 HttpOnly Cookie只能被服务端设置,防 XSS,JS 访问不到
每次只能设置一条 Cookiedocument.cookie = 只能处理单条
不能设置 Secure 属性Secure 只能由服务端设置,JS 无法控制

4. 逆向和爬虫中的 Cookie 用法

1)网站使用 JS 动态设置 Cookie

例如某反爬机制中,页面加载后通过 JS 设置一个 Cookie:

document.cookie = "__verify_token=abc123xyz; path=/; max-age=300";

如果用 requests 请求不会有这个 cookie,需要借助 Puppeteer/Selenium 来执行 JS。

2)Cookie 控制访问权限(登录态)

某些页面必须携带 token:

Cookie: token=abc123; uid=10086

可以在登录后提取浏览器中的 Cookie,用来模拟登录状态。

3)利用 DevTools 手动修改 Cookie

可以打开控制台 ➜ Application ➜ Cookies,直接改写或注入:

document.cookie = "debug=true";

可触发某些“开发者调试页面”、“跳过验证码”等逻辑。

5. 安全属性详解

属性用途
HttpOnlyJS 无法访问(防止 XSS 窃取 cookie)
Secure仅在 HTTPS 下发送
SameSite限制跨站请求时是否带 cookie,防止 CSRF 攻击

SameSite 有 3 种值:

  • Strict:完全禁止第三方携带

  • Lax:GET 请求允许(表单、a 标签)

  • None:允许任何请求,但必须搭配 Secure

总结

操作示例代码
读取 cookiedocument.cookie
获取指定值用正则解析 cookie 字符串
设置 cookiedocument.cookie = 'token=abc123; path=/; max-age=3600'
删除 cookiedocument.cookie = 'token=; max-age=0'
限制JS 无法读写 HttpOnly / Secure / SameSite 设置的 cookie

八、localStorage与sessionStorage

localStorage 和 sessionStorage这两个存储 API 是现代浏览器中常用的客户端存储方式,它们比 Cookie 更适合存储较大的数据,并且在很多场景中(例如爬虫、自动化、Web 安全等)都非常有用。

localStorage

  • 持久化存储,数据即使浏览器关闭后依然存在。

  • 适用于需要长期保存的数据,比如用户设置、缓存数据、离线应用数据等。

  • 每个域名最多可以存储 5MB(浏览器不同可能略有差异)。

sessionStorage

  • 会话存储,数据在浏览器标签页关闭后即消失。

  • 适用于需要在页面会话之间保存数据的场景,比如单页应用(SPA)中的临时状态。

  • 每个域名最多存储 5MB(与 localStorage 相同)。

1. 基本 API 操作

1)存储数据:setItem

存储数据时,需要指定键(key)和值(value),它们都是字符串类型。

// 存储数据
localStorage.setItem('username', 'johnDoe');
sessionStorage.setItem('sessionID', 'abc123');

2)读取数据:getItem

通过 getItem 获取指定键的值,如果键不存在则返回 null

// 读取数据
let username = localStorage.getItem('username'); // 'johnDoe'
let sessionID = sessionStorage.getItem('sessionID'); // 'abc123'

3)删除数据:removeItem

删除指定键的数据,删除后再读取返回 null

// 删除数据
localStorage.removeItem('username');
sessionStorage.removeItem('sessionID');

4)清除所有数据:clear

清除当前域名下的所有 localStoragesessionStorage 数据。

// 清除所有数据
localStorage.clear();
sessionStorage.clear();

5)获取存储长度:length

返回当前存储中键值对的数量。

let localStorageLength = localStorage.length; // 记录有多少条数据

6)获取所有键:key

返回指定索引位置的键名。

// 获取第一个键
let firstKey = localStorage.key(0); // 'username' 等

2. localStoragesessionStorage 的差异

特性localStoragesessionStorage
生命周期持久化存储(即使关闭浏览器也存在)会话结束时自动清除,关闭标签页后消失
数据存储范围所有标签页共享(同一浏览器窗口或标签页中同一域名下)仅限当前标签页(标签页之间不可共享)
用途长期存储数据,适用于用户设置、缓存等短期存储数据,适用于临时状态
存储大小5MB(根据浏览器不同有所不同)5MB(与 localStorage 相同)

3. 常见操作场景(应用于爬虫、自动化等)

1)爬虫应用:模拟登录状态

很多网站会将登录状态等信息保存在 localStoragesessionStorage 中,可以通过提取并模拟这些存储的内容来模拟登录。

例如:

localStorage.setItem('authToken', 'xyz123');

在自动化脚本中,可以提取这些存储数据并模拟请求,避免使用 Cookie 模拟。

2)页面抓取动态数据:读取本地存储的数据

有些网站将数据缓存到 localStoragesessionStorage,可以通过访问它们来获取动态加载的数据,绕过一些反爬机制。

let data = localStorage.getItem('userData');

3)自动化测试:模拟用户行为(Puppeteer/Selenium)

在进行 Web 自动化测试时,可以通过 localStoragesessionStorage 来模拟用户登录态,减少每次测试时的重复登录步骤。

await page.evaluate(() => {
  localStorage.setItem('authToken', 'xyz123');
});

4)Web 安全:绕过认证

通过获取并手动设置某些网站的 localStoragesessionStorage,可以模拟用户的认证状态,绕过一些验证逻辑。

例如,修改存储的用户权限字段:

localStorage.setItem('userRole', 'admin');

总结

操作localStoragesessionStorage
存储数据localStorage.setItem(key, value)sessionStorage.setItem(key, value)
读取数据localStorage.getItem(key)sessionStorage.getItem(key)
删除数据localStorage.removeItem(key)sessionStorage.removeItem(key)
清除所有数据localStorage.clear()sessionStorage.clear()
存储范围同一域名下所有标签页共享仅当前标签页有效
生命周期持久化存储(关闭浏览器也存在)会话结束时自动清除
存储大小5MB5MB

九、定时器

JavaScript 提供了两个主要的定时器方法:

  • setTimeout:用于延时执行某个代码片段一次。

  • setInterval:用于周期性执行某个代码片段,每隔一段时间重复执行。

这两个定时器方法都会返回一个“定时器 ID”,通过该 ID 可以清除定时器。

1. setTimeout(延时执行)

setTimeout 用于设置一个延时执行的函数或代码块。

语法:

setTimeout(function, delay, arg1, arg2, ...)
  • function:指定需要执行的回调函数。

  • delay:延迟时间(以毫秒为单位,1 秒 = 1000 毫秒)。

  • arg1, arg2, ...:可选的参数,传递给回调函数。

示例:

// 延时 2 秒输出 "Hello, World!"
setTimeout(function() {
  console.log('Hello, World!');
}, 2000);

setTimeout 返回定时器 ID:

let timerId = setTimeout(function() {
  console.log('This will run after 2 seconds');
}, 2000);

timerId 可以用来取消定时器(通过 clearTimeout)。

清除定时器:

可以使用 clearTimeout() 来取消定时器,避免函数被执行。

clearTimeout(timerId);  // 取消定时器

2. setInterval(周期性执行)

setInterval 用于设置一个周期性执行的函数或代码块,每隔一段时间就会执行一次。

语法:

setInterval(function, interval, arg1, arg2, ...)
  • function:指定需要执行的回调函数。

  • interval:间隔时间(以毫秒为单位,1 秒 = 1000 毫秒)。

  • arg1, arg2, ...:可选的参数,传递给回调函数。

示例:

// 每 2 秒输出一次 "Ping"
setInterval(function() {
  console.log('Ping');
}, 2000);

setInterval 返回定时器 ID:

setTimeout 类似,setInterval 也返回一个定时器 ID,可以用来清除定时器。

let intervalId = setInterval(function() {
  console.log('This will run every 2 seconds');
}, 2000);

清除定时器:

使用 clearInterval() 来取消周期性执行的定时器。

clearInterval(intervalId);  // 停止定时器

3. 定时器的应用场景

1)延时执行某个操作:

常见的用途是延时执行某个函数,例如:动画效果加载提示延时请求等。

setTimeout(() => {
  alert('这是延时 3 秒弹出的对话框');
}, 3000);

2)周期性执行操作:

例如定期获取数据、定时刷新的操作。

let counter = 0;
let intervalId = setInterval(() => {
  console.log(`Count: ${counter}`);
  counter++;
  if (counter > 5) {
    clearInterval(intervalId);  // 达到 5 次后停止计数
  }
}, 1000);

3)模拟倒计时:

可以利用 setTimeoutsetInterval 来实现一个倒计时功能。

let count = 10;
let countdown = setInterval(() => {
  console.log(count);
  count--;
  if (count < 0) {
    clearInterval(countdown);
    console.log('时间到!');
  }
}, 1000);

总结

方法作用用法示例
setTimeout()延迟执行一次函数setTimeout(() => { alert('Hi'); }, 2000)
setInterval()周期性执行函数setInterval(() => { console.log('Ping'); }, 1000)
clearTimeout()清除定时器,停止 setTimeout()clearTimeout(timerId)
clearInterval()清除定时器,停止 setInterval()clearInterval(intervalId)

十、页面加载顺序

页面加载顺序是指当一个 Web 页面加载时,浏览器如何按一定的顺序处理和渲染页面内容。

1. 页面加载的整体流程

浏览器加载页面的过程大致可以分为以下几个阶段:

  1. DNS 查询

  2. 建立 TCP 连接

  3. 发送 HTTP 请求

  4. 服务器响应(返回 HTML)

  5. 解析 HTML

  6. 解析和执行 CSS

  7. 下载和执行 JavaScript

  8. 页面渲染

2. 详细的加载顺序

1)DNS 查询

当浏览器访问一个 URL 时,首先会进行 DNS 查询(域名解析)来将域名转换成 IP 地址。这个过程是异步的,可能会缓存先前解析的结果。

2)建立 TCP 连接

浏览器根据解析到的 IP 地址与服务器建立 TCP 连接。这包括了三次握手的过程,用来确保客户端与服务器之间的通信是可靠的。

3)发送 HTTP 请求

TCP 连接建立后,浏览器会通过 HTTP 协议向服务器发送请求,获取页面的 HTML 内容。

4)服务器响应(返回 HTML)

服务器处理请求后,会返回 HTML 内容给浏览器。此时,浏览器收到 HTML 后会开始解析和渲染。

5)解析 HTML

浏览器接收到 HTML 后会开始解析。浏览器会将 HTML 解析成 DOM(文档对象模型)树。HTML 中的标签会转换为 DOM 元素节点,浏览器根据 DOM 结构来构建页面的内容。

DOM 构建过程中,遇到 <script> 标签时会出现不同的情况:

  • 同步加载的 <script><script> 标签默认行为): 浏览器会立即停止解析 HTML,去加载并执行脚本。执行完脚本后,才会继续解析 HTML。如果脚本较大或加载较慢,会导致页面加载过程变慢。

  • 异步加载的 <script><script async>): async 属性告诉浏览器可以在后台异步加载脚本,并且不会阻塞 HTML 的解析。脚本加载完成后,会立即执行。

  • 延迟加载的 <script><script defer>): defer 属性告诉浏览器延迟执行脚本,直到整个 HTML 被解析完再执行。这种方式不会阻塞 HTML 解析。

6)解析和执行 CSS

在解析 HTML 的过程中,浏览器会遇到 <style><link> 标签,这时浏览器会去请求并解析 CSS。解析 CSS 的顺序是:

  • 浏览器会从头到尾逐行解析 HTML。

  • 当遇到 <style><link> 时,浏览器会请求外部 CSS 文件(如果有的话)并解析它们。

  • CSS 会影响页面的布局和样式,浏览器必须等待 CSS 文件完全加载和解析后,才能开始渲染页面。

CSS 文件加载会阻塞页面渲染:

如果在 HTML 中没有使用 asyncdefer 属性的 <script> 标签,浏览器会在获取到 CSS 之前,不会开始页面的渲染。这样可以确保样式在页面内容渲染之前已经准备好。

7)下载和执行 JavaScript

在 HTML 中,JavaScript 文件的加载与执行是一个关键的步骤。通常,JavaScript 会影响页面的结构、数据处理、用户交互等,因此脚本加载和执行的顺序非常重要。

  • 同步脚本: <script> 标签默认是同步加载和执行的,这会阻塞 HTML 的解析和渲染。浏览器会在执行完脚本后,继续解析剩余的 HTML。

  • 异步脚本: 使用 async 属性的 <script async> 会让浏览器异步下载 JavaScript 文件,并在文件下载完成后立即执行。此时,HTML 的解析不会被阻塞。

  • 延迟脚本: 使用 defer 属性的 <script defer> 会让浏览器将脚本的执行延迟到 HTML 解析完成之后才执行,确保 DOM 已经构建完毕。

8)页面渲染

当 HTML 和 CSS 被完全解析后,浏览器开始渲染页面。在此过程中,浏览器会按照 DOM 树和 CSS 样式规则,绘制页面的布局。这个过程包括了:

  • 构建渲染树(Render Tree): 浏览器根据 DOM 和 CSSOM(CSS 对象模型)树,生成渲染树,渲染树包含了每个节点的样式信息。

  • 布局: 根据渲染树,浏览器计算每个元素的最终位置和大小。

  • 绘制: 浏览器将元素绘制到屏幕上。

3. 影响页面加载的因素

1)脚本加载顺序

如果 JavaScript 脚本没有正确配置 asyncdefer,会导致页面在加载时阻塞。为了提高页面性能,通常建议将脚本放置在页面的底部,或者使用 defer 来确保脚本的加载不会阻塞页面的渲染。

2)外部资源的加载

页面中的图片、视频、字体、外部 CSS 和 JavaScript 文件等资源可能会增加加载时间,尤其是较大的资源会显著影响页面的加载速度。可以使用 lazyload(懒加载)技术来推迟图片或其他资源的加载,直到需要时才加载。

3)网络延迟与服务器性能

网络延迟和服务器响应速度也是影响页面加载的重要因素。通过减少 HTTP 请求数量、优化服务器响应时间等方法可以提高页面加载速度。

4. 优化页面加载顺序

1)使用 asyncdefer 加速 JavaScript 加载

通过合理使用 asyncdefer 属性,避免阻塞 HTML 的解析,提高页面加载速度。

<!-- 异步加载 JavaScript -->
<script async src="script.js"></script>

<!-- 延迟执行 JavaScript -->
<script defer src="script.js"></script>

2)将脚本放在页面底部

<script> 标签放在 HTML 的底部,确保页面的内容优先加载,然后再加载和执行脚本。

3)使用 HTTP/2

HTTP/2 协议支持并发请求和更有效的资源传输,通过减少请求的延迟和优化资源传输,显著提高加载速度。

4)压缩和合并资源

通过压缩 JavaScript 和 CSS 文件,减少文件大小;将多个 JavaScript 或 CSS 文件合并为一个文件,减少 HTTP 请求的数量。

5)使用缓存

合理利用浏览器缓存,减少重复请求,提高页面加载速度。

总结

  • 页面加载的顺序是浏览器获取、解析、渲染页面的过程,关键步骤包括 DNS 查询、建立连接、请求响应、HTML 解析、CSS 解析和 JS 执行。

  • <script> 标签的加载顺序和属性(asyncdefer)会影响页面的解析顺序。

  • 优化建议: 合理使用 asyncdefer、图片懒加载、文件合并压缩等技术,减少阻塞,提升页面加载性能。


十一、DOMContentLoaded

DOMContentLoaded 是前端开发、自动化测试、逆向爬虫中一个非常重要的事件。它表示 HTML 文档被完全加载和解析完成,但样式表、图片等其他资源可能还没有加载完成

DOMContentLoadeddocument 对象上的一个事件,它在HTML 完全加载并解析完成后被触发,不必等待 CSS、图片、iframe 等资源加载完成

对比几个常见事件的触发时机:

事件名触发时机
DOMContentLoadedDOM 树构建完成,外部资源如图片、CSS 可能还未加载
load所有资源加载完成(包括图片、视频、iframe 等)
beforeunload页面将要被关闭或刷新时触发

1. 事件触发流程(以浏览器加载为例)

加载顺序如下:

  1. 浏览器开始解析 HTML。

  2. 构建 DOM 树。

  3. 不等待图片、CSS 加载,DOM 构建完成后,立即触发 DOMContentLoaded

  4. 继续加载 CSS、图片、视频等资源。

  5. 所有资源加载完后,才触发 load 事件。

2. 常见用法

1)基本写法:

document.addEventListener("DOMContentLoaded", function () {
  console.log("DOM fully loaded and parsed");
  // 这里可以安全操作 DOM 元素
});

这样写的好处是可以确保你的 JS 操作在 DOM 完全构建后进行,防止找不到 DOM 元素的错误。

2)ES6 简写方式(在模块或框架中):

window.onload = () => {
  console.log("所有资源加载完成,包括图片等");
};

document.addEventListener("DOMContentLoaded", () => {
  console.log("只等 DOM,不等图片等资源");
});

3)区分两者(DOM 完成 vs 全部加载完成):

window.addEventListener("DOMContentLoaded", () => {
  console.log("DOM 完成(早)");
});

window.addEventListener("load", () => {
  console.log("所有资源加载完成(晚)");
});

3. 在爬虫/逆向中的应用

1)通过 Puppeteer/Playwright 等自动化工具等待 DOMContentLoaded:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });

说明:适用于只需要页面结构(DOM)但不需要图片等资源的场景(速度更快)。

2)绕过 JavaScript 动态加载:

有的网站会在 DOMContentLoaded 后通过 JS 动态插入数据。你可以监听 DOMContentLoaded 然后 hook 某些函数:

document.addEventListener("DOMContentLoaded", () => {
  const originalFetch = window.fetch;
  window.fetch = function () {
    console.log("拦截 fetch 请求");
    return originalFetch.apply(this, arguments);
  };
});

3)逆向分析中监听何时页面数据出现:

一些网站页面初始没有数据,而是在 DOMContentLoaded 后通过 JS 请求接口,可以 hook 这些操作。

4. 测试 DOMContentLoadedload 的时间差

可以打开 Chrome 控制台,执行:

window.addEventListener("load", () => {
  console.log("load 时间", performance.now());
});

document.addEventListener("DOMContentLoaded", () => {
  console.log("DOMContentLoaded 时间", performance.now());
});

会看到 DOMContentLoaded 一般更早触发几百毫秒到几秒。

总结

项目DOMContentLoadedload
触发时机DOM 解析完毕,不等图片和 CSS所有资源(DOM、CSS、图片、JS)都加载完毕
是否常用于爬虫/逆向? 是(快、数据早) 慢
是否适合操作 DOM? 是 也可以
### 逆向工程与反编译概述 逆向工程是一种通过对软件的目标代码进行分析,将其转化为更高级别的表示形式的过程。这一过程通常用于研究现有系统的内部结构、功能以及实现细节。在Java和Android领域,反编译工具被广泛应用于逆向工程中。 #### Java逆向工程中的Jad反编译工具 Jad是一款经典的Java反编译工具,能够将`.class`字节码文件转换为可读的`.java`源代码[^1]。虽然它可能无法完全恢复原始源代码,但它提供了足够的信息来帮助开发者理解已编译的Java程序逻辑。Jad支持多种反编译模式,并允许用户自定义规则以适应不同的需求。此外,其命令行接口和图形界面使得复杂代码的分析变得更加便捷。 #### Android逆向工程中的JEB反编译工具 针对Android应用的逆向工程,JEB是由PNF Software开发的一款专业级工具[^2]。相较于其他同类产品,JEB不仅具备强大的APK文件反编译能力,还能对Dalvik字节码执行高效而精准的操作。它的核心优势在于以下几个方面: - **广泛的平台兼容性**:除Android外,还支持ARM、MIPS等多种架构的二进制文件反汇编。 - **混淆代码解析**:内置模块能有效应对高度混淆的代码,提供分层重构机制以便于深入分析。 - **API集成支持**:允许通过编写Python或Java脚本来扩展功能并完成特定任务。 #### APK反编译流程及其意义 当涉及到具体的APK包时,可以通过一系列步骤提取其中的信息来进行全面的安全评估或者学习目的的研究工作[^3]。这些步骤一般包括但不限于获取资产目录(`assets`)内的资源数据;解密XML配置文档如`AndroidManifest.xml`定位应用程序启动点;最后利用上述提到的各种专用软件重现整个项目框架供进一步探讨。 ```bash # 使用apktool反编译APK示例 apktool d your_app.apk -o output_directory/ ``` 以上命令展示了如何借助开源工具ApkTool轻松拆卸目标安卓档案至易于探索的状态下。 ### 结论 无论是传统的桌面端还是现代移动端环境里头,恰当运用合适的反编译解决方案都是达成逆向工程项目成功不可或缺的一环。每种工具有各自专精之处,在实际应用场景当中应当依据具体需求做出明智的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值