标签 网络爬虫 下的文章


垂死病中惊坐起,爬虫偷走了你的信息 ww!

可能就是很单纯的… 把邮箱… 放在简介,然鹅就算 202X 年了还有人靠电子邮箱发广告和垃圾邮件,虽然眼不见心不烦,哎爬虫怎么这么坏

就是这些坏东西的方法很多… 真的很多,古早时候就有把 @写成 #的邪法,就以上面那个 Demo 为例,它本身就自带 cloudflare 有形的大手,首先,cf 会编码 "可能是像电子邮箱的东西",任何能够执行 js 的浏览器会自动解码它,更别提人机验证了,直接把目录填进安全规则托管质询

不过能 "折腾就折腾",有没有什么更神奇的方法,不依赖现有的基础设施,而且很方便?有的有的,这是工作量验证

原理

这种工作验证基于 AES,ChaCha32 等对称加密算法和一串摘要值,密码被故意设计成弱口令,这使得用户可以靠猜测比对推出密码

在个别 pow 验证码中也提供了这类小组件,为了效率,通常使用 WASM,JS 好写

因为是反爬虫所以也不需要很高强度的防护,大战 GPU 这种(X)

简单实现

找哈基米写的,就很舒服 (√)

弱口令的产生方法是公开的,为了防止预先推出就加上了一串公开的 uuid 合并为一个 key,加上了一些改进

用来反爬也没什么问题


↑ Demo


📌 转载信息
原作者:
arch_linux
转载时间:
2026/1/24 06:44:59


前言

大家好,我是 aki66938。

之前我维护过一个基于 Python + Selenium/Playwright 的小红书自动化工具
小红书工具开源说明1

之前应该也有佬友看过我的帖子。但在维护过程中,我个人的感受是单纯依赖浏览器自动化的局限性:资源占用高、并发能力弱、容易被反爬策略针对。部署起来也存在各种便利性的问题。

随着 AIGC 的兴起,我发现旧项目已经存在很容易被 AI 替代的可能,所以前一个项目算是废止了。
后来在公司开发项目的过程中,收获了一些从浏览器逆向 api 接口请求逻辑实现本地后端的技能,灵感这就来了,本着学习和实践的目的,于是才有了当前这个项目,使用 Rust 重构核心网络层,构造全新的 api 路由去请求小红书官方的 api endpoint,结合 Antigravity 实现纯 AI vibe coding,完成小红书主页绝大部分接口的开发工作,目前包含对前端内容包括用户信息、搜索接口、文章接口、图文详情以及视频和图片的捕获、采集,觉得基本满足大部分采集需求后,赘述本帖以誓开源,项目名称 XHS_RS_TOOLS

项目地址

小红书工具开源说明2
小红书工具开源说明3小红书工具开源说明4

架构设计

这是一个 Rust 主导 + Python 辅助 的混合架构:

  • Server (Rust): 处理所有高并发 HTTP 请求、API 路由、数据清洗、媒体下载。
  • Signer (Python): 借助了开源项目小红书工具开源说明5 实现 agent 代理完成本地算法签名(非常给力的项目,佬友们多去点点赞支持下),伴随着 rust 一起启动,同时,还存在一个 playwright 进程,用于获取访客用户的 cookie,以实现对 qrcode 的请求,最终实现用户扫码登录

快速开始

如果你有 Rust 环境:

git clone https://github.com/aki66938/XHS_RS_TOOLS.git

cd XHS_RS_TOOLS

启动服务

docker compose up -d  #用于启动mongodb数据库,存储登录用户的cookie数据
cargo run

服务启动后,API 文档地址是

http://localhost:3000/swagger-ui

项目根目录的 client_demo.py 为全接口的测试用例,运行需要进入 script 目录进行 pip install -r requestment.txt 安装依赖环境

特点

除了官方接口获取了文章详细内容 /api/note/detail,额外实现了对该接口 response 内容的简单解析,获取视频、图片文件的链接及下载接口,包含无水印图片文件

需要特别注意的是,如果需要采集大量内容,注意分页策略,github 的自述文件中描述了分页策略的规则指南

feature

项目还会持续更新,目前针对主站的接口开发工作告一段落,后续将重点针对创作者中心尝试接口开发。
另外 python 安装 playwright 的过程也是一番折腾,由于仅用到了获取访客的 cookie 这一个动作。后续考虑将 playwright 使用容器部署的方式。

