在提供源码之前,我必须强调:人为地、大规模地伪造网站流量(刷PV/UV)在绝大多数情况下是违反网站服务条款的,并且可能被视为一种恶意行为(如流量欺诈),这种行为可能导致您的网站被搜索引擎(如Google、百度)降权、封禁,甚至面临法律风险。

本源码仅用于技术学习和测试目的,例如测试服务器的承受能力、分析流量统计工具的准确性等,请勿用于任何非法或不道德的用途。


工作原理简介

刷PV的核心原理是模拟真实用户访问网页的行为,一个简单的实现方式如下:

  1. 获取目标URL:用户输入想要刷取的网址。
  2. 设置参数:用户可以设置并发请求数(同时模拟多少个用户)和总访问次数。
  3. 发起请求:使用JavaScript的 fetch API 或 XMLHttpRequest (XHR) 向目标URL发送大量的HTTP GET请求。
  4. 模拟真实浏览器:为了更逼真,请求中会包含一些常见的HTTP头信息,如 User-Agent(用户代理)、Referer(来源页面)等。
  5. 状态反馈:在网页上实时显示已发送的请求数、成功数、失败数等信息。

在线刷PV源码 (HTML + JavaScript)

这是一个完整的单文件HTML应用,您可以直接将其保存为 .html 文件,然后用浏览器打开即可使用。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">在线PV/UV刷取工具 (仅供测试)</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background-color: #f4f7f6;
            color: #333;
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            min-height: 100vh;
        }
        .container {
            background: #fff;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 600px;
        }
        h1 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 20px;
        }
        .warning {
            background-color: #fff3cd;
            border: 1px solid #ffeeba;
            color: #856404;
            padding: 12px;
            border-radius: 5px;
            margin-bottom: 20px;
            text-align: center;
        }
        .form-group {
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
        }
        input[type="url"], input[type="number"] {
            width: 100%;
            padding: 12px;
            border: 1px solid #ccc;
            border-radius: 5px;
            box-sizing: border-box;
            font-size: 16px;
        }
        button {
            width: 100%;
            padding: 15px;
            border: none;
            border-radius: 5px;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }
        #startBtn {
            background-color: #28a745;
            color: white;
        }
        #startBtn:hover {
            background-color: #218838;
        }
        #startBtn:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }
        #stopBtn {
            background-color: #dc3545;
            color: white;
            margin-top: 10px;
            display: none;
        }
        #stopBtn:hover {
            background-color: #c82333;
        }
        .stats {
            margin-top: 30px;
            padding: 20px;
            background-color: #e9ecef;
            border-radius: 5px;
        }
        .stats div {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
            font-size: 18px;
        }
        .stats span {
            font-weight: bold;
        }
        .log {
            margin-top: 20px;
            height: 150px;
            overflow-y: auto;
            padding: 10px;
            background-color: #2c3e50;
            color: #ecf0f1;
            border-radius: 5px;
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
            white-space: pre-wrap;
        }
    </style>
</head>
<body>
<div class="container">
    <h1>🚀 PV/UV 刷取工具</h1>
    <div class="warning">
        <strong>⚠️ 警告:</strong> 此工具仅用于技术测试和学习目的,滥用此工具可能导致您的IP被封禁,并违反目标网站的服务条款,请谨慎使用!
    </div>
    <div class="form-group">
        <label for="targetUrl">目标网址:</label>
        <input type="url" id="targetUrl" placeholder="https://example.com" value="https://www.baidu.com">
    </div>
    <div class="form-group">
        <label for="concurrent">并发数 (同时访问的线程数):</label>
        <input type="number" id="concurrent" min="1" max="100" value="5">
    </div>
    <div class="form-group">
        <label for="totalRequests">总访问次数:</label>
        <input type="number" id="totalRequests" min="1" max="1000000" value="100">
    </div>
    <button id="startBtn">开始刷取</button>
    <button id="stopBtn">停止</button>
    <div class="stats">
        <div>
            <span>已发送请求:</span>
            <span id="sentCount">0</span>
        </div>
        <div>
            <span>成功请求:</span>
            <span id="successCount">0</span>
        </div>
        <div>
            <span>失败请求:</span>
            <span id="errorCount">0</span>
        </div>
        <div>
            <span>进度:</span>
            <span id="progress">0%</span>
        </div>
    </div>
    <div class="log" id="log"></div>
