From 47ff8528c3f0495abc08b6dab93686b8312f0a89 Mon Sep 17 00:00:00 2001 From: Mrx <18278715334@163.com> Date: Sat, 28 Feb 2026 17:41:25 +0800 Subject: [PATCH] f --- public/seo-templates/agent.html | 80 +++++++++++ public/seo-templates/example.html | 80 +++++++++++ public/seo-templates/help-guide.html | 80 +++++++++++ public/seo-templates/help.html | 80 +++++++++++ public/seo-templates/index.html | 80 +++++++++++ public/seo-templates/service.html | 80 +++++++++++ server/crawler-detector.js | 170 ++++++++++++++++++++++++ server/generate-seo-templates.cjs | 191 +++++++++++++++++++++++++++ server/middleware.js | 170 ++++++++++++++++++++++++ server/package.json | 27 ++++ 10 files changed, 1038 insertions(+) create mode 100644 public/seo-templates/agent.html create mode 100644 public/seo-templates/example.html create mode 100644 public/seo-templates/help-guide.html create mode 100644 public/seo-templates/help.html create mode 100644 public/seo-templates/index.html create mode 100644 public/seo-templates/service.html create mode 100644 server/crawler-detector.js create mode 100644 server/generate-seo-templates.cjs create mode 100644 server/middleware.js create mode 100644 server/package.json diff --git a/public/seo-templates/agent.html b/public/seo-templates/agent.html new file mode 100644 index 0000000..f19cbec --- /dev/null +++ b/public/seo-templates/agent.html @@ -0,0 +1,80 @@ + + + + + + + + 天远助手代理 - 免费开通代理权限 | 大数据风险报告代理 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

天远助手代理 - 免费开通代理权限 | 大数据风险报告代理

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手代理平台,免费开通代理权限,享受大数据风险报告查询服务代理收益。专业的大数据风险报告、婚姻查询、个人信用评估等服务的代理合作。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/public/seo-templates/example.html b/public/seo-templates/example.html new file mode 100644 index 0000000..157ccdc --- /dev/null +++ b/public/seo-templates/example.html @@ -0,0 +1,80 @@ + + + + + + + + 示例报告 - 天远助手报告展示 | 大数据风险报告样例 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

示例报告 - 天远助手报告展示 | 大数据风险报告样例

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手示例报告展示,包含大数据风险报告、婚姻状况查询、个人信用评估等服务的报告样例,让用户了解报告内容和格式。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/public/seo-templates/help-guide.html b/public/seo-templates/help-guide.html new file mode 100644 index 0000000..4f52bb3 --- /dev/null +++ b/public/seo-templates/help-guide.html @@ -0,0 +1,80 @@ + + + + + + + + 使用指南 - 天远助手操作教程 | 功能说明 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

使用指南 - 天远助手操作教程 | 功能说明

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手详细使用指南,包含各功能模块的操作教程、功能说明、注意事项等,让用户快速上手使用。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/public/seo-templates/help.html b/public/seo-templates/help.html new file mode 100644 index 0000000..eddace1 --- /dev/null +++ b/public/seo-templates/help.html @@ -0,0 +1,80 @@ + + + + + + + + 帮助中心 - 天远助手使用指南 | 常见问题解答 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

帮助中心 - 天远助手使用指南 | 常见问题解答

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手帮助中心,提供详细的使用指南、常见问题解答、操作教程等,帮助用户更好地使用大数据风险报告查询服务。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/public/seo-templates/index.html b/public/seo-templates/index.html new file mode 100644 index 0000000..87a83da --- /dev/null +++ b/public/seo-templates/index.html @@ -0,0 +1,80 @@ + + + + + + + + 天远助手|大数据风险报告查询与代理平台,支持个人和企业多场景风控应用 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

天远助手|大数据风险报告查询与代理平台,支持个人和企业多场景风控应用

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/public/seo-templates/service.html b/public/seo-templates/service.html new file mode 100644 index 0000000..cad8cc0 --- /dev/null +++ b/public/seo-templates/service.html @@ -0,0 +1,80 @@ + + + + + + + + 客服中心 - 天远助手在线客服 | 技术支持 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

