Back to skills
extension
Category: OtherNo API key required

web-search-screenshot-ppt

通过关键词进行网络搜索,批量抓取网页并截图,最终汇总成专业PPT演示文稿。触发词:搜索并截图、做调研报告、竞品截图分析、网页截图汇总、搜索结果截图、竞品调研PPT、研究报告生成、网络调研截图

personAuthor: user_5b12983fhubcommunity

网页搜索截图PPT Skill v1.0

版本信息

  • v1.0: 初始版本,支持关键词搜索、网页截图、PPT生成

功能概述

本Skill通过关键词自动完成:网络搜索 → 网页抓取 → 智能截图 → PPT汇总的完整流程。

| 功能模块 | 说明 | |---------|------| | 关键词搜索 | 支持多搜索引擎(百度、Google、微信文章) | | 网页抓取 | 批量抓取目标页面内容 | | 智能截图 | 全页面或指定区域截图 | | 内容分析 | AI智能提取关键信息 | | PPT生成 | 一键生成结构化演示文稿 |


使用方法

基本语法

@skill://web-search-screenshot-ppt [关键词] [选项]

示例

| 用户输入 | 效果 | |---------|------| | @skill://web-search-screenshot-ppt 人工智能发展趋势 | 搜索并生成AI趋势报告PPT | | @skill://web-search-screenshot-ppt 竞品分析 智能家居 | 抓取竞品页面截图生成PPT | | 帮我搜索"新能源汽车",做调研报告PPT | 同上,触发技能 | | 搜索竞品官网截图并汇总 | 竞品分析模式 |


执行流程

┌─────────────────────────────────────────────────────────────────────┐
│                      网页搜索截图PPT工作流                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐      │
│  │ 1.搜索   │───▶│ 2.抓取   │───▶│ 3.截图   │───▶│ 4.分析   │      │
│  └──────────┘    └──────────┘    └──────────┘    └──────────┘      │
│       │              │              │              │               │
│       ▼              ▼              ▼              ▼               │
│  多引擎搜索      内容提取        全页面截图      关键信息提取        │
│                                                                     │
│  ┌──────────┐                                                       │
│  │ 5.汇总PPT│                                                       │
│  └──────────┘                                                       │
│       │                                                             │
│       ▼                                                             │
│  结构化报告 ──▶ 专业演示文稿                                         │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

核心模块

1. 搜索引擎模块

// 搜索引擎配置
const searchEngines = {
    baidu: {
        name: '百度搜索',
        baseUrl: 'https://www.baidu.com/s',
        params: { wd: 'keyword', rn: 10 },
        extractFn: extractBaiduResults
    },
    google: {
        name: 'Google搜索',
        baseUrl: 'https://www.google.com/search',
        params: { q: 'keyword', num: 10 },
        extractFn: extractGoogleResults
    },
    wechat: {
        name: '微信文章',
        baseUrl: 'https://weixin.sogou.com/weixin',
        params: { type: 2, query: 'keyword' },
        extractFn: extractWeChatResults
    }
};

// 搜索执行函数
async function searchByKeyword(keyword, options = {}) {
    const {
        engine = 'baidu',
        limit = 10,
        days = 30  // 只取近N天结果
    } = options;

    const searcher = searchEngines[engine];
    const url = buildSearchUrl(searcher, keyword, limit);
    
    // 获取搜索结果
    const html = await fetchPage(url);
    const results = searcher.extractFn(html);
    
    // 过滤时间范围
    const filteredResults = filterByDate(results, days);
    
    return {
        keyword,
        engine: searcher.name,
        count: filteredResults.length,
        results: filteredResults
    };
}

2. 网页抓取模块

// 网页内容提取
async function fetchAndExtract(url, options = {}) {
    const {
        extractTitle = true,
        extractContent = true,
        extractImages = true,
        extractMeta = true
    } = options;

    // 获取页面HTML
    const html = await fetchPage(url, {
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }
    });

    // 使用cheerio解析
    const $ = cheerio.load(html);

    const result = {
        url,
        title: extractTitle ? $('title').text().trim() : null,
        meta: extractMeta ? {
            description: $('meta[name="description"]').attr('content'),
            keywords: $('meta[name="keywords"]').attr('content'),
            author: $('meta[name="author"]').attr('content')
        } : null,
        content: extractContent ? extractMainContent($) : null,
        images: extractImages ? extractImages($, url) : [],
        links: extractLinks($, url),
        timestamp: new Date().toISOString()
    };

    return result;
}

