前言

因为我的博客关于页中网站数据是基于51la的接口制作的,但51la的接口调用存在次数限制,每个月100次,换算下来每天只能调用3次左右,虽然做了静态化缓存,勉强也能用,但就是数据更新不够及时,所以就折腾了一下,打算用百度统计的接口做个API来替换掉51la。

实现效果

API链接:https://api.huib.top/?api=baiduStatistics

{
    "code": 200,
    "time": "2024-11-22 13:12:35",
    "data": {
        "curUv": 7,
        "curSv": 11,
        "beforeUv": 21,
        "beforeSv": 101,
        "monthSv": 384,
        "yearSv": 3191
    }
}

部署教程

复制以下代码,保存为 baiduStatistics.php,放到你的网站根目录中,就可以通过以下链接来访问这个API接口了

http://127.0.0.1/baiduStatistics.php

<?php

header("Access-Control-Allow-Origin: *");

// 使用前请修改配置
$apiKey = '这里替换为你的apikey';
$secretKey = '替换为你的secretKey';
$siteId = '替换为你的siteId';
$code = '替换为你的code';

$cacheFile = 'lib/cache/' . pathinfo(__FILE__)['filename'] . '.json';
$cacheKey = 'lib/cache/' . pathinfo(__FILE__)['filename'] . '_key.json';
$cacheTime = 10800;

function getAccessToken($apiKey, $secretKey, $code) {
    $url = "https://openapi.baidu.com/oauth/2.0/token";
    $params = array(
        'grant_type' => 'authorization_code',
        'code' => $code,
        'client_id' => $apiKey,
        'client_secret' => $secretKey,
        'redirect_uri' => 'oob',
    );

    $query = http_build_query($params);
    $response = @file_get_contents($url . '?' . $query); // 使用@符号忽略警告
    if ($response === false) {
        header('Content-Type: application/json');
        exit(json_encode(array(
            'code' => 403,
            'msg'  => '获取访问令牌时出错'
        ))); // 返回错误信息给前端
    }
    $data = json_decode($response, true);
    return $data;
}



function refreshAccessToken($apiKey, $secretKey, $refreshToken) {
    $url = "https://openapi.baidu.com/oauth/2.0/token";
    $params = array(
        'grant_type' => 'refresh_token',
        'refresh_token' => $refreshToken,
        'client_id' => $apiKey,
        'client_secret' => $secretKey,
    );

    $query = http_build_query($params);
    $response = @file_get_contents($url . '?' . $query); // 使用@符号忽略警告
    if ($response === false) {
        throw new Exception('Error refreshing access token.');
    }
    $data = json_decode($response, true);

    if (isset($data['error'])) {
        throw new Exception('Error refreshing access token: ' . $data['error_description']);
    }

    return $data;
}



function getData($startDate, $endDate, $metrics, $accessToken, $siteId) {
    $url = "https://openapi.baidu.com/rest/2.0/tongji/report/getData";
    $params = array(
        'access_token' => $accessToken,
        'site_id' => $siteId,
        'method' => 'overview/getTimeTrendRpt',
        'start_date' => $startDate,
        'end_date' => $endDate,
        'metrics' => $metrics,
    );

    $query = http_build_query($params);
    $fullUrl = $url . '?' . $query;

    $response = @file_get_contents($fullUrl); // 使用@符号忽略警告
    if ($response === false) {
        throw new Exception('Error fetching data.');
    }
    return json_decode($response, true);
}


function saveTokens($accessToken, $refreshToken) {
    global $cacheKey;
    @file_put_contents(
        $cacheKey, 
        json_encode(
            array(
                'access_token' => $accessToken,
                'refresh_token' => $refreshToken,
            )
        )
    );
}

function loadTokens() {
    global $cacheKey;
    if (!file_exists($cacheKey)) {
        return null;
    }
    return json_decode(file_get_contents($cacheKey), true);
}


