网页搜索截图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
Scan to contact