// 提取正文内容(智能去除广告和导航)
function extractMainContent($) {
    // 尝试多种选择器
    const selectors = [
        'article',
        '[class*="content"]',
        '[class*="article"]',
        '[class*="post"]',
        'main',
        '.main-content'
    ];

    for (const selector of selectors) {
        const content = $(selector);
        if (content.length > 0) {
            return cleanText(content.text());
        }
    }

    // 降级方案:取body前1000字符
    return cleanText($('body').text()).substring(0, 1000);
}

// 清理文本
function cleanText(text) {
    return text
        .replace(/\s+/g, ' ')
        .replace(/[\n\r\t]/g, '')
        .trim();
}

3. 截图模块

// 浏览器截图配置
const screenshotConfig = {
    viewport: {
        width: 1920,
        height: 1080,
        deviceScaleFactor: 2  // 2x分辨率
    },
    fullPage: true,  // 截取整页
    format: 'png',
    quality: 90
};

// 批量截图函数
async function captureScreenshots(urls, options = {}) {
    const {
        outputDir = './screenshots',
        delay = 1000,  // 每个页面延迟
        waitForSelector = null  // 等待特定元素加载
    } = options;

    const results = [];

    for (const url of urls) {
        console.log(`📸 正在截图: ${url}`);

        try {
            // 导航到页面
            await page.goto(url, { 
                waitUntil: 'networkidle2',
                timeout: 30000 
            });

            // 等待特定元素(可选)
            if (waitForSelector) {
                await page.waitForSelector(waitForSelector, { timeout: 5000 }).catch(() => {});
            }

            // 额外延迟确保渲染完成
            await page.waitForTimeout(delay);

            // 截图
            const filename = sanitizeFilename(url) + '.png';
            const filepath = path.join(outputDir, filename);

            await page.screenshot({
                path: filepath,
                fullPage: screenshotConfig.fullPage,
                type: screenshotConfig.format,
                quality: screenshotConfig.quality
            });

            results.push({
                url,
                success: true,
                filepath,
                timestamp: new Date().toISOString()
            });

        } catch (error) {
            console.error(`❌ 截图失败: ${url}`, error.message);
            results.push({
                url,
                success: false,
                error: error.message
            });
        }
    }

    return results;
}

