影刀RPA进阶教程:浏览器控制台与开发者工具在自动化中的高阶应用

做网页自动化时,F12开发者工具几乎是必开的——查XPath、看网络请求、调试JavaScript。但大部分人只用到了它的1%功能。

其实,开发者工具本身也可以被自动化利用——通过它来执行调试脚本、拦截请求、分析页面性能、甚至绕过某些前端限制。今天讲浏览器控制台和开发者工具在RPA流程中的高阶用法,以及如何把它的能力集成到影刀流程中。

picture.image


先说核心:开发者工具能为RPA提供什么

picture.image

能力说明RPA中的价值
Console执行JS在控制台直接运行JavaScript实时调试、动态修改页面

picture.image | Network抓包 | 捕获页面所有网络请求 | 提取API接口、获取隐藏数据 | | Elements实时编辑 | 修改DOM结构和样式 | 绕过前端限制、临时修复页面 | | Performance分析 | 页面加载性能分析 | 优化等待策略 | | Application存储 | 查看Cookie/LocalStorage | 调试登录态 |

picture.image

第一部分:Console控制台的高阶操作

1.1 在控制台中执行多行脚本

picture.image

// 在浏览器控制台中执行(F12 → Console)

// 1. 获取页面上所有商品链接

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/f0e515b03d8c482381ec0465527f1ffe~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=zhIxXSfa3aRBIVd%2Bz1g%2BAYw5rUU%3D)
let links = document.querySelectorAll('.product-item a');
let urls = Array.from(links).map(a => a.href);
console.log(urls);

// 2. 模拟滚动到底部(触发懒加载)

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/3dd1f5603f504f5b93224bcefe0fc491~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=tvbQc1f%2B127fOxPz4Nfbt4961kY%3D)
window.scrollTo(0, document.body.scrollHeight);

// 3. 批量点击所有“加载更多”按钮
document.querySelectorAll('.load-more-btn').forEach(btn => btn.click());


![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/7d0de1a810bf4cc88c82cdfe2310b260~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=oiWU%2B%2Fbytd3zILKMPFCA%2FJpZx60%3D)
// 4. 提取表格数据
let tableData = [];
document.querySelectorAll('#data-table tbody tr').forEach(row => {
    let cells = row.querySelectorAll('td');
    tableData.push(Array.from(cells).map(c => c.innerText));
});
console.table(tableData);

1.2 在影刀中通过ExecuteScript执行控制台命令

# ============================================
# 影刀中执行JavaScript(同控制台效果)
# ============================================

# 获取页面上所有商品价格
执行JavaScript("""
    let prices = document.querySelectorAll('.price');
    let priceArray = Array.from(prices).map(el => el.innerText);
    return priceArray;
""", 存入变量=价格列表)

输出日志("采集到 " + 获取列表长度(价格列表) + " 个价格")

# 模拟控制台中的批量操作
执行JavaScript("""
    // 模拟人类滚动
    
![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/09783ae118e542f987117f7c785c0cb1~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=LSf6zeBIs8RlpQp%2B6YgSognQDQk%3D)
    window.scrollTo(0, document.body.scrollHeight / 2);
    setTimeout(() => {
        window.scrollTo(0, document.body.scrollHeight);
    }, 500);
    return 'scroll completed';
""")

1.3 使用控制台调试XPath(比XPath Helper更灵活)

// 在控制台中测试XPath
function getElementByXPath(xpath) {
    return document.evaluate(
        xpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
    ).singleNodeValue;
}

// 测试XPath是否能定位到元素
let el = getElementByXPath('//div[@class="product"]//span[@class="price"]');
console.log(el ? '✅ 找到元素' : '❌ 未找到');

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/74c5a85145e14081bba54739a4c6365b~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=FG7PKTaWgmO1PIZe%2BRzw3oNneAs%3D)
if (el) console.log(el.innerText);