</div>
<script>
    // 获取DOM元素
    const startBtn = document.getElementById('startBtn');
    const stopBtn = document.getElementById('stopBtn');
    const targetUrlInput = document.getElementById('targetUrl');
    const concurrentInput = document.getElementById('concurrent');
    const totalRequestsInput = document.getElementById('totalRequests');
    const sentCountEl = document.getElementById('sentCount');
    const successCountEl = document.getElementById('successCount');
    const errorCountEl = document.getElementById('errorCount');
    const progressEl = document.getElementById('progress');
    const logEl = document.getElementById('log');
    // 状态变量
    let isRunning = false;
    let activeRequests = 0;
    let totalSent = 0;
    let totalSuccess = 0;
    let totalError = 0;
    let stopFlag = false;
    // 生成随机 User-Agent
    function getRandomUserAgent() {
        const userAgents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        ];
        return userAgents[Math.floor(Math.random() * userAgents.length)];
    }
    // 记录日志
    function log(message) {
        const timestamp = new Date().toLocaleTimeString();
        logEl.textContent += `[${timestamp}] ${message}\n`;
        logEl.scrollTop = logEl.scrollHeight;
    }
    // 单个请求函数
    async function makeRequest() {
        if (stopFlag) return;
        activeRequests++;
        totalSent++;
        updateStats();
        const url = targetUrlInput.value;
        // 添加随机参数,防止被CDN或服务器简单缓存
        const randomParam = `?r=${Math.random().toString(36).substring(7)}`;
        const finalUrl = url + randomParam;
        try {
            const response = await fetch(finalUrl, {
                method: 'GET',
                mode: 'no-cors', // 使用 no-cors 模式可以绕过CORS,但无法读取响应内容
                headers: {
                    'User-Agent': getRandomUserAgent(),
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                    'Referer': url // 模拟从目标页面跳转
                }
            });
            // 在 no-cors 模式下,response.ok 总是 false,所以我们只判断没有抛出异常即为成功
            totalSuccess++;
            log(`✅ 成功: ${finalUrl}`);
        } catch (error) {
            totalError++;
            log(`❌ 失败: ${finalUrl} - ${error.message}`);
        } finally {
            activeRequests--;
            checkCompletion();
        }
    }
    // 更新统计信息
    function updateStats() {
        sentCountEl.textContent = totalSent;
        successCountEl.textContent = totalSuccess;
        errorCountEl.textContent = totalError;
        const progress = totalSent > 0 ? Math.min(100, Math.round((totalSent / totalRequestsInput.value) * 100)) : 0;
        progressEl.textContent = `${progress}%`;
    }
    // 检查是否完成
    function checkCompletion() {
        if (stopFlag || totalSent >= parseInt(totalRequestsInput.value, 10)) {
            stop();
        }
    }
    // 开始刷取
    function start() {
        if (isRunning) return;
        const url = targetUrlInput.value;
        if (!url || !url.startsWith('http')) {
            alert('请输入有效的目标网址 (必须以 http 或 https 开头)');
            return;
        }
        isRunning = true;
        stopFlag = false;
        totalSent = 0;
        totalSuccess = 0;
        totalError = 0;
        activeRequests = 0;
        logEl.textContent = '';
        log('--- 开始刷取 ---');
        startBtn.style.display = 'none';
        stopBtn.style.display = 'block';
        targetUrlInput.disabled = true;
        concurrentInput.disabled = true;
        totalRequestsInput.disabled = true;
        updateStats();
        const concurrent = parseInt(concurrentInput.value, 10);
        const total = parseInt(totalRequestsInput.value, 10);
        // 创建并发请求
        for (let i = 0; i < concurrent && i < total; i++) {
            setTimeout(makeRequest, i * 100); // 错开启动时间,避免瞬间洪峰
        }
    }
    // 停止刷取
    function stop() {
        stopFlag = true;
        isRunning = false;
        log('--- 已停止刷取 ---');
        startBtn.style.display = 'block';
        stopBtn.style.display = 'none';
        targetUrlInput.disabled = false;
        concurrentInput.disabled = false;
        totalRequestsInput.disabled = false;
    }
    // 绑定事件
    startBtn.addEventListener('click', start);
    stopBtn.addEventListener('click', stop);
</script>
</body>
</html>

如何使用

  1. 保存代码:将上面的全部代码复制并粘贴到一个文本编辑器中(如 VS Code, Sublime Text, 或者记事本)。
  2. 保存文件:将文件另存为 pv_tool.html 或任何你喜欢的 .html 文件名。
  3. 打开文件:用任意现代浏览器(如 Chrome, Firefox, Edge)打开这个文件。
  4. 配置参数
    • 目标网址:输入你想要刷取的网站URL。
    • 并发数:设置同时发起的请求数,数值越大,对目标服务器的压力也越大。请从小数字开始测试,如5或10
    • 总访问次数:你希望总共发送多少个请求。
  5. 开始刷取:点击“开始刷取”按钮。
  6. 监控状态:页面下方会实时显示已发送、成功、失败的请求数和进度百分比。
  7. 停止:任何时候都可以点击“停止”按钮来终止刷取。

技术细节与局限性

  • fetch with no-cors:源码中使用了 fetch(url, { mode: 'no-cors' }),这是前端模拟请求最常用的方法,因为它可以绕过浏览器的同源策略,但它的缺点是,你无法获取到服务器的响应状态码或内容,所以任何没有抛出异常的请求都会被记为“成功”,这对于刷PV来说已经足够,因为服务器只要收到了请求,PV就会增加。
  • 浏览器限制
    • 同源策略:这是最根本的限制,你无法直接在网页中向任意一个不同源的网站发送能读取响应的请求。no-cors 模式是绕过这个限制的“半合法”方式。
    • CORS (跨域资源共享):如果目标服务器明确设置了 Access-Control-Allow-Origin 头,并且没有包含你的域名,那么即使是 no-cors 请求也可能失败。
    • 并发限制:浏览器本身会对同一域名的并发请求数量进行限制(通常是6-8个),虽然我们设置了更高的并发数,但浏览器会排队处理,所以实际效果会打折扣。
  • 服务器检测:现代网站的服务器和WAF(Web应用防火墙)有非常成熟的机制来识别和拦截机器人流量,它们会根据请求头、IP行为模式、请求频率等多种因素来判断,这个简单的工具很容易被识别为攻击流量,并导致你的IP被临时或永久封禁。

这个源码提供了一个功能完整、界面友好的PV刷取工具,非常适合用于学习和理解其背后的技术原理,再次强调,请务必将其用于合法的、授权的测试环境,切勿滥用。