结语

项目完全开源 (MIT),目前已经迭代到 v1.7.0 版本,功能基本稳定。欢迎佬友关注评论交流,技术细节可全公开。

GitHub: GitHub - aki66938/XHS_RS_TOOLS: xhs_toolkit for rust


以技术探索为名,行守法合规之事。


📌 转载信息
原作者:
krik
转载时间:
2026/1/21 22:38:45

本工具仅限学术交流使用,严格遵循相关法律法规,符合平台内容的合法及合规性,禁止用于任何商业用途!

1. 项目背景与核心功能整合

开发初衷

小红书作为国内头部的社区种草平台,其海量笔记数据蕴含着极高的商业与学术价值。此前,为了满足不同场景的采集需求,我曾分别开发了针对评论、博主主页以及UID转换的三款独立工具。然而,许多用户反馈在处理复杂任务(如同时采集评论和主页笔记)时,频繁切换软件带来了操作上的不便。

为了解决这一痛点,我将上述三个核心模块进行了深度融合,推出了全新的 “爬小红书聚合软件v1.0”。这是一款集成了“评论采集”、“达人笔记采集”及“UID转换”的一体化数据解决方案。

适用场景

本工具严格遵循相关法律法规,仅限于学术交流与合规性研究,具体适用场景包括:

  • 获客截流: 从行业热门作品评论区精准挖掘目标用户画像。
  • 舆情分析: 用于社会舆情挖掘、网络传播规律等学术研究。
  • 内容优化: 辅助内容创作者分析优质博主风格与热门话题。
  • 运营辅助: 解决跨平台协作中链接与ID转换的痛点。

2. 技术架构与实现逻辑

本软件完全由 Python 语言独立开发,采用模块化设计以保证高效运行与维护。

核心模块分工

序号模块名称功能描述
1tkinter构建GUI图形用户界面
2requests负责发送HTTP请求
3json解析服务器返回的响应数据
4pandas处理并保存为CSV数据结果
5logging记录运行日志,便于异常回溯

核心代码实现

以下是软件中处理数据请求与保存的关键代码片段:

发送请求与解析:

# 发送请求
r = requests.get(url, headers=h1, params=params)
# 解析数据
json_data = r.json()

数据解析示例(评论内容):

for c in json_data['data']['comments']: 
    # 评论内容 
    content = c['content'] 
    self.tk_show('评论内容:' + str(content)) 
    content_list.append(content)

数据保存至CSV:

# 保存数据到DF
df = pd.DataFrame( {  
    '笔记链接': 'https://www.xiaohongshu.com/explore/' + note_id,  
    '笔记链接_长': note_url2,  
    '页码': page,  
    '评论者昵称': nickname_list,  
    '评论者id': user_id_list,  
    '评论者主页链接': user_link_list,  
    '评论时间': create_time_list,  
    '评论IP属地': ip_list,  
    '评论点赞数': like_count_list,  
    '评论级别': comment_level_list,  
    '评论内容': content_list, })
# 设置csv文件表头
if os.path.exists(self.result_file3): 
    header = False
else: 
    header = True
# 保存到csv
df.to_csv(self.result_file3, mode='a+', header=header, index=False, encoding='utf_8_sig')
self.tk_show('文件保存成功:' + self.result_file3)

采用logging模块记录日志运行过程,方便debug回溯场景:

def get_logger(self):    
    self.logger = logging.getLogger(__name__)    
    # 日志格式
    formatter = '[%(asctime)s-%(filename)s][%(funcName)s-%(lineno)d]--%(message)s'    
    # 日志级别
    self.logger.setLevel(logging.DEBUG)    
    # 控制台日志
    sh = logging.StreamHandler()    
    log_formatter = logging.Formatter(formatter, datefmt='%Y-%m-%d %H:%M:%S')    
    # info日志文件名
    info_file_name = time.strftime("%Y-%m-%d") + '.log'    
    # 将其保存到特定目录
    case_dir = r'./logs/'    
    info_handler = TimedRotatingFileHandler(filename=case_dir + info_file_name,                                        
                                          when='MIDNIGHT',                                        
                                          interval=1,                                        
                                          backupCount=7,                                        
                                          encoding='utf-8')

3. 功能详解与数据产出

