新球体育网(球探)手机端网页,比赛的分析页面里按开盘顺序列出所有公司,选择所需的公司列出各个时间点的欧赔作对比。
// ==UserScript==
// @name 新球体育网欧赔分析
// @namespace http://dol.freevar.com/
// @version 0.9
// @description 新球体育网(球探)手机端网页,比赛的分析页面里按开盘顺序列出所有公司,选择所需的公司列出各个时间点的欧赔作对比。
// @author Dolphin
// @match https://m.titan007.com/analy/Analysis/*
// @match https://m.titan007.com/Analy/Analysis/*
// @run-at document-idle
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 请求赔率数据
GM_xmlhttpRequest({
method: 'GET',
url: `https://txt.titan007.com/1x2/${scheduleId}.js`,
onload: function(response) {
if (response.status === 200) {
processOddsData(response.responseText);
} else {
console.log('请求赔率数据失败:', response.status);
}
},
onerror: function(error) {
alert('请求赔率数据出错:', error);
}
});
function processOddsData(data) {
// 解析公司数据
const companyMap = {};
const companyGameMatch = data.match(/var game=Array\((.*?)\);/);
if (companyGameMatch && companyGameMatch[1]) {
const companyData = companyGameMatch[1].split('","').map(item => item.replace(/"/g, ''));
companyData.forEach(item => {
const fields = item.split('|');
if (fields.length >= 3) {
const companyId = fields[1];
const companyName = fields[2];
companyMap[companyId] = companyName;
}
});
}
// 解析赔率详细数据
const oddsDetailMatch = data.match(/var gameDetail=Array\((.*?)\);/);
if (!oddsDetailMatch || !oddsDetailMatch[1]) {
console.log('未找到赔率详细数据');
return;
}
const oddsData = {};
const detailData = oddsDetailMatch[1].split('","').map(item => item.replace(/"/g, ''));
detailData.forEach(item => {
const [companyId, oddsString] = item.split('^');
if (oddsString) {
const oddsRecords = oddsString.split(';').filter(record => record.trim());
oddsData[companyId] = oddsRecords.map(record => {
const fields = record.split('|');
if (fields.length >= 8) {
return {
win: parseFloat(fields[0]),
draw: parseFloat(fields[1]),
lose: parseFloat(fields[2]),
time: fields[3], // 月-日 时:分
year: fields[7]
};
}
return null;
}).filter(record => record !== null);
}
});
// 创建UI界面
createUI(companyMap, oddsData);
}
function createUI(companyMap, oddsData) {
const contentDiv = document.querySelector('div#content');
if (!contentDiv) return;
// 创建容器
const container = document.createElement('div');
container.id = 'ctrlPanel';
container.style.fontSize = '16px';
container.style.textAlign = 'center';
// 按最早赔率时间排序公司
const sortedCompanies = Object.keys(companyMap).sort((a, b) => {
const timeA = getEarliestTime(oddsData[a]);
const timeB = getEarliestTime(oddsData[b]);
return timeA - timeB;
});
// 创建公司复选框
sortedCompanies.forEach(companyId => {
const companyName = companyMap[companyId];
const checkboxContainer = document.createElement('div');
checkboxContainer.style.display = 'inline-block';
checkboxContainer.style.margin = '5px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.value = companyId;
checkbox.id = `company_${companyId}`;
checkbox.checked = false;
checkbox.style.position = 'initial';
const span = document.createElement('span');
span.textContent = companyName;
//重点公司标红
const keyCompanies = ['Pinnacle', '18Bet', 'Vcbet', '188bet', 'Bet 365', 'Wewbet', 'Easybets', 'Marathonbet'];
if (keyCompanies.includes(companyName)) span.style.color = '#f00';
span.style.cursor = 'pointer';
span.onclick = function () { checkbox.checked = !checkbox.checked; };
checkboxContainer.appendChild(checkbox);
checkboxContainer.appendChild(span);
container.appendChild(checkboxContainer);
});
// 创建按钮
const button = document.createElement('button');
button.textContent = '欧赔概率';
button.style.padding = '8px 10px';
button.style.backgroundColor = '#007bff';
button.style.color = 'white';
button.style.border = 'none';
button.style.borderRadius = '4px';
button.style.cursor = 'pointer';
button.onclick = () => {
showOddsProbability(companyMap, oddsData);
};
container.appendChild(document.createElement('br'));
container.appendChild(button);
// 插入到页面中
contentDiv.insertBefore(container, contentDiv.firstChild);
}
function getEarliestTime(oddsRecords) {
if (!oddsRecords || oddsRecords.length === 0) return Infinity;
return Math.min(...oddsRecords.map(record => {
const [monthDay, time] = record.time.split(' ');
const [month, day] = monthDay.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
// 使用记录中的年份
const year = parseInt(record.year);
return new Date(year, month - 1, day, hour, minute).getTime();
}));
}
function showOddsProbability(companyMap, oddsData) {
// 获取选中的公司
const selectedCompanies = Array.from(document.querySelectorAll('div#ctrlPanel input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
if (selectedCompanies.length === 0) {
alert('请至少选择一家公司');
return;
}
// 获取比赛时间
const homeMatchTime = parseInt(jsonData.nearMatches.homeMatches.matches[0].matchTime) * 1000;
const awayMatchTime = parseInt(jsonData.nearMatches.awayMatches.matches[0].matchTime) * 1000;
const homeEndTime = homeMatchTime + 2 * 60 * 60 * 1000;
const awayEndTime = awayMatchTime + 2 * 60 * 60 * 1000;
// 收集所有时间点
const allTimePoints = new Set();
const companyColors = {};
const colorPalette = ['#000000', '#FF0000', '#0000FF', '#008000', '#800080', '#FFA500', '#00FFFF', '#FF00FF', '#800000', '#008080'];
selectedCompanies.forEach((companyId, index) => {
companyColors[companyId] = colorPalette[index % colorPalette.length];
if (oddsData[companyId]) {
oddsData[companyId].forEach(record => {
const timestamp = convertToTimestamp(record.time, record.year);
allTimePoints.add(timestamp);
});
}
});
// 添加必须的时间点
allTimePoints.add(homeMatchTime);
allTimePoints.add(homeEndTime);
allTimePoints.add(awayMatchTime);
allTimePoints.add(awayEndTime);
// 排序时间点
const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
// 创建表格
const table = document.createElement('table');
table.style.borderCollapse = 'collapse';
table.style.margin = '10px auto 0';
// 表头
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
['时间', '胜概率', '平概率', '负概率', '公司', '返还率'].forEach(text => {
const th = document.createElement('th');
th.textContent = text;
th.style.border = '1px solid #888';
th.style.backgroundColor = '#eee';
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// 表格内容
const tbody = document.createElement('tbody');
sortedTimePoints.forEach(timestamp => {
// 检查是否在比赛期间
const isDuringHomeMatch = (timestamp >= homeMatchTime && timestamp <= homeEndTime);
const isDuringAwayMatch = (timestamp >= awayMatchTime && timestamp <= awayEndTime);
const isDuringMatches = isDuringHomeMatch || isDuringAwayMatch;
let hasDataForThisTime = false;
// 为每个选中的公司创建行
selectedCompanies.forEach(companyId => {
const companyOdds = oddsData[companyId];
if (!companyOdds) return;
const record = companyOdds.find(r =>
convertToTimestamp(r.time, r.year) === timestamp
);
if (record) {
hasDataForThisTime = true;
const row = createDataRow(record, companyMap[companyId], companyColors[companyId], isDuringMatches);
tbody.appendChild(row);
}
});
// 如果没有数据但这是必须的时间点,创建空行
if (!hasDataForThisTime && [homeMatchTime, homeEndTime, awayMatchTime, awayEndTime].includes(timestamp)) {
const row = document.createElement('tr');
if (isDuringMatches) {
row.style.backgroundColor = '#ffb';
}
const timeCell = document.createElement('td');
timeCell.style.border = '1px solid #888';
timeCell.textContent = formatTimestamp(timestamp);
row.appendChild(timeCell);
// 空单元格
for (let i = 0; i < 5; i++) {
const emptyCell = document.createElement('td');
emptyCell.style.border = '1px solid #888';
row.appendChild(emptyCell);
}
tbody.appendChild(row);
}
});
table.appendChild(tbody);
// 移除已存在的表格
const existingTable = document.querySelector('#oddsProbabilityTable');
if (existingTable) {
existingTable.remove();
}
table.id = 'oddsProbabilityTable';
// 插入表格
const container = document.querySelector('div#ctrlPanel');
container.appendChild(table);
}
function createDataRow(record, companyName, color, isDuringMatches) {
const row = document.createElement('tr');
row.style.color = color;
if (isDuringMatches) {
row.style.backgroundColor = '#ffb';
}
// 计算概率和返还率
const returnRate = 1/(1/record.win+1/record.draw+1/record.lose);
const winProb = (returnRate / record.win * 100).toFixed(2);
const drawProb = (returnRate / record.draw * 100).toFixed(2);
const loseProb = (returnRate / record.lose * 100).toFixed(2);
// 时间单元格
const timeCell = document.createElement('td');
timeCell.style.border = '1px solid #888';
timeCell.textContent = record.time;
row.appendChild(timeCell);
// 概率单元格
[winProb, drawProb, loseProb].forEach(prob => {
const probCell = document.createElement('td');
probCell.style.border = '1px solid #888';
probCell.textContent = prob;
row.appendChild(probCell);
});
// 公司单元格
const companyCell = document.createElement('td');
companyCell.style.border = '1px solid #888';
companyCell.textContent = companyName;
row.appendChild(companyCell);
// 返还率单元格
const returnCell = document.createElement('td');
returnCell.style.border = '1px solid #888';
returnCell.textContent = (returnRate * 100).toFixed(2);
row.appendChild(returnCell);
return row;
}
function convertToTimestamp(timeStr, yearStr) {
const [monthDay, time] = timeStr.split(' ');
const [month, day] = monthDay.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
const year = parseInt(yearStr);
return new Date(year, month - 1, day, hour, minute).getTime();
}
function formatTimestamp(timestamp) {
const date = new Date(timestamp);
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${month}-${day} ${hours}:${minutes}`;
}
})();