本站更新:已替换成 who.cx 并适配进论坛系统,使用本站无需手动添加脚本
![图片[1]|油猴脚本:Whois信息卡片 3.0|不死鸟资源网](https://www.busi.net/wp-content/uploads/2025/06/20250613063635170-image-1024x424.png)
对论坛上帖子、评论提到的域名添加 “?”,并且在点击时弹出 whois 卡片信息。
该脚本使用了 https://who.0du.ru/ 的 whois 功能,本来想用 who.cx 的,但是有跨域限制。
一些细节没有优化,比如:中文及 IDN 域名未添加识别;
卡片的加载显示时间取决于 who.0du.ru 的响应速度和存活情况。
修复:
修复原域名含超链接时将卡片也加入超链接导致冲突问题;
修复将.php、.png 等文件识别成域名后缀问题;
修复将代码块中域名添加 “问号” 的问题;
修复 “?” 被错误的插入到路径中的问题;
修复域名存在超链接时问号无法点击的问题;
增加域名后缀名单,避免将非域名添加查询;
将问号更改为 svg 放大镜图标;
对卡片插入内容做了筛选和文本更替;
若干细节优化。
脚本内容:
// ==UserScript==
// @name dalao.net 查询whois卡片
// @version 2.0
// @grant none
// @description 在 dalao.net 上对域名点击时显示 whois 查询卡片
// @author 大佬论坛
// @match *://dalao.net/*
// @match *://www.dalao.net/*
// ==/UserScript==
(function() {
// 创建关闭按钮
function createCloseButton(card) {
const closeButton = document.createElement('button');
closeButton.innerText = 'X';
closeButton.style.position = 'absolute';
closeButton.style.top = '0px';
closeButton.style.right = '0px';
closeButton.style.borderTopRightRadius = '0.6rem';
closeButton.style.backgroundColor = 'red';
closeButton.style.color = 'white';
closeButton.style.border = 'none';
closeButton.style.cursor = 'pointer';
closeButton.style.padding = '0px 6px';
closeButton.addEventListener('click', (event) => {
card.style.display = 'none';
event.stopPropagation(); // 阻止事件冒泡,防止关闭卡片时误触发点击外部事件
});
return closeButton;
}
function loadExternalCSS() {
const style = document.createElement('style');
style.innerHTML = `
.text-sm { font-size: .875rem; line-height: 1.25rem; }
.whitespace-pre-wrap { white-space: pre-wrap; }
.w-full { width: 100%; }
.text-secondary { color: #6b6b74; }
.text-xs { font-size: .75rem; line-height: 1rem; }
.py-0\\.5 { padding-top: .125rem; padding-bottom: .125rem; }
.px-1 { padding-left: .25rem; padding-right: .25rem; }
.border { border-width: 1px; }
.rounded { border-radius: .25rem; }
.whitespace-nowrap { white-space: nowrap; }
.items-center { align-items: center; }
.flex-nowrap { flex-wrap: nowrap; }
.flex-row { flex-direction: row; }
.cursor-pointer { cursor: pointer; }
.inline-flex { display: inline-flex; }
.m-0\\.5 { margin: .125rem; }
.w-2\\.5 { width: .625rem; }
.h-2\\.5 { height: .625rem; }
.mr-1 { margin-right: .25rem; }
.shrink-0 { flex-shrink: 0; }
.w-3 { width: .75rem; }
.h-3 { height: .75rem; }
.w-3\\.5 { width: .875rem; }
.h-3\\.5 { height: .875rem; }
.inline { display: inline; }
.ml-1\\.5 { margin-left: .375rem; }
.text-right { white-space: nowrap;padding-left: 1.5rem !important;}/* 自定义滚动条样式 */
::-webkit-scrollbar {
width: 12px; /* 设置垂直滚动条的宽度 */
height: 12px; /* 设置水平滚动条的高度 */
}
::-webkit-scrollbar-track {
background-color: #f1f1f1; /* 滚动条轨道颜色 */
border-radius: 0.6rem; /* 给轨道加圆角 */
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3); /* 滚动条滑块颜色 */
border-radius: 0.6rem; /* 滚动条滑块圆角 */
}
/* 设置右下角圆角效果 */
::-webkit-scrollbar-corner {
border-radius: 0.6rem; /* 右下角圆角 */
background-color: transparent; /* 使右下角背景透明 */
}
`;
document.head.appendChild(style);
}
// 创建显示 WHOIS 信息的卡片
function createHoverCard(domain) {
const card = document.createElement('div');
card.className = 'rounded-lg border bg-card text-card-foreground shadow';
card.style.position = 'absolute';
card.style.zIndex = '29'; // 确保卡片显示在最上层
card.style.backgroundColor = 'white';
card.style.border = '1px solid black';
card.style.padding = '0 0 10px 0;';
card.style.display = 'none';
card.style.borderRadius = '0.6rem';
card.style.boxShadow = 'rgba(0, 0, 0, 0.4) 0px 0px 4px';
// 添加可拖动栏
const dragBar = document.createElement('div');
dragBar.style.backgroundColor = '#f0f0f0';
dragBar.style.borderTopLeftRadius = '0.6rem';
dragBar.style.borderTopRightRadius = '0.6rem';
dragBar.style.padding = '10px';
dragBar.style.cursor = 'move';
dragBar.style.borderBottom = '1px solid #ccc';
card.appendChild(dragBar);
const closeButton = createCloseButton(card);
dragBar.appendChild(closeButton);
const contentDiv = document.createElement('div');
contentDiv.style.width = '300px';
contentDiv.style.height = '200px';
contentDiv.style.overflowY = 'auto';
card.appendChild(contentDiv);
// 实现拖动功能
let offsetX, offsetY;
dragBar.addEventListener('mousedown', (e) => {
offsetX = e.clientX - card.offsetLeft;
offsetY = e.clientY - card.offsetTop;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
});
function mouseMoveHandler(e) {
card.style.left = (e.clientX - offsetX) + 'px';
card.style.top = (e.clientY - offsetY) + 'px';
}
function mouseUpHandler() {
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
}
// 加载 WHOIS 信息并插入卡片
const loadWhois = async () => {
if (contentDiv.innerHTML === '') {
try {
// 加载外部 CSS
loadExternalCSS();
const response = await fetch(`https://who.0du.ru/${domain}`);
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
// 提取 whois 信息,并将其放入指定的表格容器内
const whoisTable = doc.querySelector('table.w-full.text-sm.mb-4.whitespace-pre-wrap');
if (whoisTable) {
contentDiv.innerHTML = ''; // 清空当前内容
// 剔除“状态”与“IANA ID”所在的 <tr> 内容
const rows = whoisTable.querySelectorAll('tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
let shouldRemoveRow = false;
cells.forEach(cell => {
if (cell.textContent.includes('状态') || cell.textContent.includes('IANA ID')) {
shouldRemoveRow = true;
} else if (cell.textContent.includes('注册人国家/地区')) {
cell.textContent = '国家/地区';
} else if (cell.textContent.includes('注册人电话')) {
cell.textContent = '电话';
} else if (cell.textContent.includes('注册人 Email')) {
cell.textContent = '邮箱';
} else if (cell.textContent.includes('unsigned')) {
cell.textContent = '未配置';
} else if (cell.textContent.includes('signedDelegation')) {
cell.textContent = '已配置';
} else if (cell.textContent.includes('Whois Server')) {
shouldRemoveRow = true;
} else if (cell.textContent.includes('注册商 URL')) {
shouldRemoveRow = true;
} else if (cell.textContent.includes('Please query the RDDS service of the Registrar of Record identified in this')) {
cell.textContent = '请联系注册商查询';
} else if (cell.textContent.includes('Please query the RDDS service of the Registrar of Record identified in this output for information on how to contact the Registrant, Admin, or Tech contact of the queried domain name.')) {
cell.textContent = '请联系注册商查询';
}else if (cell.textContent.includes('Alibaba Cloud Computing Ltd. d/b/a HiChina (www.net.cn)')) {
cell.textContent = '阿里云万网(www.net.cn)';
} else if (cell.textContent.includes('注册组织')) {
cell.textContent = '机构';
} else if (cell.textContent.includes('MarkMonitor Information Technology (Shanghai) Co., Ltd.')) {
cell.textContent = 'MarkMonitor上海(美国公司)';
} else if (cell.textContent.includes('REDACTED FOR PRIVACY')) {
cell.textContent = '已设置隐私';
} else if (cell.textContent.includes('Redacted for Privacy Purposes')) {
cell.textContent = '已设置隐私';
} else if (cell.textContent.includes('DNSSEC')) {
cell.textContent = 'DNS安全';
} else if (cell.textContent.includes('Redacted | Registry Policy')) {
cell.textContent = '注册表策略隐藏';
} else if (cell.textContent.includes('Not Disclosed - Visit www.internet.ee for webbased WHOIS')) {
cell.textContent = '已设置隐私';
} else if (cell.textContent.includes('West263 International Limited')) {
cell.textContent = '成都西部数码国际站(West263)';
} else if (cell.textContent.includes('Hefei Juming Network Technology Co., Ltd')) {
cell.textContent = '聚名网络科技有限公司';
} else if (cell.textContent.includes('Xin Net Technology Corporation')) {
cell.textContent = '北京新网科技';
} else if (cell.textContent.includes('Chengdu west dimension digital')) {
cell.textContent = '成都西部数码有限公司';
} else if (cell.textContent.includes('Chengdu West Dimension Digital Technology Co., Ltd.')) {
cell.textContent = '成都西维数码科技有限公司';
} else if (cell.textContent.includes('Chengdu West Dimension Digital Technology Company Limited')) {
cell.textContent = '成都西维数码科技有限公司';
} else if (cell.textContent.includes('CHENGDU WEST DIMENSION DIGITAL TECHNOLOGY CO., LTD.')) {
cell.textContent = '成都西维数码科技有限公司';
} else if (cell.textContent.includes('eName Technology Co., Ltd.')) {
cell.textContent = '易名科技有限公司';
} else if (cell.textContent.includes('Domain Science Kutatási Szolgáltató Korlátolt Felelősségű Társaság')) {
cell.textContent = '领域科学(匈牙利)';
} else if (cell.textContent.includes('Electronic and Postal Communications Authority - AKEP')) {
cell.textContent = '电子和邮政通信管理局';
} else if (cell.textContent.includes('Privacy service provided by Withheld for Privacy ehf')) {
cell.textContent = '已设置隐私';
} else if (cell.textContent.includes('HANGZHOUDIANZISHANGWUYANJIUYUAN')) {
cell.textContent = '杭州电子商务研究院';
} else if (cell.textContent.includes('Super Privacy Service LTD c/o Dynadot')) {
cell.textContent = '隐私服务由Dynadot提供';
} else if (cell.textContent.includes('Internet Corporation for Assigned Names and Numbers')) {
cell.textContent = '互联网名称与数字地址分配机构';
} else if (cell.textContent.includes('Conexión al Desarrollo de El Salvador')) {
cell.textContent = '萨尔瓦多发展联盟';
} else if (cell.textContent.includes('Alibaba Cloud Computing (Beijing) Co., Ltd.')) {
cell.textContent = '阿里云计算有限公司';
} else if (cell.textContent.includes('CSL Computer Service Langenbach GmbH d/b/a joker.com')) {
cell.textContent = 'joker.com';
} else if (cell.textContent.includes('Xiamen ChinaSource Internet Service Co., Ltd')) {
cell.textContent = '中资源网络服务有限公司';
} else if (cell.textContent.includes('Shen Zhen Shi Teng Xun Ji Suan Ji Xi Tong You Xian Gong Si')) {
cell.textContent = '深圳市腾讯计算机系统有限公司';
} else if (cell.textContent.includes('Hong Kong (HK)')) {
cell.textContent = '中国香港(Hong Kong)';
} else if (cell.textContent.includes('Netser Group Holdings Limited')) {
cell.textContent = 'Netser集团控股有限公司';
} else if (cell.textContent.includes('Whois Privacy Protection Service, Inc')) {
cell.textContent = 'Whois隐私保护';
} else if (cell.textContent.includes('Redacted | EU Data Subject')) {
cell.textContent = '欧盟隐私保护';
} else if (cell.textContent.includes('Cloudflare, Inc. [Tag = CLOUDFLARE]')) {
cell.textContent = 'Cloudflare';
} else if (cell.textContent.includes('Knock Knock WHOIS Not There, LLC')) {
cell.textContent = 'Whois隐私保护';
} else if (cell.textContent.includes('Dynadot Privacy Service')) {
cell.textContent = 'Dynadot隐私服务';
} else if (cell.textContent.includes('InterCat Ltd')) {
cell.textContent = '域名大盗(InterCat)';
} else if (cell.textContent.includes('王剑飞')) {
cell.textContent = '域名大盗(王剑飞)';
} else if (cell.textContent.includes('Jianfei Wang')) {
cell.textContent = '域名大盗(Jianfei Wang)';
}
});
if (shouldRemoveRow) {
row.remove();
}
});
contentDiv.appendChild(whoisTable); // 插入 WHOIS 信息的表格
} else {
contentDiv.innerHTML = '查询失败,可能的原因有:此域未注册、此后缀没有whois服务器、接口响应过多等。';
}
} catch (error) {
contentDiv.innerHTML = '请求失败,请重试!';
}
}
};
// 点击卡片外部关闭卡片
const closeOnClickOutside = (event) => {
if (!card.contains(event.target)) {
card.style.display = 'none';
document.removeEventListener('click', closeOnClickOutside);
}
};
document.addEventListener('click', closeOnClickOutside);
return { card, loadWhois };
}
// 查找文本节点并处理
function findTextNodesInContainers(containers) {
const textNodes = [];
containers.forEach(container => {
// 如果容器本身是 <pre>,直接跳过
if (container.matches('pre') || container.closest('pre')) {
return;
}
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null, false);
let node;
while (node = walker.nextNode()) {
// 如果文本节点的父节点是 <pre>,跳过
if (!node.parentNode.closest('pre')) {
textNodes.push(node);
}
}
});
return textNodes;
}
// 验证域名是否有效
function isValidDomain(domain, validTLDs) {
const domainParts = domain.split('.');
const tld = domainParts[domainParts.length - 1];
return validTLDs.has(tld.toLowerCase());
}
// 添加问号图标并绑定点击事件
async function addQuestionMarkToDomains(textNode, validTLDs) {
const text = textNode.nodeValue;
// 修改正则表达式以匹配所有类型的URL
const urlRegex = /\b(?:https?:\/\/)?(?:[a-z0-9-]+\.)+[a-z0-9]{2,6}(?:\/[^\s]*)?\b/gi;
const matches = [...text.matchAll(urlRegex)];
if (matches.length) {
const span = document.createElement('span');
const parts = [];
let lastIndex = 0;
matches.forEach(match => {
parts.push(text.slice(lastIndex, match.index)); // 插入前半部分
const url = match[0];
// 提取域名部分进行验证
const domain = url.split('/')[2] || url.split('/')[0]; // 处理不带协议的情况
// 验证域名是否有效
if (isValidDomain(domain, validTLDs)) {
const parentAnchor = textNode.parentNode.closest('a');
if (parentAnchor) {
// 如果URL在超链接中,不替换内容,而是在超链接外部插入SVG
const questionMarkSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
questionMarkSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
questionMarkSvg.setAttribute("width", "16");
questionMarkSvg.setAttribute("height", "16");
questionMarkSvg.setAttribute("fill", "currentColor");
questionMarkSvg.setAttribute("class", "bi bi-search");
questionMarkSvg.setAttribute("viewBox", "0 0 16 16");
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0");
questionMarkSvg.appendChild(path);
// 设置SVG样式
questionMarkSvg.style.marginLeft = '4px';
questionMarkSvg.style.cursor = 'pointer';
questionMarkSvg.style.color = 'blue';
questionMarkSvg.style.userSelect = 'none';
questionMarkSvg.style.width = '10px';
questionMarkSvg.style.height = '10px';
questionMarkSvg.style.display = 'inline-block';
questionMarkSvg.style.verticalAlign = '-0.125em';
questionMarkSvg.style.fill = 'currentcolor';
// 如果SVG不存在,则插入到超链接后方
if (!parentAnchor.nextSibling || parentAnchor.nextSibling.tagName !== 'svg') {
parentAnchor.insertAdjacentElement('afterend', questionMarkSvg);
}
// 为SVG绑定点击事件
questionMarkSvg.addEventListener('click', (event) => {
event.stopPropagation();
// 创建并显示 WHOIS 卡片
const { card, loadWhois } = createHoverCard(domain);
document.body.appendChild(card);
card.style.display = 'block';
// 获取目标元素的位置和尺寸
const targetRect = event.target.getBoundingClientRect();
// 获取卡片的尺寸
const cardRect = card.getBoundingClientRect();
// 计算卡片的初始位置
let left = targetRect.right + 10;
let top = targetRect.bottom + 10;
// 获取窗口的宽度和高度
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// 检查右侧边界
if (left + cardRect.width > windowWidth) {
left = windowWidth - cardRect.width - 10;
}
// 检查底部边界
if (top + cardRect.height > windowHeight) {
top = windowHeight - cardRect.height - 10;
}
// 设置卡片的位置
card.style.left = left + 'px';
card.style.top = `${event.pageY + 10}px`;
loadWhois();
});
} else {
// 如果URL不在超链接中,在URL末尾插入SVG
const spanUrl = document.createElement('span');
spanUrl.textContent = url;
const questionMarkSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
questionMarkSvg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
questionMarkSvg.setAttribute("width", "16");
questionMarkSvg.setAttribute("height", "16");
questionMarkSvg.setAttribute("fill", "currentColor");
questionMarkSvg.setAttribute("class", "bi bi-search");
questionMarkSvg.setAttribute("viewBox", "0 0 16 16");
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", "M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0");
questionMarkSvg.appendChild(path);
// 设置SVG样式
questionMarkSvg.style.marginLeft = '4px';
questionMarkSvg.style.cursor = 'pointer';
questionMarkSvg.style.color = 'blue';
questionMarkSvg.style.userSelect = 'none';
questionMarkSvg.style.width = '10px';
questionMarkSvg.style.height = '10px';
questionMarkSvg.style.display = 'inline-block';
questionMarkSvg.style.verticalAlign = '0.15em';
questionMarkSvg.style.fill = 'currentcolor';
questionMarkSvg.addEventListener('click', (event) => {
event.stopPropagation();
// 创建并显示 WHOIS 卡片
const { card, loadWhois } = createHoverCard(domain);
document.body.appendChild(card);
card.style.display = 'block';
// 获取目标元素的位置和尺寸
const targetRect = event.target.getBoundingClientRect();
// 获取卡片的尺寸
const cardRect = card.getBoundingClientRect();
// 计算卡片的初始位置
let left = targetRect.right + 10;
let top = targetRect.bottom + 10;
// 获取窗口的宽度和高度
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// 检查右侧边界
if (left + cardRect.width > windowWidth) {
left = windowWidth - cardRect.width - 10;
}
// 检查底部边界
if (top + cardRect.height > windowHeight) {
top = windowHeight - cardRect.height - 10;
}
// 设置卡片的位置
card.style.left = left + 'px';
card.style.top = `${event.pageY + 10}px`;
loadWhois();
});
const containerSpan = document.createElement('span');
containerSpan.appendChild(spanUrl);
containerSpan.appendChild(questionMarkSvg);
parts.push(containerSpan);
}
} else {
parts.push(url); // 如果不是有效域名,保持原样
}
lastIndex = match.index + url.length;
});
parts.push(text.slice(lastIndex));
span.append(...parts);
// 替换原始文本节点
if (!textNode.parentNode.closest('a')) {
textNode.replaceWith(span);
}
}
}
// 获取所有包含文本的容器
const containers = Array.from(document.querySelectorAll('.message.break-all.box-shadow, .message.mt-1.break-all'));
// 下载域名后缀名单
async function fetchValidTLDs() {
try {
const response = await fetch('https://www.dalao.net/lunbo/domain.txt');
const text = await response.text();
const validTLDs = new Set(text.trim().split('\n').map(tld => tld.toLowerCase()));
return validTLDs;
} catch (error) {
console.error('Failed to fetch valid TLDs:', error);
return new Set(); // 返回空集以防后续错误
}
}
// 初始化并处理文本节点
fetchValidTLDs().then(validTLDs => {
const textNodes = findTextNodesInContainers(containers);
textNodes.forEach(textNode => {
addQuestionMarkToDomains(textNode, validTLDs);
});
});
})();
如您需要添加到网站,直接复制以上脚本的 js 部分,放到网站 footer 部分,修改
const containers = Array.from(document.querySelectorAll('.message.break-all.box-shadow, .message.mt-1.break-all'));
此处的容器名称为你网站的容器名,卡片只会在此处填的容器内生效。
这里使用了 who.0du.ru 的 whois 功能,所以请求响应情况根据对方速度而定,你可以自己部署一个:
https://github.com/zmh-program/next-whois-ui
本站资源均为作者提供和网友推荐收集整理而来,仅供学习和研究使用,请在下载后24小时内删除,谢谢合作!
THE END