本软件通过接口协议进行数据交互,相比模拟浏览器(RPA)具有更高的稳定性。采集过程中,系统会实时(每页请求间隔1~2s)将数据存入CSV文件,有效防止因网络异常导致的数据丢失。

功能一:搜索笔记与评论采集

该模块支持根据关键词或笔记链接采集评论区数据。在这里插入图片描述

  • 笔记数据字段(19个): 包含关键词、笔记ID、标题、正文、点赞/收藏/评论数、发布时间及IP属地等。
  • 评论数据字段(11个): 包含评论者昵称/ID、评论内容、点赞数、IP属地及评论级别等。
  • 多媒体支持: 自动下载搜索到的笔记封面图片。

功能二:博主主页笔记采集

支持根据博主主页链接批量抓取其发布的历史笔记。在这里插入图片描述

  • 采集字段(18个): 包含作者信息、笔记ID、链接、类型、互动数据及正文内容等。
  • 结果展示: 生成结构化的CSV文件及对应的图片素材包。

功能三:UID与链接转换工具

提供高频使用的转换功能,无需打开网页即可完成:在这里插入图片描述

  1. 主页链接 $\leftrightarrow$ 小红书号(xhs号)互转。
  2. App端作品链接 $\rightarrow$ PC端作品链接转换。

4. 使用指南

前置准备

  • 在开始采集前,用户需获取并填写自己的Cookie值。
  • 打开浏览器开发者工具(F12),复制Cookie值。
  • 将其粘贴至软件同级目录下的 cookie.txt 文件中。

操作流程

  • 登录界面: 启动软件并完成登录验证。
  • 选择模块: 根据需求选择“搜索采集”、“主页采集”或“转换工具”。
  • 配置参数: 填写关键词、时间范围或博主链接等信息。
  • 执行任务: 点击「开始执行」,实时监控进度条。
  • 查看结果: 任务完成后,在软件所在文件夹查看生成的CSV文件及图片文件夹。

5.演示视频

为了方便用户上手,附带了完整的操作演示视频:

mp.weixin.qq.com/s/t9cKGsgJoI9rca3I1w5RdA

END. 版权声明

本软件及文章均为本人独立原创开发与编写。请尊重原创成果,严禁任何形式的二创、转载或盗发,违者必究!

鼠鼠因逆向教务系统,被校方误以为网络攻击 (马上要报警),差点被记过继续讨论:

佬友的这个帖子让我想起以前本科的一些类似的有趣事情。

第一件事是我参加比赛认识的一个师弟干的。
他在参加某比赛的时候在宿舍用校园网去爬了某政府网站上的统计数据,刚开始爬的当天下午学校网管和警察就直接敲开他的宿舍门了。
该政府网站的工作人员认为收到了网络攻击,于是找了网警,网警定位到了 IP 是我们学校,打电话找学校网管,因为没有做任何遮掩,所以学校网管直接定位到宿舍然后上门了。
后面是因为理由正当,没有造成严重后果,口头警告一下就过了。
这件事对当时我的我造成了很大冲击,因为我当时正在参加学院和某企业合作的一个项目,通过爬各种新闻网站、论坛的评论区来做舆情分析。虽然这个项目有一定政府背景,但当时我写爬虫测试的时候都是用的自己的电脑,唯一想到要用代理池的原因是爬太多被封 IP 不能继续测试…
我一个不太靠谱的师兄在拉我们进这个爬虫项目的时候信誓旦旦地说,学会了爬虫就不怕吃不饱饭了,因为他有一个同学,毕业之后靠写爬虫爬数据去卖,一天能赚 100 多。我一开始还傻傻觉得挺有道理。后面想起来,觉得这只是当时的法规不成熟,放现在分分钟出事。
结合佬友的故事,各位同仁学、用爬虫的时候需谨慎

第二个事情是想吐槽一下高校纸糊一样的网络系统。
我们本科的时候有一门忘记干嘛的公选课,第一天上课老师就让我们要经常登录某个学校部门自己搭的网站,学期末的时候会根据登录次数来算平时分。
我们研究之后发现那个网站的登录系统完全是纸糊的,除了账号密码之外不做任何校验,甚至连登录都是一次性的,就是它不记录你的登录状态,你关闭网站再打开就是注销状态了。
为了刷登录次数,我有同学用脚本精灵做了个脚本去点。我则用 python 写了个代码去刷,经历了第一件事之后,我还专门把登录间隔设大一点,爬刷出问题。后来发现完全没有关系,系统完全不在乎这些事情。
想起来挺好笑的。