// 测试contains匹配
let el2 = getElementByXPath('//*[contains(text(),"下一页")]');
console.log(el2 ? '✅ 找到下一页按钮' : '❌ 未找到');

1.4 批量测试多个XPath

// 测试一组XPath,返回第一个有效的
const xpaths = [
    '//*[@id="price"]',
    '//span[contains(@class,"price")]',
    '//*[contains(text(),"¥")]'
];

for (let xp of xpaths) {
    let el = getElementByXPath(xp);
    if (el) {
        console.log('✅ 有效XPath:', xp);
        console.log('   内容:', el.innerText);
        break;
    } else {
        console.log('❌ 无效XPath:', xp);
    }
}

第二部分:Network抓包——获取API接口数据

很多网站的数据是通过API接口加载的,直接在Network面板里可以找到这些接口,比从页面上抓取更高效。

2.1 手动抓包流程

  1. 打开开发者工具(F12)→ Network标签
  2. 刷新页面或执行操作
  3. 在请求列表中找到数据接口(通常是XHR/Fetch类型)
  4. 查看请求URL、Headers、Response

2.2 在影刀中直接调用API接口

# ============================================
# 从Network抓包获取的API直接调用
# ============================================

# 示例:淘宝商品详情API(从Network中捕获)
# URL: https://detail.tmall.com/item.htm?id=123456
# 实际数据接口: https://mdskip.taobao.com/api/... 

# 在影刀中直接请求API,绕过页面渲染
HTTP请求(
    方法="GET",
    URL="https://mdskip.taobao.com/api/xxx?itemId=123456",
    请求头={
        "User-Agent": "Mozilla/5.0...",
        "Referer": "https://detail.tmall.com/"
    }
)

# 解析JSON响应(比从HTML提取快10倍)
响应JSON = JSON解析(HTTP响应内容)
商品价格 = 响应JSON["data"]["price"]
商品标题 = 响应JSON["data"]["title"]

2.3 用JavaScript拦截并获取页面网络请求

# ============================================
# 通过JavaScript拦截网络请求
# ============================================

执行JavaScript("""
    // 拦截所有fetch请求
    const originalFetch = window.fetch;
    window.fetch = function(...args) {
        console.log('Fetch请求:', args[0]);
        // 如果请求的是数据接口,记录响应
        if (args[0].includes('/api/')) {
            return originalFetch.apply(this, args).then(response => {
                response.clone().json().then(data => {
                    window.__captured_api_data = data;
                });
                return response;
            });
        }
        return originalFetch.apply(this, args);
    };
    
    // 拦截所有XHR请求
    const originalXHROpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        this._url = url;
        return originalXHROpen.apply(this, arguments);
    };
    XMLHttpRequest.prototype.send = function(body) {
        this.addEventListener('load', function() {
            if (this._url && this._url.includes('/api/')) {
                try {
                    window.__captured_xhr_data = JSON.parse(this.responseText);
                } catch(e) {}
            }
        });
        return originalXHRSend.apply(this, arguments);
    };
    
    return '拦截已启动';
""")

第三部分:Elements实时编辑——临时修复页面

3.1 在控制台中修改页面元素

有时候页面元素因为某些原因被隐藏或禁用,无法通过正常方式操作。可以在控制台中临时修改DOM。

# ============================================
# 通过JavaScript修改页面元素
# ============================================

# 场景1:移除禁用属性,让按钮可点击
执行JavaScript("""
    document.querySelector('#submit-btn').disabled = false;
    document.querySelector('#submit-btn').removeAttribute('disabled');
    return '按钮已启用';
""")

# 场景2:显示隐藏元素
执行JavaScript("""
    document.querySelector('.hidden-modal').style.display = 'block';
    document.querySelector('.hidden-modal').style.visibility = 'visible';
    return '元素已显示';
""")

# 场景3:修改元素文本(用于绕过文本验证)
执行JavaScript("""
    document.querySelector('.error-message').innerText = '';
    document.querySelector('.warning').style.display = 'none';
    return '页面已修复';
""")