function checkAndRefreshTokens($apiKey, $secretKey) {
    $tokens = loadTokens();
    if ($tokens === null || expired($tokens['access_token'])) {
        global $code;
        $tokens = getAccessToken($apiKey, $secretKey, $code);
        saveTokens($tokens['access_token'], $tokens['refresh_token']);
    } elseif (expired($tokens['refresh_token'])) {
        // 如果 refresh token 过期,返回获取授权码地址的文本信息给前端
        die('获取授权码地址:http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=' . $apiKey . '&redirect_uri=oob&scope=basic&display=popup');
    } else {
        $attempts = 0;
        $maxAttempts = 3;
        while ($attempts < $maxAttempts) {
            try {
                $tokens = refreshAccessToken($apiKey, $secretKey, $tokens['refresh_token']);
                saveTokens($tokens['access_token'], $tokens['refresh_token']);
                break;
            } catch (Exception $e) {
                $attempts++;
                if ($attempts == $maxAttempts) {
                    // 如果刷新 access token 失败,返回获取授权码地址的文本信息给前端
                    die('获取授权码地址:http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=' . $apiKey . '&redirect_uri=oob&scope=basic&display=popup');
                }
                sleep(1); // 延迟一秒后重试
            }
        }
    }
    return $tokens['access_token'];
}

function expired($token) {
    // 检查 token 是否过期,这里假设 access token 过期时间为一个月
    // 实际情况应该根据百度开放平台的规定来判断
    // 这里简化为每次都重新获取新 token
    return false; // 返回 true 或 false
}

$accessToken = checkAndRefreshTokens($apiKey, $secretKey);

// 检查缓存文件是否存在且未过期
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
    $data = json_decode(file_get_contents($cacheFile), true);
} else {
    // 准备数据
    $data = array(
        'curUv' => null,
        'curSv' => null,
        'beforeUv' => null,
        'beforeSv' => null,
        'monthSv' => null,
        'yearSv' => null,
    );

    // 获取最近31天的数据
    $startDate = date('Ymd', strtotime('-31 days'));
    $endDate = date('Ymd');
    $monthData = getData($startDate, $endDate, 'pv_count,visitor_count', $accessToken, $siteId);

    // 处理并提取最近31天的数据
    $last31DaysPV = 0;
    if (isset($monthData['result']['items'][1])) {
        $dataPoints = $monthData['result']['items'][1];
        foreach ($dataPoints as $point) {
        $last31DaysPV += (int)$point[0];
        }
    }


    // 获取一整年的数据
    $startDate = date('Ymd', strtotime('-1 year'));
    $endDate = date('Ymd');
    $yearData = getData($startDate, $endDate, 'pv_count,visitor_count', $accessToken, $siteId);

    // 处理并提取所需数据
    if (isset($yearData['result']['items'][1])) {
        $dataPoints = $yearData['result']['items'][1];
        $today = date('Y/m/d');
        $yesterday = date('Y/m/d', strtotime('-1 day'));
        $lastMonth = date('Y/m/d', strtotime('-30 days'));


        foreach ($yearData['result']['items'][0] as $index => $date) {
            if ($date[0] == $today) {
                $data['curUv'] = (int)$dataPoints[$index][1];
                $data['curSv'] = (int)$dataPoints[$index][0];
            } elseif ($date[0] == $yesterday) {
                $data['beforeUv'] = (int)$dataPoints[$index][1];
                $data['beforeSv'] = (int)$dataPoints[$index][0];
            } elseif ($date[0] == $lastMonth) {
                $data['monthSv'] = (int)$dataPoints[$index][0];
            }
        }
        
        $data['yearSv'] = array_sum(array_map('intval', array_column($dataPoints, 0)));
    }

    // 添加最近31天的PV总和
    $data['monthSv'] = $last31DaysPV;
    $data = array(
        'code' => 200,
        'time' => date('Y-m-d H:i:s'),
        'data' => $data
    );

    // 保存数据到缓存文件
    @file_put_contents($cacheFile, json_encode($data));
}

// 返回JSON数据
header('Content-Type: application/json');
echo json_encode($data);

?>

修改代码

因为调用的是百度账号的 API 接口,在部署之后我们需要修改以下代码才能正常使用:

// 使用前请修改配置
$apiKey = '这里替换为你的apikey';
$secretKey = '替换为你的secretKey';
$siteId = '替换为你的siteId';
$code = '替换为你的code';

1、获取API Key 和 Secret Key

首先进入百度统计》管理》数据导出服务,获取到 API Key 和 Secret Key ,并保存下来备用

2、获取code

将以下链接中的 {CLIENT_ID} 需要修改成你的 API Key,然后复制链接进入百度账号登录页,此处的登录账号就是您登录百度统计查看报告数据的账号,登录完成后将跳转至获取code的页面,也就是授权码。

http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri=oob&scope=basic&display=popup

3、获取 siteid

siteid 为你的网站 id,查看百度统计报告的url中会显示 siteid,例如:

将刚刚获取到的API Key、Secret Key、Code、Siteid,填入PHP对应的变量赋值中,然后访问一次接口即可,必须先访问一次,否则授权码会很快过期。