📌 转载信息
原作者:
linyopt
转载时间:
2026/1/14 11:05:06

深度! 评论分析!用户画像!找商机!
都在说这些,但是感觉都没有人开源,那么我开源一个:

为什么找市场机会小红书?

商机在具体的问题里

小红书这里汇聚着包罗万象的生活问题和经验分享,“遇事不决小红书” 成为年轻人常用的决策路径,他们相信能在这里找到答案。

对商家而言,要想深入了解今年的消费者在苦恼些什么、真正需要些什么,小红书是必经之路。

消费者不是没有需求,而是需求太具体。

背景知识

几个月前,我在 linuxdo 开源了一个调研小生意的工具:

得到一些反响,旧的版本适合入门学习,因为过去的技术就是那样基础。而现在又新技术了,那么我升级一个大的版本,强大和有用很多。这是一个专为商业创意验证设计的高级多智能体系统。该系统利用人工智能(AI)和网络爬虫技术,通过从社交媒体平台(尤其是小红书)收集和分析数据,来解析市场需求、用户痛点及竞争格局。

新的版本

在整理上传中,很快完成…
github 地址:

核心功能

  • 智能关键词生成: 根据业务创意自动生成搜索关键词
  • 小红书数据抓取: 自动抓取相关笔记和评论数据
  • AI 内容分析: 使用 LLM 分析用户痛点和市场需求
  • 自动化报告生成: 生成专业的市场验证报告

案例展示

** 1 业务创意:** 在重庆卖陈皮



词频分析:

用户画像:

快速开始

# 安装依赖 cd agent_system
pip install -r requirements.txt

# 配置 API 密钥 (编辑 agent_system/.env 文件) # OPENAI_API_KEY=your_key # TIKHUB_TOKEN=your_token # 运行验证
python run_agent.py 在深圳卖陈皮

总结

开源地址: 整理上传中…
github 地址:


📌 转载信息
转载时间:
2026/1/5 15:29:52

// ==UserScript==
// @name         Grok/X.ai 自动化注册机 (集成自动清理与循环版)
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  全自动流程:注册 -> 提取Token -> 清理Cookie -> 循环重启
// @author       Bytebender
// @match        *://*/*
// @match        https://x.ai/*
// @match        https://www.x.ai/*
// @match        https://accounts.x.ai/*
// @match        https://grok.com/*
// @match        https://www.grok.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_cookie
// @connect      mail.chatgpt.org.uk
// @connect      100.64.0.101
// @connect      api.x.ai
// @connect      x.ai
// @connect      grok.com
// @connect      www.grok.com
// @connect      accounts.x.ai
// ==/UserScript==