3.2 注入自定义CSS

# ============================================
# 注入CSS样式,让隐藏元素可见
# ============================================

执行JavaScript("""
    const style = document.createElement('style');
    style.textContent = `
        /* 让所有隐藏元素可见(调试用) */
        [style*="display:none"], 
        [style*="display: none"],
        [hidden] {
            display: block !important;
            border: 3px solid red !important;
        }
        
        /* 高亮所有可点击元素 */
        button, a, [role="button"] {
            outline: 2px solid blue !important;
        }
    `;
    document.head.appendChild(style);
    return 'CSS已注入';
""")

3.3 通过控制台直接调用页面函数

很多网站的前端代码包含全局函数,可以在控制台中直接调用。

// 在控制台中尝试调用页面函数
// 例如:电商网站的加入购物车函数
if (typeof window.addToCart === 'function') {
    window.addToCart('product_123', 2);
    console.log('已调用加入购物车函数');
}

// 查找页面中的可用全局函数
let globalFuncs = Object.keys(window).filter(key => typeof window[key] === 'function');
console.log('可用函数:', globalFuncs);

第四部分:Performance分析——优化等待策略

4.1 分析页面加载关键指标

# ============================================
# 获取页面性能数据
# ============================================

执行JavaScript("""
    const perfData = window.performance.timing;
    const loadTime = perfData.loadEventEnd - perfData.navigationStart;
    const domReady = perfData.domContentLoadedEventEnd - perfData.navigationStart;
    const firstPaint = perfData.responseStart - perfData.navigationStart;
    
    return {
        totalLoad: loadTime,
        domReady: domReady,
        firstPaint: firstPaint,
        isLoaded: loadTime > 0
    };
""", 存入变量=性能数据)

输出日志("页面加载总耗时:" + 性能数据["totalLoad"] + "ms")
输出日志("DOM就绪耗时:" + 性能数据["domReady"] + "ms")

# 根据性能数据动态调整等待时间
如果 性能数据["totalLoad"] > 5000:
    输出日志("页面加载较慢,增加等待时间")
    设置变量(动态超时 = 15)
否则:
    设置变量(动态超时 = 5)
结束If

4.2 检测页面是否有未完成的请求

# ============================================
# 检测页面是否还有未完成的网络请求
# ============================================

执行JavaScript("""
    // 检查是否还有未完成的fetch/XHR请求
    if (window.__pendingRequests) {
        return {pending: window.__pendingRequests > 0};
    }
    
    // 通过performance API检测
    const entries = performance.getEntriesByType('resource');
    const incomplete = entries.filter(e => 
        e.responseEnd === 0 || e.duration === 0
    );
    
    return {
        pending: incomplete.length > 0,
        count: incomplete.length
    };
""", 存入变量=请求状态)

如果 请求状态["pending"] == True:
    输出日志("还有 " + 请求状态["count"] + " 个请求未完成")
    等待(2)
否则:
    输出日志("所有请求已完成")
结束If

第五部分:Application存储——调试登录态

5.1 查看和管理Cookie

# ============================================
# 通过JavaScript获取所有Cookie
# ============================================

执行JavaScript("""
    // 获取所有Cookie
    const cookies = document.cookie.split(';').map(c => c.trim());
    const cookieObj = {};
    cookies.forEach(c => {
        const [key, value] = c.split('=');
        if (key) cookieObj[key] = decodeURIComponent(value || '');
    });
    return cookieObj;
""", 存入变量=Cookie对象)

输出日志("当前Cookie数量:" + 获取字典键数量(Cookie对象))

# 检查关键登录Cookie是否存在
如果 字典包含键(Cookie对象, "session_id") == True:
    输出日志("会话Cookie存在,登录态有效")
否则:
    输出日志("会话Cookie不存在,可能未登录")
结束If

5.2 查看LocalStorage数据

# ============================================
# 获取LocalStorage中的调试数据
# ============================================