// 文件名安全处理
function sanitizeFilename(url) {
    return url
        .replace(/https?:\/\//, '')
        .replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_')
        .substring(0, 100);
}

4. 内容分析模块

// 内容分析配置
const analysisConfig = {
    // 提取的关键信息
    extractFields: [
        'company_name',      // 公司/品牌名
        'product_features',  // 产品特点
        'pricing',          // 价格信息
        'highlights',       // 亮点/优势
        'contact_info',     // 联系方式
        'news_events'       // 最新动态
    ],
    
    // 分析规则
    rules: {
        maxSentences: 5,     // 每个字段最多5句
        minWordCount: 10    // 最少10个词
    }
};

// 批量分析内容
async function analyzeContents(items, options = {}) {
    const analysisResults = [];

    for (const item of items) {
        console.log(`🔍 正在分析: ${item.title || item.url}`);

        const analysis = {
            url: item.url,
            title: item.title,
            timestamp: new Date().toISOString()
        };

        if (item.content) {
            // 使用LLM分析内容
            analysis.summary = await summarizeContent(item.content, options);
            analysis.keywords = await extractKeywords(item.content, options);
            analysis.categories = await classifyContent(item.content, options);
        }

        // 提取结构化信息
        analysis.structuredData = extractStructuredData(item, analysisConfig);

        analysisResults.push(analysis);
    }

    return analysisResults;
}

// 内容摘要
async function summarizeContent(content, options = {}) {
    const { maxLength = 200 } = options;
    
    // 使用AI提取摘要
    const prompt = `请用50字概括以下内容的核心要点:\n\n${content.substring(0, 2000)}`;
    
    // 调用LLM接口
    const response = await callLLM({
        model: 'gpt-4',
        messages: [{ role: 'user', content: prompt }]
    });

    return response.content;
}

// 关键词提取
async function extractKeywords(content, options = {}) {
    const { topN = 10 } = options;
    
    // 使用NLP提取关键词
    const response = await callLLM({
        model: 'gpt-4',
        messages: [{
            role: 'user', 
            content: `从以下内容中提取${topN}个最重要的关键词,用逗号分隔:\n\n${content.substring(0, 1500)}`
        }]
    });

    return response.content.split('、').map(k => k.trim()).filter(Boolean);
}

5. PPT生成模块

// PPT生成器
class PPTGenerator {
    constructor(options = {}) {
        this.pptx = new PptxGenJS();
        this.options = {
            title: options.title || '调研报告',
            author: options.author || 'AI Assistant',
            company: options.company || '',
            theme: options.theme || 'professional',
            ...options
        };

        // 配置主题
        this.setupTheme();
    }

    // 主题配置
    setupTheme() {
        const themes = {
            professional: {
                primary: '1E3A5F',      // 深蓝
                secondary: '3B82F6',    // 天蓝
                accent: 'F59E0B',       // 橙色
                text: '1F2937',
                background: 'FFFFFF',
                fontTitle: 'Arial',
                fontBody: 'Arial'
            },
            modern: {
                primary: '6366F1',
                secondary: '8B5CF6',
                accent: 'EC4899',
                text: '1F2937',
                background: 'F9FAFB',
                fontTitle: 'Microsoft YaHei',
                fontBody: 'Microsoft YaHei'
            }
        };

        this.theme = themes[this.options.theme] || themes.professional;
    }

    // 添加标题页
    addTitleSlide(title, subtitle, options = {}) {
        const slide = this.pptx.addSlide();
        
        // 背景色
        slide.addShape('rect', {
            fill: { color: this.theme.primary },
            w: '100%',
            h: '100%'
        });

        // 标题
        slide.addText(title, {
            x: 0.5,
            y: 2.5,
            w: 9,
            h: 1.5,
            fontSize: 44,
            bold: true,
            color: 'FFFFFF',
            align: 'center',
            fontFace: this.theme.fontTitle
        });

        // 副标题
        if (subtitle) {
            slide.addText(subtitle, {
                x: 0.5,
                y: 4.2,
                w: 9,
                h: 0.8,
                fontSize: 20,
                color: 'E5E7EB',
                align: 'center',
                fontFace: this.theme.fontBody
            });
        }

        // 日期
        slide.addText(new Date().toLocaleDateString('zh-CN'), {
            x: 0.5,
            y: 5.2,
            w: 9,
            h: 0.5,
            fontSize: 14,
            color: '9CA3AF',
            align: 'center'
        });

        return slide;
    }

    // 添加目录页
    addTOCSlide(tocItems) {
        const slide = this.pptx.addSlide();
        slide.background = { color: 'FFFFFF' };

        // 标题
        slide.addText('目录', {
            x: 0.5,
            y: 0.3,
            w: 9,
            h: 0.8,
            fontSize: 32,
            bold: true,
            color: this.theme.primary,
            fontFace: this.theme.fontTitle
        });

        // 目录项
        tocItems.forEach((item, index) => {
            const y = 1.4 + index * 0.6;

            // 序号
            slide.addText(`${index + 1}`, {
                x: 0.8,
                y: y,
                w: 0.5,
                h: 0.5,
                fontSize: 18,
                bold: true,
                color: this.theme.secondary,
                align: 'center'
            });

            // 标题
            slide.addText(item, {
                x: 1.4,
                y: y,
                w: 7,
                h: 0.5,
                fontSize: 16,
                color: this.theme.text,
                fontFace: this.theme.fontBody
            });

            // 分隔线
            slide.addShape('line', {
                x: 1.4,
                y: y + 0.5,
                w: 7,
                h: 0,
                line: { color: 'E5E7EB', width: 0.5 }
            });
        });

        return slide;
    }

    // 添加内容页(带截图)
    addScreenshotSlide(title, screenshotPath, notes = '') {
        const slide = this.pptx.addSlide();
        slide.background = { color: 'FFFFFF' };

        // 标题栏背景
        slide.addShape('rect', {
            x: 0,
            y: 0,
            w: 10,
            h: 0.8,
            fill: { color: this.theme.primary }
        });

        // 标题
        slide.addText(title, {
            x: 0.3,
            y: 0.15,
            w: 9,
            h: 0.5,
            fontSize: 20,
            bold: true,
            color: 'FFFFFF',
            fontFace: this.theme.fontTitle
        });

        // 截图
        if (screenshotPath && fs.existsSync(screenshotPath)) {
            slide.addImage({
                x: 0.3,
                y: 1,
                w: 9.4,
                h: 5,
                src: screenshotPath,
                shadow: {
                    type: 'outer',
                    color: '000000',
                    blur: 8,
                    offset: 3,
                    angle: 135,
                    opacity: 0.2
                }
            });
        }

        // 备注
        if (notes) {
            slide.addText(notes, {
                x: 0.3,
                y: 6.2,
                w: 9,
                h: 0.6,
                fontSize: 12,
                color: '6B7280',
                fontFace: this.theme.fontBody
            });
        }

        return slide;
    }

    // 添加分析结果页
    addAnalysisSlide(title, analysisData, options = {}) {
        const slide = this.pptx.addSlide();
        slide.background = { color: 'FFFFFF' };

        // 标题栏
        slide.addShape('rect', {
            x: 0,
            y: 0,
            w: 10,
            h: 0.8,
            fill: { color: this.theme.primary }
        });

        slide.addText(title, {
            x: 0.3,
            y: 0.15,
            w: 9,
            h: 0.5,
            fontSize: 20,
            bold: true,
            color: 'FFFFFF',
            fontFace: this.theme.fontTitle
        });

        // 内容区域
        const contentY = 1.2;
        let currentY = contentY;

        if (analysisData.summary) {
            slide.addText('摘要', {
                x: 0.5,
                y: currentY,
                w: 2,
                h: 0.4,
                fontSize: 14,
                bold: true,
                color: this.theme.secondary
            });
            currentY += 0.4;

            slide.addText(analysisData.summary, {
                x: 0.5,
                y: currentY,
                w: 9,
                h: 1,
                fontSize: 12,
                color: this.theme.text,
                fontFace: this.theme.fontBody,
                valign: 'top'
            });
            currentY += 1.2;
        }

        if (analysisData.keywords && analysisData.keywords.length > 0) {
            slide.addText('关键词', {
                x: 0.5,
                y: currentY,
                w: 2,
                h: 0.4,
                fontSize: 14,
                bold: true,
                color: this.theme.secondary
            });
            currentY += 0.4;

            const keywordText = analysisData.keywords.join(' | ');
            slide.addText(keywordText, {
                x: 0.5,
                y: currentY,
                w: 9,
                h: 0.5,
                fontSize: 12,
                color: this.theme.accent,
                fontFace: this.theme.fontBody
            });
            currentY += 0.7;
        }

        // 来源链接
        if (analysisData.url) {
            slide.addText(`来源: ${analysisData.url}`, {
                x: 0.5,
                y: 6.5,
                w: 9,
                h: 0.3,
                fontSize: 10,
                color: '9CA3AF',
                fontFace: this.theme.fontBody
            });
        }

        return slide;
    }

    // 添加对比表格页
    addComparisonSlide(title, items, columns) {
        const slide = this.pptx.addSlide();
        slide.background = { color: 'FFFFFF' };

        // 标题
        slide.addShape('rect', {
            x: 0,
            y: 0,
            w: 10,
            h: 0.8,
            fill: { color: this.theme.primary }
        });

        slide.addText(title, {
            x: 0.3,
            y: 0.15,
            w: 9,
            h: 0.5,
            fontSize: 20,
            bold: true,
            color: 'FFFFFF',
            fontFace: this.theme.fontTitle
        });

        // 表格数据
        const tableData = [
            columns.map(col => ({
                text: col.header,
                options: {
                    bold: true,
                    fill: { color: this.theme.secondary },
                    color: 'FFFFFF',
                    align: 'center'
                }
            })),
            ...items.map(item => 
                columns.map(col => ({
                    text: item[col.key] || '-',
                    options: { align: 'center' }
                }))
            )
        ];

        slide.addTable(tableData, {
            x: 0.3,
            y: 1.2,
            w: 9.4,
            colW: columns.map(() => 9.4 / columns.length),
            border: { pt: 0.5, color: 'E5E7EB' },
            fontFace: this.theme.fontBody,
            fontSize: 11
        });

        return slide;
    }

    // 添加总结页
    addSummarySlide(title, conclusions, recommendations) {
        const slide = this.pptx.addSlide();
        slide.background = { color: 'FFFFFF' };

        // 标题
        slide.addText(title, {
            x: 0.5,
            y: 0.3,
            w: 9,
            h: 0.8,
            fontSize: 28,
            bold: true,
            color: this.theme.primary,
            fontFace: this.theme.fontTitle
        });

        // 结论
        let y = 1.3;
        if (conclusions && conclusions.length > 0) {
            slide.addText('主要结论', {
                x: 0.5,
                y: y,
                w: 9,
                h: 0.5,
                fontSize: 16,
                bold: true,
                color: this.theme.secondary
            });
            y += 0.6;

            conclusions.forEach((item, index) => {
                slide.addText(`${index + 1}. ${item}`, {
                    x: 0.8,
                    y: y,
                    w: 8.5,
                    h: 0.5,
                    fontSize: 13,
                    color: this.theme.text,
                    fontFace: this.theme.fontBody,
                    bullet: false
                });
                y += 0.5;
            });
        }

        // 建议
        if (recommendations && recommendations.length > 0) {
            y += 0.3;
            slide.addText('建议', {
                x: 0.5,
                y: y,
                w: 9,
                h: 0.5,
                fontSize: 16,
                bold: true,
                color: this.theme.accent
            });
            y += 0.6;

            recommendations.forEach((item, index) => {
                slide.addText(`${index + 1}. ${item}`, {
                    x: 0.8,
                    y: y,
                    w: 8.5,
                    h: 0.5,
                    fontSize: 13,
                    color: this.theme.text,
                    fontFace: this.theme.fontBody
                });
                y += 0.5;
            });
        }

        return slide;
    }

    // 保存文件
    async save(filepath = 'report.pptx') {
        this.pptx.author = this.options.author;
        this.pptx.company = this.options.company;
        this.pptx.title = this.options.title;
        
        await this.pptx.writeFile({ fileName: filepath });
        console.log(`✅ PPT已保存: ${filepath}`);
        return filepath;
    }
}

完整使用示例

// 主程序示例
async function generateSearchReport(keyword, options = {}) {
    const {
        searchEngine = 'baidu',
        maxResults = 10,
        days = 30,
        includeScreenshots = true,
        outputPath = './output/report.pptx'
    } = options;

    console.log(`🔍 开始搜索: "${keyword}"`);

    // Step 1: 搜索
    const searchResults = await searchByKeyword(keyword, {
        engine: searchEngine,
        limit: maxResults,
        days: days
    });
    console.log(`📋 找到 ${searchResults.count} 条结果`);

    // Step 2: 抓取内容
    const fetchedItems = [];
    for (const result of searchResults.results.slice(0, 5)) {
        const content = await fetchAndExtract(result.url);
        fetchedItems.push(content);
        await sleep(1000); // 避免请求过快
    }

    // Step 3: 分析
    const analyzedItems = await analyzeContents(fetchedItems);

    // Step 4: 截图
    let screenshots = [];
    if (includeScreenshots) {
        screenshots = await captureScreenshots(
            fetchedItems.map(i => i.url),
            { outputDir: './screenshots' }
        );
    }

    // Step 5: 生成PPT
    const pptx = new PPTGenerator({
        title: `${keyword} 调研报告`,
        author: 'AI Assistant',
        theme: 'professional'
    });

    // 封面
    pptx.addTitleSlide(`${keyword} 调研报告`, `搜索关键词: ${keyword}`);

    // 目录
    pptx.addTOCSlide([
        '搜索概述',
        '网页内容分析',
        '截图展示',
        '总结与建议'
    ]);

    // 内容页
    analyzedItems.forEach((item, index) => {
        pptx.addAnalysisSlide(`内容分析 ${index + 1}`, item);
    });

    // 截图页
    screenshots.forEach((shot, index) => {
        if (shot.success) {
            pptx.addScreenshotSlide(
                `网页截图 ${index + 1}`,
                shot.filepath,
                `来源: ${shot.url}`
            );
        }
    });

    // 总结页
    pptx.addSummarySlide(
        '总结与建议',
        ['搜索完成,内容已整理', '详见各页面详细分析'],
        ['建议深入研究重点页面', '可进一步扩大搜索范围']
    );

    // 保存
    await pptx.save(outputPath);

    return {
        searchResults,
        analyzedItems,
        screenshots,
        pptxPath: outputPath
    };
}

// 执行示例
generateSearchReport('人工智能发展趋势', {
    searchEngine: 'baidu',
    maxResults: 10,
    includeScreenshots: true
});

依赖环境

Node.js 依赖

npm install puppeteer cheerio pptxgenjs axios

| 包名 | 用途 | |------|------| | puppeteer | 浏览器自动化和截图 | | cheerio | HTML解析 | | pptxgenjs | PPT生成 | | axios | HTTP请求 |

Python 备选方案

pip install requests beautifulsoup4 python-pptx playwright

常见问题

| 问题 | 解决方案 | |------|---------| | 截图失败 | 检查Puppeteer是否正确安装,运行 npx puppeteer browsers install | | 内容提取不完整 | 尝试指定不同的选择器或手动调整提取逻辑 | | PPT排版错乱 | 检查中文字体是否正确加载 | | 请求被拦截 | 添加适当的User-Agent和使用延迟 |


输出示例

生成的文件结构:

output/
├── report.pptx           # 最终PPT报告
├── screenshots/          # 截图文件夹
│   ├── example.com_1.png
│   ├── example.com_2.png
│   └── ...
└── data/
    └── search_results.json   # 搜索数据备份

触发词清单

  • 搜索并截图
  • 做调研报告
  • 竞品截图分析
  • 网页截图汇总
  • 搜索结果截图
  • 竞品调研PPT
  • 研究报告生成
  • 网络调研截图
  • 关键词搜索汇总
  • 网页内容抓取PPT