(function() {
    'use strict';

    // ========================================================
    // 1. 随机数据生成工具
    // ========================================================

    // 生成随机姓名 (首字母大写)
    function getRandomName() {
        const chars = 'abcdefghijklmnopqrstuvwxyz';
        const len = Math.floor(Math.random() * 5) + 4; // 长度 4-8
        let result = '';
        for (let i = 0; i < len; i++) {
            result += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return result.charAt(0).toUpperCase() + result.slice(1);
    }

    // 生成强密码 (12位,包含大小写+数字+特殊符号)
    function getRandomPassword() {
        const lower = "abcdefghijklmnopqrstuvwxyz";
        const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        const nums = "0123456789";
        const symbols = "!@#$%^&*";

        // 1. 确保每种字符至少有一个
        let pass = "";
        pass += lower[Math.floor(Math.random() * lower.length)];
        pass += upper[Math.floor(Math.random() * upper.length)];
        pass += nums[Math.floor(Math.random() * nums.length)];
        pass += symbols[Math.floor(Math.random() * symbols.length)];

        // 2. 补足剩余长度
        const allChars = lower + upper + nums + symbols;
        for (let i = 0; i < 8; i++) {
            pass += allChars[Math.floor(Math.random() * allChars.length)];
        }

        // 3. 打乱顺序 (洗牌)
        return pass.split('').sort(() => 0.5 - Math.random()).join('');
    }

    // ========================================================
    // 2. 任务编排配置
    // ========================================================
    const actions = [
        // --- 阶段一:Grok 首页跳转 ---
        {
            "step_name": "1. 点击 Grok 首页入口",
            "type": "click",
            "selector": "html > body > div:nth-of-type(2) > div > div > div > main > div > div > div:nth-of-type(3) > div:nth-of-type(2) > a:nth-of-type(2)",
            "url_keyword": "grok.com"
        },
        // --- 阶段二:进入注册页 (X.ai) ---
        {
            "step_name": "2. 点击 X.ai 注册/登录按钮",
            "type": "click",
            "selector": "html > body > div:nth-of-type(2) > div > div > div:nth-of-type(2) > div > div:nth-of-type(2) > button",
            "url_keyword": "accounts.x.ai"
        },
        // --- 阶段三:自动化邮箱 ---
        {
            "step_name": "3. 自动申请并填写临时邮箱",
            "type": "get_email",
            "selector": "html > body > div:nth-of-type(2) > div > div > div:nth-of-type(2) > div > form > div > div > input",
        },
        {
            "step_name": "4. 点击下一步 (提交邮箱)",
            "type": "click",
            "selector": "html > body > div:nth-of-type(2) > div > div > div:nth-of-type(2) > div > form > div:nth-of-type(2) > button"
        },
        // --- 阶段四:验证码 ---
        {
            "step_name": "5. 等待邮件验证码并自动填写",
            "type": "fill_code",
            "selector": "input[name='code']"
        },
        // --- 阶段五:填写个人信息 (全随机) ---
        {
            "step_name": "6. 填写名 (First Name)",
            "type": "input",
            "selector": "input[name='givenName']",
            "value": "__RANDOM__"
        },
        {
            "step_name": "7. 填写姓 (Last Name)",
            "type": "input",
            "selector": "input[name='familyName']",
            "value": "__RANDOM__"
        },
        {
            "step_name": "8. 填写密码 (强密码)",
            "type": "input",
            "selector": "input[name='password']",
            "value": "__RANDOM_PASS__"
        },
        // --- 阶段六:提交 ---
        {
            "step_name": "9. 点击最终提交",
            "type": "click",
            "selector": "button[type='submit']"
        },
        // --- 阶段七:提取 Token ---
        {
            "step_name": "10. 检查跳转并上传 Token",
            "type": "wait_url_and_upload",
            "target_url": "grok.com"
        },
        // --- 阶段八:清理环境并循环 ---
        {
            "step_name": "11. 清理 Cookie 并重启循环",
            "type": "clean_and_restart",
            "clean_targets": [
                "https://x.ai/",
                "https://www.x.ai/",
                "https://accounts.x.ai/",
                "https://grok.com/",
                "https://www.grok.com/"
            ]
        }
    ];

    // ========================================================
    // 3. 核心功能类 (邮箱/网络/Cookie)
    // ========================================================

    // 3.1 网络请求封装
    function gmFetch(url, options) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                url: url,
                method: options.method || 'GET',
                headers: options.headers || {},
                data: options.body || null,
                onload: (response) => {
                    if (response.status >= 200 && response.status < 300) {
                        try { resolve(JSON.parse(response.responseText)); }
                        catch (e) { resolve(response.responseText); }
                    } else { reject(new Error(`HTTP Error: ${response.status}`)); }
                },
                onerror: () => reject(new Error('Network Error')),
                ontimeout: () => reject(new Error('Timeout'))
            });
        });
    }

    // 3.2 临时邮箱客户端
    class TempMailClient {
        constructor() {
            this.baseUrl = "https://mail.chatgpt.org.uk/api";
            this.headers = {
                "User-Agent": "Mozilla/5.0",
                "Origin": "https://mail.chatgpt.org.uk",
                "Referer": "https://mail.chatgpt.org.uk/"
            };
        }
        async getEmail() {
            const result = await gmFetch(`${this.baseUrl}/generate-email`, {
                method: "GET",
                headers: { ...this.headers, "content-type": "application/json" }
            });
            if (result && result.success && result.data?.email) return result.data.email;
            throw new Error("邮箱API返回异常");
        }
        async fetchMessages(email) {
            const url = `${this.baseUrl}/emails?email=${encodeURIComponent(email)}`;
            const result = await gmFetch(url, {
                method: "GET",
                headers: { ...this.headers, "cache-control": "no-cache" }
            });
            return (result.success && result.data?.emails) ? result.data.emails : [];
        }
        async waitForCode(email, timeoutSec = 120) {
            console.log(`[Mail] 开始监听 ${email} ...`);
            const startTime = Date.now();
            const codeRegex = /\b[A-Z0-9]{3}-[A-Z0-9]{3}\b|\b\d{6}\b/;
            return new Promise((resolve, reject) => {
                const timer = setInterval(async () => {
                    if (Date.now() - startTime > timeoutSec * 1000) {
                        clearInterval(timer);
                        reject(new Error("等待验证码超时"));
                    }
                    try {
                        const msgs = await this.fetchMessages(email);
                        if (msgs.length > 0) {
                            for (const msg of msgs) {
                                const content = (msg.subject || "") + " " + (msg.html_content || "");
                                const match = content.match(codeRegex);
                                if (match) {
                                    clearInterval(timer);
                                    resolve(match[0]);
                                    return;
                                }
                            }
                        }
                    } catch(e) { console.warn("Polling error:", e); }
                }, 3000);
            });
        }
    }

    // 3.3 Token 上传逻辑
    async function extractAndUploadToken() {
        return new Promise((resolve, reject) => {
            GM_cookie.list({ name: "sso" }, (cookies, error) => {
                if (error || !cookies || cookies.length === 0) {
                    return reject(new Error("SSO Cookie missing"));
                }
                const ssoToken = cookies[0].value;
                console.log("获取到 Token:", ssoToken.substring(0, 10) + "...");

                GM_xmlhttpRequest({
                    url: "http://xxx/api/tokens/add",
                    method: "POST",
                    headers: {
                        "content-type": "application/json",
                        "authorization": "Bearer xxxxx"
                    },
                    data: JSON.stringify({ tokens: [ssoToken], token_type: "sso" }),
                    onload: (response) => {
                        if (response.status >= 200 && response.status < 300) {
                            console.log("Token 上传成功!");
                            resolve();
                        } else {
                            reject(new Error("Upload failed: " + response.responseText));
                        }
                    },
                    onerror: (err) => reject(err)
                });
            });
        });
    }

    // 3.4 Cookie 清理逻辑
    function executeCleanCookies(targetUrls) {
        return new Promise((resolve) => {
            if (!targetUrls || targetUrls.length === 0) return resolve();
            let completed = 0;
            targetUrls.forEach(url => {
                GM_cookie.list({ url: url }, function(cookies, error) {
                    if (cookies && cookies.length > 0) {
                        cookies.forEach(c => {
                            GM_cookie.delete({ name: c.name, url: url }, () => {});
                        });
                    }
                    completed++;
                    if (completed === targetUrls.length) {
                        setTimeout(resolve, 800); // 缓冲
                    }
                });
            });
        });
    }

    // 3.5 Native 输入模拟 (绕过 React/Vue 绑定)
    function setNativeValue(element, value) {
        const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
        const prototype = Object.getPrototypeOf(element);
        const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
        if (valueSetter && valueSetter !== prototypeValueSetter) {
            prototypeValueSetter.call(element, value);
        } else {
            valueSetter.call(element, value);
        }
        element.dispatchEvent(new Event('input', { bubbles: true }));
    }

    // 3.6 元素等待
    const waitForElement = (selector, timeout = 10000) => {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) return resolve(el);
            const observer = new MutationObserver(() => {
                const el = document.querySelector(selector);
                if (el) { observer.disconnect(); resolve(el); }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            setTimeout(() => { observer.disconnect(); reject(new Error('元素超时: ' + selector)); }, timeout);
        });
    };

    // ========================================================
    // 4. 自动化执行引擎
    // ========================================================
    const mailClient = new TempMailClient();
    let isRunning = GM_getValue('script_is_running', false);
    let currentIndex = GM_getValue('script_step_index', 0);

    console.log(`🚀 [注册机状态] Running: ${isRunning} | Step: ${currentIndex}`);

    GM_registerMenuCommand(`▶️ 启动/继续`, () => {
        GM_setValue('script_is_running', true);
        isRunning = true;
        runCurrentStep();
    });

    GM_registerMenuCommand("🔄 强制重置", () => {
        GM_setValue('script_step_index', 0);
        GM_setValue('script_is_running', false);
        GM_setValue('current_temp_email', '');
        location.reload();
    });

    async function runCurrentStep() {
        if (!GM_getValue('script_is_running', false)) return;

        // 异常保护:索引越界重置
        if (currentIndex >= actions.length) {
            GM_setValue('script_step_index', 0);
            return location.reload();
        }

        const action = actions[currentIndex];
        console.log(`[Step ${currentIndex + 1}] ${action.step_name} (${action.type})`);

        // URL 检查 (如果不在目标域名,等待跳转)
        if (action.url_keyword && !location.href.includes(action.url_keyword)) {
            console.log(`等待跳转到 ${action.url_keyword}...`);
            return setTimeout(runCurrentStep, 2000);
        }

        try {
            await new Promise(r => setTimeout(r, 3000)); // 基础缓冲
            let el = null;
            if (action.selector) el = await waitForElement(action.selector);

            // --- 动作分发 ---
            if (action.type === 'get_email') {
                const email = await mailClient.getEmail();
                console.log("获取邮箱:", email);
                GM_setValue('current_temp_email', email);
                GM_setClipboard(email);

                el.click(); el.focus();
                setNativeValue(el, email);
                el.dispatchEvent(new Event('change', { bubbles: true }));
                el.blur();
            }
            else if (action.type === 'fill_code') {
                const email = GM_getValue('current_temp_email');
                const rawCode = await mailClient.waitForCode(email);
                const code = rawCode.replace(/-/g, ''); // 清洗连字符
                console.log('填入验证码:', code);

                el.scrollIntoView({block: "center"});
                el.click(); el.focus();
                const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
                nativeSetter.call(el, code);
                el.dispatchEvent(new Event('input', { bubbles: true }));
                el.dispatchEvent(new Event('change', { bubbles: true }));
                await new Promise(r => setTimeout(r, 500));
                el.blur();
            }
            else if (action.type === 'input') {
                el.focus();
                let val = action.value;

                // 处理随机变量
                if (val === '__RANDOM__') val = getRandomName();
                if (val === '__RANDOM_PASS__') val = getRandomPassword();

                console.log(`[Input] 填写值: ${val}`);
                setNativeValue(el, val);
                el.blur();
            }
            else if (action.type === 'wait_url_and_upload') {
                if (!location.href.includes(action.target_url)) {
                    return setTimeout(runCurrentStep, 1500); // URL不对,继续等待
                }

                // 尝试多次上传,防止 Cookie 未即时写入
                let retry = 0;
                while (retry < 5) {
                    try {
                        await extractAndUploadToken();
                        GM_notification({ text: 'Token 上传成功!准备清理...', title: '成功' });
                        break;
                    } catch (e) {
                        console.warn("Token提取失败,重试中...", e);
                        await new Promise(r => setTimeout(r, 2000));
                        retry++;
                    }
                }
                // 继续下一步
            }
            else if (action.type === 'clean_and_restart') {
                GM_notification({ text: '清理 Cookie 并重启循环...', title: '系统维护' });
                await executeCleanCookies(action.clean_targets);

                GM_setValue('script_step_index', 0);
                GM_setValue('current_temp_email', '');

                console.log(">>> 循环重置完成,3秒后刷新");
                setTimeout(() => {
                    window.location.href = "https://grok.com/";
                }, 3000);
                return; // 结束本次执行栈
            }
            else if (action.type === 'click') {
                el.click();
            }

            // --- 步进逻辑 ---
            currentIndex++;
            GM_setValue('script_step_index', currentIndex);
            setTimeout(runCurrentStep, 1500);

        } catch (e) {
            console.error("执行出错:", e);
            // 遇到严重错误可以考虑刷新页面重试
            // setTimeout(() => location.reload(), 5000);
        }
    }

    // 启动检测
    function tryStart() {
        if (isRunning) {
            setTimeout(runCurrentStep, 1500);
        }
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        tryStart();
    } else {
        window.addEventListener('load', tryStart);
    }

})();

修改 extractAndUploadToken 填入自己的 Grok2api 地址和凭证

开启无痕窗口打开 Grok,接着启动脚本即可

会自动生成邮箱、填写注册信息和验证码

IP 不好 CF 验证不会自动过,需要手动继续和点击下一步

IP 好就全自动了啦

循环注册 w