执行JavaScript("""
    // 获取所有LocalStorage数据
    const keys = Object.keys(localStorage);
    const data = {};
    keys.forEach(key => {
        try {
            data[key] = JSON.parse(localStorage.getItem(key));
        } catch(e) {
            data[key] = localStorage.getItem(key);
        }
    });
    return data;
""", 存入变量=本地存储数据)

# 查找特定配置项
如果 字典包含键(本地存储数据, "user_config") == True:
    配置 = 本地存储数据["user_config"]
    输出日志("用户配置:" + JSON序列化(配置))
结束If

第六部分:实战——自动捕获API并替代页面采集

# ============================================
# 完整实战:通过Network抓包获取商品数据
# ============================================

# 场景:采集商品价格,但不想从DOM中解析

# ----- 1. 打开商品页面 -----
打开网页("https://detail.xxx.com/item/123456", 打开方式="新建标签页")
等待(3秒)

# ----- 2. 在页面中注入请求拦截脚本 -----
执行JavaScript("""
    // 拦截商品详情API
    window.__productData = null;
    
    const originalFetch = window.fetch;
    window.fetch = function(...args) {
        const url = args[0];
        if (typeof url === 'string' && url.includes('/api/product/detail')) {
            return originalFetch.apply(this, args).then(response => {
                response.clone().json().then(data => {
                    window.__productData = data;
                    console.log('✅ 已捕获商品数据:', data);
                });
                return response;
            });
        }
        return originalFetch.apply(this, args);
    };
    
    // 触发页面加载(如果页面还没加载完)
    // 如果已经加载完,手动刷新触发请求
    if (document.readyState === 'complete') {
        location.reload();
    }
    
    return '拦截已启动,等待数据...';
""")

# ----- 3. 等待数据被捕获 -----
设置变量(等待次数 = 0)
条件循环(等待次数 < 30):
    执行JavaScript("return window.__productData !== null;", 存入变量=数据已捕获)
    
    如果 数据已捕获 == True:
        输出日志("数据已捕获")
        跳出循环
    否则:
        等待(1)
        等待次数 = 等待次数 + 1
    结束If
结束条件循环

# ----- 4. 读取捕获的数据 -----
执行JavaScript("return window.__productData;", 存入变量=商品数据)

# ----- 5. 提取关键字段 -----
解析JSON(商品数据, 存入变量=数据对象)
价格 = 数据对象["data"]["price"]
标题 = 数据对象["data"]["title"]
库存 = 数据对象["data"]["stock"]

输出日志("商品:标题=" + 标题 + ",价格=" + 价格 + ",库存=" + 库存)

第七部分:开发者工具常用快捷键

快捷键功能RPA中的用途
F12 / Ctrl+Shift+I打开/关闭开发者工具调试时快速打开
Ctrl+Shift+C元素选择模式快速定位元素XPath
Ctrl+F在元素/网络/源码中搜索搜索特定XPath或接口
Ctrl+Shift+F在所有源码中搜索查找页面中的JS函数
Ctrl+Shift+J打开Console面板执行测试脚本
Ctrl+Shift+E打开Network面板查看网络请求

常见问题速查

问题原因解决方法
控制台命令在影刀中不执行页面未加载完成先等待页面加载
拦截不到网络请求拦截代码执行太晚在页面加载前注入
JSON解析失败响应不是JSON格式检查响应类型
跨域请求被拦截CORS限制使用浏览器环境,不单独调用
控制台命令返回值太大数据量超限在JavaScript中处理后再返回

推荐资源

  • Chrome开发者工具官方文档
  • 浏览器Console API文档
  • 本系列第28篇《API与HTTP请求的鉴权与分页处理》
  • 本系列第59篇《API数据采集与鉴权实战(淘宝/拼多多/飞书)》

#影刀RPA #RPA自动化 #开发者工具 #ChromeDevTools #JavaScript #Network

作者:林焱

本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。

0
0
0
0
评论
未登录
暂无评论