Files
links/_php_code/Xinghe2.php
T

176 lines
5.9 KiB
PHP
Raw Normal View History

2026-06-20 04:47:34 +08:00
<?php
require_once __DIR__ . '/UnifiedScrmData.class.php';
/**
* 数据爬虫类
* 用于处理需要先获取 Token 授权,再读取接口数据的场景
*/
class Xinghe
{
private $cookieFile;
private $ch; // cURL 句柄
private $pageUrl;
private $account;
private $password;
private $unifiedData;
private $baseUrl;
/**
* 构造函数:初始化 cURL 和 Cookie 文件
*/
public function __construct($pageUrl, $username, $password)
{
$this->pageUrl = $pageUrl;
$this->username = $username;
$this->password = $password;
$parsedUrl = parse_url($pageUrl);
$scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'] . '://' : '';
$host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
$baseUrl = $scheme . $host;
$this->baseUrl = $baseUrl;
$this->unifiedData = new UnifiedScrmData();
}
public function run()
{
// 1. 创建临时 Cookie 文件
$this->cookieFile = tempnam(sys_get_temp_dir(), 'spider_cookie_');
if ($this->cookieFile === false) {
throw new \RuntimeException("无法在系统临时目录创建 Cookie 文件");
}
// 2. 初始化 cURL 并设置全局参数
$this->ch = curl_init();
$apiUrl = '/share/share/api_yinliu_count.html?page=1&limit=10&id=&class_id=&is_repet=1&start_time=&end_time=';
$defaultOptions = [
CURLOPT_RETURNTRANSFER => true, // 将结果返回为字符串
CURLOPT_HEADER => false, // 不输出响应头
CURLOPT_TIMEOUT => 15, // 设置超时时间(秒)
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
// 核心优化:同时指定读写 Cookie 为同一个文件,cURL 会自动维护会话
CURLOPT_COOKIEJAR => $this->cookieFile,
CURLOPT_COOKIEFILE => $this->cookieFile,
];
curl_setopt_array($this->ch, $defaultOptions);
$this->authenticate();
$first_page_data = $this->fetchApiData($apiUrl); // 首页数据
$this->unifiedData->total = $first_page_data['count']; // 总在线人数
$this->unifiedData->todayNewCount = $first_page_data['totalRow']['day_sum']; // 今日新增在线人数
$total_pages = ceil($this->unifiedData->total/10); // 总页数
$allListPagesData = [$first_page_data['data']]; // 初始化列表容器,装入第一页数据
for ($page = 2; $page <= $total_pages; $page++) {
$apiUrl = '/share/share/api_yinliu_count.html?page=' . $page . '&limit=10&id=&class_id=&is_repet=1&start_time=&end_time=';
$page_data = $this->fetchApiData($apiUrl);
$allListPagesData[] = $page_data['data'];
}
return $this->parseToUnifiedData($first_page_data, $allListPagesData);
}
// 清爽至极的数据清洗:详情是详情,列表是列表
protected function parseToUnifiedData($detailData, $allListPagesData)
{
$unifiedData = $this->unifiedData;
// 循环合并了所有页数的 List 数组
foreach ($allListPagesData as $records) {
foreach ($records as $item) {
if(!empty($item['user'])) {
$number = $item['user'] ?? null;
$isOnline = (isset($item['online']) && $item['online'] == 1);
$unifiedData->addNumber($number, $isOnline, $item['day_sum']);
}
}
}
return $unifiedData;
}
/**
* 析构函数:释放资源,清理垃圾
*/
public function __destruct()
{
// 关闭 cURL 会话
if (is_resource($this->ch) || $this->ch instanceof \CurlHandle) {
curl_close($this->ch);
}
// 删除临时 Cookie 文件
if (file_exists($this->cookieFile)) {
unlink($this->cookieFile);
}
}
/**
* 第一步:访问授权页面,建立会话
*
* @param string $authUrl 包含 token 的授权地址
* @return bool 授权请求是否成功
* @throws \Exception
*/
public function authenticate()
{
$this->sendRequest($this->pageUrl);
return true; // 如果没有抛出异常,则认为请求成功
}
/**
* 第二步:请求 API 接口获取数据
*
* @param string $apiUrl 接口数据地址
* @return array|null 解析后的数组数据,如果解析失败返回 null
* @throws \Exception
*/
public function fetchApiData($apiUrl)
{
$response = $this->sendRequest($this->baseUrl . $apiUrl);
// 尝试解析 JSON 数据
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \RuntimeException("JSON 解析失败: " . json_last_error_msg() . "。原始响应: " . $response);
}
return $data;
}
/**
* 发送 cURL 请求的底层私有方法
*
* @param string $url 目标地址
* @return string 服务器响应内容
* @throws \Exception
*/
private function sendRequest($url)
{
curl_setopt($this->ch, CURLOPT_URL, $url);
$response = curl_exec($this->ch);
// 检查是否有网络或 cURL 底层错误
if ($response === false) {
$error = curl_error($this->ch);
throw new \RuntimeException("请求失败 [{$url}]: {$error}");
}
// 检查 HTTP 状态码
$httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
if ($httpCode >= 400) {
throw new \RuntimeException("HTTP 请求异常 [{$url}],状态码: {$httpCode}");
}
return (string)$response;
}
}