export interface SearchResultItem { title: string; url: string; snippet: string; displayUrl?: string; } export interface ParsedSearchResults { items: SearchResultItem[]; totalResults?: string; engine: string; } export function parseBingResults(html: string, maxResults: number, snippetLength: number): ParsedSearchResults { const items: SearchResultItem[] = []; // Bing 结果项在
  • 中 const itemRegex = /]*class="[^"]*b_algo[^"]*"[^>]*>([\s\S]*?)<\/li>/gi; let match: RegExpExecArray | null; while ((match = itemRegex.exec(html)) !== null && items.length < maxResults) { const block = match[1]; // 标题和链接在

    标题

    const linkMatch = block.match(/]*href="(https?:\/\/[^"]+)"[^>]*>([\s\S]*?)<\/a>/i); if (!linkMatch) continue; const url = linkMatch[1]; const title = stripTags(linkMatch[2]).trim(); if (!title || !url) continue; // 摘要在

    const snippetMatch = block.match(/]*class="[^"]*b_lineclamp[^"]*"[^>]*>([\s\S]*?)<\/p>/i) || block.match(/]*class="[^"]*b_caption[^"]*"[^>]*>[\s\S]*?]*>([\s\S]*?)<\/p>/i); const snippet = snippetMatch ? truncate(stripTags(snippetMatch[1]).trim(), snippetLength) : ""; // 显示URL const displayMatch = block.match(/]*>([\s\S]*?)<\/cite>/i); const displayUrl = displayMatch ? stripTags(displayMatch[1]).trim() : undefined; items.push({ title, url, snippet, displayUrl }); } return { items, engine: "bing" }; } export function parseBaiduResults(html: string, maxResults: number, snippetLength: number): ParsedSearchResults { const items: SearchResultItem[] = []; // 百度结果项在

    const itemRegex = /]*class="[^"]*(?:result|c-container)[^"]*"[^>]*>([\s\S]*?)(?=]*class="[^"]*(?:result|c-container)|$)/gi; let match: RegExpExecArray | null; while ((match = itemRegex.exec(html)) !== null && items.length < maxResults) { const block = match[1]; const linkMatch = block.match(/]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i); if (!linkMatch) continue; const url = linkMatch[1]; const title = stripTags(linkMatch[2]).trim(); if (!title || !url) continue; const snippetMatch = block.match(/]*class="[^"]*content-right[^"]*"[^>]*>([\s\S]*?)<\/span>/i) || block.match(/]*class="[^"]*c-abstract[^"]*"[^>]*>([\s\S]*?)<\/div>/i) || block.match(/]*>([\s\S]*?)<\/span>/i); const snippet = snippetMatch ? truncate(stripTags(snippetMatch[1]).trim(), snippetLength) : ""; items.push({ title, url, snippet }); } return { items, engine: "baidu" }; } export function parseGoogleResults(html: string, maxResults: number, snippetLength: number): ParsedSearchResults { const items: SearchResultItem[] = []; // Google 结果项在
    中 const itemRegex = /]*class="[^"]*\bg\b[^"]*"[^>]*>([\s\S]*?)<\/div>\s*(?=]*class="[^"]*\bg\b|]*id="foot"|$)/gi; let match: RegExpExecArray | null; while ((match = itemRegex.exec(html)) !== null && items.length < maxResults) { const block = match[1]; const linkMatch = block.match(/]*href="\/url\?q=(https?:\/\/[^&"]+)&[^"]*"[^>]*>([\s\S]*?)<\/a>/i) || block.match(/]*href="(https?:\/\/[^"]+)"[^>]*>([\s\S]*?)<\/a>/i); if (!linkMatch) continue; const url = decodeURIComponent(linkMatch[1]); const title = stripTags(linkMatch[2]).trim(); if (!title || !url) continue; const snippetMatch = block.match(/]*>([\s\S]*?)<\/span>/i); const snippet = snippetMatch ? truncate(stripTags(snippetMatch[1]).trim(), snippetLength) : ""; items.push({ title, url, snippet }); } return { items, engine: "google" }; } function stripTags(html: string): string { return html .replace(/<[^>]+>/g, "") .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, '"') .replace(/'/g, "'") .replace(/ /g, " ") .replace(/\s+/g, " ") .trim(); } function truncate(str: string, maxLen: number): string { if (str.length <= maxLen) return str; return str.slice(0, maxLen) + "..."; }