客服中心 - 天远助手在线客服 | 技术支持

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

天远助手客服中心,提供在线客服支持、技术咨询、问题反馈等服务,确保用户获得及时有效的帮助。

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ + \ No newline at end of file diff --git a/server/crawler-detector.js b/server/crawler-detector.js new file mode 100644 index 0000000..b49fec1 --- /dev/null +++ b/server/crawler-detector.js @@ -0,0 +1,170 @@ +/** + * 爬虫检测模块 + * 用于识别搜索引擎爬虫和社交媒体爬虫 + */ + +class CrawlerDetector { + constructor() { + // 常见搜索引擎爬虫User-Agent列表 + this.crawlerPatterns = [ + // 百度爬虫 + 'baiduspider', + 'baiduspider-mobile', + 'baiduspider-image', + 'baiduspider-video', + 'baiduspider-news', + 'baiduboxapp', + + // Google爬虫 + 'googlebot', + 'googlebot-image', + 'googlebot-news', + 'googlebot-mobile', + 'googlebot-video', + 'google-web-snippet', + + // 360搜索 + '360spider', + 'soha-agent', + 'haosouspider', + + // 搜狗搜索 + 'sogou spider', + 'sogou news spider', + 'sogou orion spider', + 'sogou-blog', + + // 必应 + 'bingbot', + 'msnbot', + + // 雅虎 + 'slurp', + + // 搜搜 + 'sosospider', + 'sosoimagespider', + + // 有道 + 'youdaobot', + 'yodaobot', + + // 头条搜索 + 'bytedance-spider', + 'toutiaospider', + + // 社交媒体爬虫 + 'facebookexternalhit', + 'facebookcatalog', + 'twitterbot', + 'linkedinbot', + 'whatsapp', + 'telegrambot', + 'viber', + 'line', + + // 其他常见爬虫 + 'applebot', + 'semrushbot', + 'ahrefsbot', + 'mj12bot', + 'dotbot', + 'crawler', + 'spider', + 'bot' + ] + + // 需要检测的头部字段 + this.crawlerHeaders = ['x-bot', 'x-crawler', 'x-forwarded-for'] + } + + /** + * 检测请求是否来自爬虫 + * @param {Object} req - HTTP请求对象 + * @returns {Boolean} 是否为爬虫 + */ + isCrawler(req) { + const userAgent = req.headers['user-agent']?.toLowerCase() || '' + const headers = req.headers + + // 1. 通过User-Agent检测 + if (this.checkUserAgent(userAgent)) { + console.log(`[CrawlerDetector] 检测到爬虫 UA: ${userAgent}`) + return true + } + + // 2. 通过特定头部检测 + if (this.checkHeaders(headers)) { + console.log(`[CrawlerDetector] 检测到爬虫 Headers`) + return true + } + + // 3. 通过IP地址检测(可选) + // if (this.checkIP(req.connection.remoteAddress)) { + // return true + // } + + return false + } + + /** + * 检查User-Agent + * @param {String} userAgent + * @returns {Boolean} + */ + checkUserAgent(userAgent) { + if (!userAgent) return false + + return this.crawlerPatterns.some(pattern => { + return userAgent.includes(pattern.toLowerCase()) + }) + } + + /** + * 检查请求头 + * @param {Object} headers + * @returns {Boolean} + */ + checkHeaders(headers) { + for (const header of this.crawlerHeaders) { + const headerValue = headers[header]?.toLowerCase() + if (headerValue && (headerValue.includes('bot') || headerValue.includes('crawler'))) { + return true + } + } + return false + } + + /** + * 检查IP地址是否为已知爬虫IP + * @param {String} ip + * @returns {Boolean} + */ + checkIP(ip) { + // 这里可以添加已知爬虫IP段的检测 + // 需要定期更新爬虫IP列表 + return false + } + + /** + * 获取爬虫类型 + * @param {String} userAgent + * @returns {String} 爬虫类型 + */ + getCrawlerType(userAgent) { + const ua = userAgent.toLowerCase() + + if (ua.includes('baiduspider')) return 'baidu' + if (ua.includes('googlebot')) return 'google' + if (ua.includes('bingbot') || ua.includes('msnbot')) return 'bing' + if (ua.includes('360spider')) return '360' + if (ua.includes('sogou spider')) return 'sogou' + if (ua.includes('facebookexternalhit')) return 'facebook' + if (ua.includes('twitterbot')) return 'twitter' + if (ua.includes('linkedinbot')) return 'linkedin' + + return 'unknown' + } +} + +module.exports = CrawlerDetector diff --git a/server/generate-seo-templates.cjs b/server/generate-seo-templates.cjs new file mode 100644 index 0000000..5a81854 --- /dev/null +++ b/server/generate-seo-templates.cjs @@ -0,0 +1,191 @@ +/** + * SEO模板生成器 + * 根据 useSEO.js 的页面配置自动生成静态 HTML 模板,供爬虫访问时返回 + * 配置与 src/composables/useSEO.js 保持一致 + * + * 多站点:通过环境变量 SEO_BASE_URL 指定 canonical/og:url 域名后生成 + * 例:SEO_BASE_URL=https://www.tianyuandb.com node generate-seo-templates.cjs + */ + +const fs = require('fs') +const path = require('path') + +const BASE_URL = process.env.SEO_BASE_URL || 'https://www.zhinengcha.cn' + +// 页面 SEO 配置(与 useSEO.js 的 routeConfigs 保持一致) +const pageSEOConfigs = { + 'index.html': { + title: '天远助手|大数据风险报告查询与代理平台,支持个人和企业多场景风控应用', + description: '天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。', + keywords: '大数据风险报告查询、大数据风险评估、大数据分析报告、个人大数据风险查询、小微企业风险、贷前风险背调、代理管理平台、免费开通代理、风险管控平台、信用风险分析、企业风险报告、贷前信用审核、失信人名单查询、被执行人信息、信用黑名单查询', + url: BASE_URL + }, + 'agent.html': { + title: '天远助手代理 - 免费开通代理权限 | 大数据风险报告代理', + description: '天远助手代理平台,免费开通代理权限,享受大数据风险报告查询服务代理收益。专业的大数据风险报告、婚姻查询、个人信用评估等服务的代理合作。', + keywords: '天远助手代理, 免费代理, 大数据风险报告代理, 代理权限, 代理收益', + url: `${BASE_URL}/agent` + }, + 'help.html': { + title: '帮助中心 - 天远助手使用指南 | 常见问题解答', + description: '天远助手帮助中心,提供详细的使用指南、常见问题解答、操作教程等,帮助用户更好地使用大数据风险报告查询服务。', + keywords: '天远助手帮助, 使用指南, 常见问题, 操作教程, 客服支持', + url: `${BASE_URL}/help` + }, + 'help-guide.html': { + title: '使用指南 - 天远助手操作教程 | 功能说明', + description: '天远助手详细使用指南,包含各功能模块的操作教程、功能说明、注意事项等,让用户快速上手使用。', + keywords: '使用指南, 操作教程, 功能说明, 快速上手, 天远助手教程', + url: `${BASE_URL}/help/guide` + }, + 'example.html': { + title: '示例报告 - 天远助手报告展示 | 大数据风险报告样例', + description: '天远助手示例报告展示,包含大数据风险报告、婚姻状况查询、个人信用评估等服务的报告样例,让用户了解报告内容和格式。', + keywords: '示例报告, 报告展示, 报告样例, 大数据风险报告, 婚姻查询报告', + url: `${BASE_URL}/example` + }, + 'service.html': { + title: '客服中心 - 天远助手在线客服 | 技术支持', + description: '天远助手客服中心,提供在线客服支持、技术咨询、问题反馈等服务,确保用户获得及时有效的帮助。', + keywords: '客服中心, 在线客服, 技术支持, 问题反馈, 天远助手客服', + url: `${BASE_URL}/service` + } +} + +/** + * 规范化文案:统一为中文标点,避免乱码 + */ +function normalizeText(str) { + if (typeof str !== 'string') return str + return str + .replace(/\uFFFD/g, '') + .replace(/。/g, '。') + .replace(/、/g, '、') +} + +/** + * 转义 HTML 属性值 + */ +function escapeAttr(str) { + if (typeof str !== 'string') return '' + return str + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>') +} + +/** + * 生成单页 HTML 模板 + */ +function generateHTMLTemplate(config) { + const title = normalizeText(config.title) + const description = normalizeText(config.description) + const keywords = normalizeText(config.keywords) + const structuredData = { + '@context': 'https://schema.org', + '@type': 'WebPage', + name: title, + description: description, + url: config.url, + mainEntity: { + '@type': 'Organization', + name: '天远助手', + url: 'https://www.zhinengcha.cn/', + description: '专业大数据风险报告查询与代理平台,支持个人和企业多场景风控应用' + } + } + + return ` + + + + + + + ${escapeAttr(title)} + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

${escapeAttr(title)}

+
+

正在跳转到完整版网站...

+

如果浏览器没有自动跳转,请 点击这里

+
+

${escapeAttr(description)}

+
+

关于天远助手

+

天远助手,专业大数据风险报告查询与代理平台,支持个人信用查询、小微企业风控、贷前风险背调等多场景报告应用,免费开通代理权限,助力高效识别信用与风险。

+
+
+

核心服务

+ +
+
+ +` +} + +function main() { + const outputDir = path.join(__dirname, '../public/seo-templates') + + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }) + console.log(`✓ 创建模板目录: ${outputDir}`) + } + + let successCount = 0 + Object.entries(pageSEOConfigs).forEach(([filename, config]) => { + const htmlContent = generateHTMLTemplate(config) + const filePath = path.join(outputDir, filename) + fs.writeFileSync(filePath, htmlContent, 'utf-8') + console.log(`✓ 生成模板: ${filename}`) + successCount++ + }) + + console.log(`\n✓ 成功生成 ${successCount} 个 SEO 模板文件`) + console.log(`📁 模板目录: ${outputDir}`) + console.log(`💡 配置与 useSEO.js 一致,当前域名: ${BASE_URL}`) +} + +main() diff --git a/server/middleware.js b/server/middleware.js new file mode 100644 index 0000000..aa9f2d8 --- /dev/null +++ b/server/middleware.js @@ -0,0 +1,170 @@ +/** + * SEO中间件 + * 用于在Node.js服务器中检测爬虫并返回静态HTML + */ + +const fs = require('fs') +const path = require('path') +const CrawlerDetector = require('./crawler-detector') + +class SEOMiddleware { + constructor(options = {}) { + this.detector = new CrawlerDetector() + this.templateDir = options.templateDir || path.join(__dirname, '../public/seo-templates') + this.defaultTemplate = options.defaultTemplate || 'index.html' + this.fallbackToSPA = options.fallbackToSPA !== false + this.debug = options.debug || false + + // 路由到模板的映射(与 useSEO.js 的 routeConfigs 保持一致) + this.routeTemplateMap = { + '/': 'index.html', + '/agent': 'agent.html', + '/help/guide': 'help-guide.html', + '/help': 'help.html', + '/example': 'example.html', + '/service': 'service.html' + } + + // 初始化模板缓存 + this.templateCache = new Map() + this.cacheTemplates() + } + + /** + * 缓存所有模板文件 + */ + cacheTemplates() { + try { + if (!fs.existsSync(this.templateDir)) { + console.warn(`[SEOMiddleware] 模板目录不存在: ${this.templateDir}`) + return + } + + const files = fs.readdirSync(this.templateDir) + files.forEach(file => { + const filePath = path.join(this.templateDir, file) + if (fs.statSync(filePath).isFile()) { + this.templateCache.set(file, fs.readFileSync(filePath, 'utf-8')) + if (this.debug) { + console.log(`[SEOMiddleware] 已缓存模板: ${file}`) + } + } + }) + + console.log(`[SEOMiddleware] 已缓存 ${this.templateCache.size} 个模板文件`) + } catch (error) { + console.error('[SEOMiddleware] 缓存模板失败:', error) + } + } + + /** + * 获取对应的模板文件名 + * @param {String} path - 请求路径 + * @returns {String} 模板文件名 + */ + getTemplatePath(requestPath) { + // 完全匹配 + if (this.routeTemplateMap[requestPath]) { + return this.routeTemplateMap[requestPath] + } + + // 模糊匹配(处理动态路由) + const matchedKey = Object.keys(this.routeTemplateMap).find(route => { + return requestPath.startsWith(route) + }) + + return matchedKey ? this.routeTemplateMap[matchedKey] : this.defaultTemplate + } + + /** + * 获取模板内容 + * @param {String} templateName - 模板文件名 + * @returns {String|null} 模板内容 + */ + getTemplate(templateName) { + // 首先尝试缓存 + let content = this.templateCache.get(templateName) + + // 如果缓存中没有,尝试从磁盘读取 + if (!content) { + try { + const filePath = path.join(this.templateDir, templateName) + if (fs.existsSync(filePath)) { + content = fs.readFileSync(filePath, 'utf-8') + this.templateCache.set(templateName, content) + } + } catch (error) { + console.error(`[SEOMiddleware] 读取模板失败: ${templateName}`, error) + } + } + + return content || null + } + + /** + * Express中间件 + */ + express() { + return (req, res, next) => { + // 检测是否为爬虫 + if (this.detector.isCrawler(req)) { + const templateName = this.getTemplatePath(req.path) + const template = this.getTemplate(templateName) + + if (template) { + // 设置响应头 + res.setHeader('Content-Type', 'text/html; charset=utf-8') + res.setHeader('X-SEOMiddleware', 'prerendered') + + // 返回静态HTML + if (this.debug) { + console.log(`[SEOMiddleware] 返回SEO模板: ${templateName} for ${req.path}`) + } + + return res.send(template) + } + } + + // 不是爬虫或模板不存在,继续处理SPA + next() + } + } + + /** + * Koa中间件 + */ + koa() { + return async (ctx, next) => { + // 检测是否为爬虫 + if (this.detector.isCrawler(ctx.req)) { + const templateName = this.getTemplatePath(ctx.path) + const template = this.getTemplate(templateName) + + if (template) { + ctx.type = 'text/html; charset=utf-8' + ctx.set('X-SEOMiddleware', 'prerendered') + + if (this.debug) { + console.log(`[SEOMiddleware] 返回SEO模板: ${templateName} for ${ctx.path}`) + } + + ctx.body = template + return + } + } + + await next() + } + } + + /** + * 重新加载模板缓存 + */ + reloadCache() { + this.templateCache.clear() + this.cacheTemplates() + console.log('[SEOMiddleware] 模板缓存已重新加载') + } +} + +module.exports = SEOMiddleware diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..dbb7c3e --- /dev/null +++ b/server/package.json @@ -0,0 +1,27 @@ +{ + "name": "tydata-seo-server", + "version": "1.0.0", + "description": "SPA SEO 优化 - 爬虫检测与静态 HTML 回退,与 useSEO.js 同步", + "main": "server-example-express.js", + "scripts": { + "start": "node server-example-express.js", + "dev": "node server-example-express.js", + "generate": "node generate-seo-templates.cjs", + "test": "node test-seo.js", + "test:crawler": "node test-crawler-detection.js" + }, + "keywords": [ + "seo", + "crawler", + "spa", + "prerender" + ], + "license": "MIT", + "dependencies": { + "express": "^4.18.2", + "compression": "^1.7.4" + }, + "devDependencies": { + "nodemon": "^3.0.1" + } +}