1. 项目概述与核心价值最近在折腾一些AI应用开发发现一个挺有意思的现象很多开发者想基于ChatGPT的插件生态做点东西但第一步就卡住了——不知道插件到底能怎么用或者说用户会怎么用。官方文档里给的例子往往比较基础而真正能激发灵感的是那些已经上线的、被真实用户使用过的插件以及它们背后那些五花八门的提示词Prompts。这就是我偶然发现simonw/scrape-chatgpt-plugin-prompts这个项目时眼前一亮的原因。这个项目直击了一个非常具体的痛点如何系统地、自动化地收集和分析ChatGPT插件在实际对话中被触发的真实提示词。它不是一个成品工具而是一个“元工具”的脚本集合或者说是一个技术探索的起点。它的核心价值在于通过逆向工程或数据抓取的方式为我们打开了一扇窗让我们能窥见插件生态中那些鲜活的、动态的用户意图和交互模式。对于AI应用开发者、产品经理甚至是研究人机交互的朋友来说这些真实的提示词数据是无价之宝。它们能告诉你用户真实需求用户到底想用插件解决什么问题他们的提问方式和你预设的一样吗插件能力边界哪些功能被高频使用哪些功能可能设计得过于复杂用户根本想不到去用提示词工程样本优秀的、能成功调用插件的提示词长什么样它们是如何结构化地表达需求的竞品与市场分析同类插件之间用户的使用场景和提问模式有何异同简单说这个项目提供了一套方法论和工具雏形帮助我们从“黑盒”的外部去观察和理解ChatGPT插件这个快速演进生态的内部运作。接下来我就结合自己的理解和一些扩展思路来拆解一下这个项目可能涉及的技术路径、实操难点以及我们能从中获得什么。2. 技术路径与实现思路拆解项目标题scrape-chatgpt-plugin-prompts已经点明了核心动作scrape抓取。但具体抓什么、从哪里抓、怎么抓就是技术实现上需要深入思考的地方了。根据当前ChatGPT的交互模式我推测并梳理了几种可能的技术路径。2.1 数据源分析与可行性评估首先我们需要明确“插件提示词”可能存在于哪些地方。这直接决定了抓取的策略和复杂度。1. 官方插件商店与描述页面这是最直观但信息量可能最有限的来源。OpenAI的插件商店会列出每个插件的名称、描述、认证信息以及一个“尝试”按钮。点击“尝试”通常会开启一个与ChatGPT的预置对话窗口里面可能包含一些示例性的提示词。抓取这些页面可以获取插件的基础元数据如名称、ID、描述和官方提供的少数几个示例提示。可行性高。这属于标准的网页抓取目标明确结构相对固定。技术手段使用requests或httpx库获取HTML然后用BeautifulSoup或lxml进行解析。需要注意处理可能存在的JavaScript动态加载内容可能需要用到Selenium或Playwright进行模拟浏览器操作。价值获取插件名录和基础示例是构建数据集的起点。2. 社区分享与第三方聚合网站国内外有一些开发者和爱好者会分享他们使用插件的心得、技巧和“咒语”即精心设计的提示词。这些内容可能发布在个人博客、GitHub Gist、Reddit、Twitter或专门的提示词分享网站上。可行性中到高但分散且噪音大。需要针对特定站点定制抓取规则且需要很强的文本清洗和去重能力。技术手段除了基础的网页抓取还需要用到更复杂的文本挖掘技术如关键词匹配“plugin”、“prompt”、“use case”、正文提取工具如readability、newspaper3k来从网页中剥离出核心内容。价值能获得经过人工筛选和优化的高质量提示词案例富含实践智慧。3. 客户端界面模拟与交互日志分析高难度/灰色地带理论上最丰富、最真实的提示词数据流存在于用户与ChatGPT的实时对话中特别是当插件被选择、调用时的上下文。但这部分数据是私密的且受到严格保护。可行性极低且涉及严重的法律和道德风险。任何尝试通过逆向工程客户端、拦截网络请求、注入脚本来获取用户私人对话数据的行为都是明确违反服务条款甚至可能触犯法律的。这个路径必须坚决摒弃。替代思路合规项目作者Simon Willison是一位资深的技术博主和开发者他更可能倡导的是通过公开、合规的方式收集数据。例如鼓励用户自愿、匿名地分享脱敏后的提示词片段需去除任何个人身份信息或者通过研究公开的API文档和沙箱环境来推断交互模式。2.2 核心抓取架构设计基于对公开数据源的抓取我们可以设计一个相对稳健的架构。这个架构的核心是“可扩展的抓取管道”。调度中心一个简单的脚本或使用Celery、APScheduler这样的工具定期触发抓取任务。频率需要合理避免对目标网站造成压力例如每天或每周执行一次。抓取器针对不同的数据源如插件商店、特定博客、Reddit版块编写独立的抓取模块。每个模块负责发送HTTP请求并处理响应状态码、重定向、反爬虫机制如速率限制、验证码。解析HTML/JSON响应提取目标数据插件名称、ID、描述、示例提示词、分享的提示词文本等。将提取的数据转换为结构化的格式如JSON。数据清洗与标准化原始抓取的数据往往很脏。需要清洗步骤去重根据插件ID和提示词内容哈希值去除完全重复的记录。标准化统一提示词的格式如去除多余空格、换行符。分类/打标尝试根据提示词内容或来源为其打上粗略的标签如“数据查询”、“内容生成”、“工具调用”等。存储将清洗后的结构化数据存储起来。对于初期探索或小规模数据一个SQLite数据库Simon Willison很喜欢用这个就足够了。字段可以设计为id,plugin_name,plugin_id,prompt_text,source_url,category,collected_at。分析与导出提供简单的查询接口或导出功能如导出为JSON Lines或CSV文件方便后续进行数据分析。注意在整个设计和实施过程中必须严格遵守robots.txt协议为请求设置合理的延迟如time.sleep(2)并使用真实的User-Agent字符串以体现对目标网站的尊重做一个负责任的网络公民。3. 实操构建一个基础的抓取示例我们以“抓取ChatGPT插件商店的插件基本信息及示例提示”为目标来构建一个最小可行版本。这里假设插件商店的页面结构是相对静态的。3.1 环境准备与依赖安装首先创建一个干净的Python虚拟环境并安装必要的库。我偏好使用httpx因为它支持HTTP/2且异步友好用BeautifulSoup做解析。# 创建并激活虚拟环境以macOS/Linux为例 python -m venv venv source venv/bin/activate # 安装核心依赖 pip install httpx beautifulsoup4 lxml # 可选用于处理更复杂动态页面的库 # pip install playwright # playwright install chromium3.2 编写核心抓取脚本我们创建一个名为scrape_plugin_store.py的脚本。由于我们无法得知真实的插件商店URL结构这里以模拟逻辑为例。import httpx from bs4 import BeautifulSoup import json import time from urllib.parse import urljoin import sqlite3 from datetime import datetime # 假设的插件商店列表页和详情页URL模式需根据实际情况替换 PLUGIN_LIST_URL https://chat.openai.com/plugins # 示例非真实地址 PLUGIN_DETAIL_BASE https://chat.openai.com/plugins/ def fetch_page(url): 获取网页内容处理基础错误 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 } try: # 使用httpx可以方便地设置超时和重试 with httpx.Client(timeout30.0, headersheaders) as client: resp client.get(url) resp.raise_for_status() # 如果状态码不是200抛出异常 return resp.text except httpx.RequestError as e: print(f请求错误: {e}) return None except httpx.HTTPStatusError as e: print(fHTTP状态错误: {e.response.status_code}) return None def parse_plugin_list(html): 从列表页解析出插件详情页链接列表 soup BeautifulSoup(html, lxml) plugin_links [] # 这里的选择器是假设的需要根据实际网页结构用浏览器开发者工具分析 # 例如每个插件卡片可能在一个带有特定class的a标签里 for card in soup.select(a.plugin-card): # 假设的选择器 href card.get(href) if href: # 拼接完整的详情页URL full_url urljoin(PLUGIN_LIST_URL, href) plugin_links.append(full_url) return plugin_links def parse_plugin_detail(html, detail_url): 从插件详情页解析插件名称、描述和示例提示 soup BeautifulSoup(html, lxml) plugin_data { name: , description: , example_prompts: [], detail_url: detail_url, scraped_at: datetime.utcnow().isoformat() } # 1. 解析插件名称 (假设在h1标签里) name_tag soup.find(h1) if name_tag: plugin_data[name] name_tag.get_text(stripTrue) # 2. 解析描述 (假设在某个div classdescription里) desc_tag soup.select_one(div.description) if desc_tag: plugin_data[description] desc_tag.get_text(stripTrue) # 3. 解析示例提示 (假设在code或特定class的div里) # 这里需要仔细观察页面示例提示可能在一个“Try it”区域 for prompt_area in soup.select(div.example-prompt): # 假设的选择器 prompt_text prompt_area.get_text(stripTrue) if prompt_text: plugin_data[example_prompts].append(prompt_text) return plugin_data def init_database(db_pathplugins.db): 初始化SQLite数据库 conn sqlite3.connect(db_path) c conn.cursor() c.execute( CREATE TABLE IF NOT EXISTS plugins ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, example_prompts TEXT, -- 存储为JSON字符串 detail_url TEXT UNIQUE, scraped_at TEXT ) ) conn.commit() conn.close() def save_to_database(plugin_data, db_pathplugins.db): 将插件数据存入数据库 conn sqlite3.connect(db_path) c conn.cursor() # 使用INSERT OR IGNORE防止重复插入基于detail_url c.execute( INSERT OR IGNORE INTO plugins (name, description, example_prompts, detail_url, scraped_at) VALUES (?, ?, ?, ?, ?) , ( plugin_data[name], plugin_data[description], json.dumps(plugin_data[example_prompts]), # 列表转为JSON字符串存储 plugin_data[detail_url], plugin_data[scraped_at] )) conn.commit() conn.close() def main(): print(初始化数据库...) init_database() print(开始抓取插件列表页...) list_html fetch_page(PLUGIN_LIST_URL) if not list_html: print(无法获取列表页退出。) return plugin_urls parse_plugin_list(list_html) print(f发现 {len(plugin_urls)} 个插件。) for i, url in enumerate(plugin_urls): print(f处理 [{i1}/{len(plugin_urls)}]: {url}) detail_html fetch_page(url) if detail_html: plugin_info parse_plugin_detail(detail_html, url) if plugin_info[name]: # 确保有基本数据 save_to_database(plugin_info) print(f 已保存: {plugin_info[name]}) else: print(f 警告: 未能从 {url} 解析出插件名称) else: print(f 错误: 无法获取详情页 {url}) # 礼貌性延迟避免请求过快 time.sleep(1) print(抓取完成) if __name__ __main__: main()脚本要点解析请求与错误处理fetch_page函数封装了HTTP请求并处理了网络错误和HTTP错误使主流程更健壮。选择器是关键parse_plugin_list和parse_plugin_detail函数中的CSS选择器如a.plugin-card,div.example-prompt是完全假设的。在实际操作中你必须使用浏览器的开发者工具F12仔细分析目标网页的真实HTML结构找到正确的标签和类名来定位数据。这是网页抓取中最耗时、最需要耐心的部分。数据存储这里使用了SQLite轻量且无需额外服务。将示例提示词列表转为JSON字符串存储便于后续查询和解析。延迟与礼貌time.sleep(1)是基本的反反爬虫策略也是对目标服务器资源的尊重。3.3 处理动态加载内容现代网页大量使用JavaScript动态加载数据。如果上述方法获取的HTML中找不到数据很可能数据是通过API异步获取的。这时需要换用Playwright或Selenium。# 使用Playwright的示例片段 from playwright.sync_api import sync_playwright def scrape_with_playwright(url): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) # 无头模式 page browser.new_page() page.goto(url) # 等待特定元素出现确保数据加载完成 page.wait_for_selector(div.plugin-list, timeout10000) # 假设的选择器 html page.content() browser.close() return html # 然后用这个html替换上面fetch_page获取的html进行解析。实操心得优先尝试分析网络请求。打开开发者工具的“网络”(Network)选项卡刷新页面过滤XHR/Fetch请求往往能找到直接返回结构化JSON数据的API接口。直接调用这些API比解析HTML更稳定、更高效。但这需要一定的逆向工程能力并且API接口可能随时变更。4. 从数据到洞察分析与应用场景抓取到数据只是第一步如何从这些看似杂乱的提示词中提炼出有价值的信息才是项目的精髓。4.1 基础数据分析方法将数据从SQLite中导出后我们可以用Python的pandas、matplotlib或Jupyter Notebook进行快速分析。import sqlite3 import pandas as pd import json from collections import Counter import re # 连接数据库并加载数据 conn sqlite3.connect(plugins.db) df pd.read_sql_query(SELECT * FROM plugins, conn) conn.close() # 将存储的JSON字符串转换回列表 df[example_prompts_list] df[example_prompts].apply(json.loads) # 1. 基础统计 print(f总共抓取了 {len(df)} 个插件。) # 计算每个插件的平均示例提示词数量 df[prompt_count] df[example_prompts_list].apply(len) print(f平均每个插件有 {df[prompt_count].mean():.2f} 个示例提示。) # 2. 词频分析简单的关键词提取 all_prompts [] for prompts in df[example_prompts_list]: all_prompts.extend(prompts) # 将所有提示词合并成一个长文本 all_text .join(all_prompts) # 简单的分词这里按空格分中文需要更复杂的分词库如jieba words re.findall(r\b\w\b, all_text.lower()) # 提取单词并转为小写 # 过滤掉常见的停用词如 the, a, an, in, on, for... stop_words set([the, a, an, in, on, for, to, of, and, is, at]) filtered_words [w for w in words if w not in stop_words and len(w) 3] word_freq Counter(filtered_words).most_common(20) print(最常见的20个词汇) for word, freq in word_freq: print(f {word}: {freq}) # 3. 插件功能分类基于描述关键词 def categorize_by_description(desc): desc_lower desc.lower() if any(word in desc_lower for word in [search, find, query, data]): return 数据查询 elif any(word in desc_lower for word in [generate, write, create, content]): return 内容生成 elif any(word in desc_lower for word in [calculate, convert, translate, tool]): return 工具助手 else: return 其他 df[category] df[description].apply(categorize_by_description) category_counts df[category].value_counts() print(\n插件功能分类统计) print(category_counts)4.2 高级应用场景挖掘基础分析能给出一个概貌但深度价值在于针对性的挖掘。场景一竞品插件对比分析假设你想开发一个“旅行规划”插件。你可以筛选出所有描述中包含“travel”、“trip”、“flight”、“hotel”的插件仔细研究它们的示例提示词。用户是倾向于问“帮我规划一个去东京的5天行程”这样的开放式问题还是“查找下周五从北京飞往东京的最便宜航班”这样的具体指令提示词中是否普遍包含了预算、人数、时间等约束条件哪些插件的提示词设计得更自然、更像真人对话这能为你设计自己插件的“自然语言理解”逻辑提供参考。场景二提示词模式Pattern归纳这是对开发者最有用的部分。通过大量样本你可以总结出调用某类插件的“最佳实践”提示词结构。 例如对于“数据查询”类插件模式可能是“使用 [插件名] 查找/搜索 [具体查询内容]条件包括 [条件1]、[条件2]并按照 [排序方式] 返回结果。”而对于“内容生成”类插件模式可能更偏向“扮演一个 [角色]以 [风格] 写一篇关于 [主题] 的 [文章类型]要求包含 [要点1]、[要点2]字数大约在 [字数] 左右。”将这些模式整理成文档或代码模板能极大提升你设计插件交互逻辑的效率。场景三发现“未满足的需求”有时用户会在提示词中“抱怨”或提出超出插件当前能力的请求。例如一个天气插件下的用户提问“告诉我明天下午会不会下雨如果下雨顺便推荐几个室内的活动。” 后半句“推荐室内活动”可能超出了该插件的设计范围。大量收集这类“边界性”提示词能帮助你发现用户潜在的、未被现有插件满足的复合型需求这可能就是新产品的机会点。5. 常见问题、伦理考量与避坑指南在实际操作这类项目时你会遇到技术和非技术上的各种挑战。5.1 技术性挑战与解决方案问题可能原因解决方案与建议抓取不到数据1. 网页结构已更新CSS选择器失效。2. 数据由JavaScript动态加载。3. 网站有反爬虫机制如验证码、IP封锁。1.定期维护将选择器作为配置项便于更新。2.使用无头浏览器换用Playwright/Selenium。3.分析网络请求直接调用数据API。4.遵守robots.txt设置合理延迟和轮换User-Agent。数据质量差1. 示例提示词数量少或质量低。2. 抓取了大量无关文本导航栏、广告。1.多源抓取不局限于官方商店增加社区源。2.精细化解析结合多个HTML标签和属性来精确定位目标区域。3.后清洗编写更复杂的文本清洗规则如基于长度、关键词的过滤。解析速度慢1. 同步请求导致I/O等待。2. 页面元素复杂解析耗时。1.异步抓取使用asyncioaiohttp/httpx并发请求。2.增量抓取只抓取新增或更新的插件记录最后抓取时间。数据存储混乱不同来源的数据格式不统一。1.设计弹性Schema在数据库中使用JSON字段存储可变部分。2.ETL管道在存储前增加一个“转换(Transform)”步骤将不同源的数据映射到统一模型。5.2 法律、伦理与合规性这是比技术问题更重要的红线必须严肃对待。尊重版权与数据所有权抓取的数据尤其是社区用户创作的提示词可能包含版权内容。切勿将抓取的数据用于商业用途或大规模公开分发特别是未脱敏的原始数据。本项目应定位为个人研究、学习和非商业的探索工具。严格遵守服务条款仔细阅读目标网站如OpenAI插件商店、Reddit、Twitter等的服务条款。明确禁止抓取的行为坚决不做。对于有公开API的站点优先使用API。隐私保护绝对不要尝试抓取任何个人隐私数据或非公开对话。我们关注的是公开的、示例性的、非个人的提示词信息。设置明确的免责声明如果你公开分享你的抓取代码或分析结果务必附带清晰的免责声明说明该项目仅用于教育研究目的数据来源于公开网络并鼓励用户尊重原始数据源的版权和条款。5.3 我的实操心得与建议从小处着手快速验证不要一开始就想构建一个覆盖全网的庞大系统。先从抓取一个你最感兴趣的插件类别开始写一个简单的脚本看看能拿到什么数据数据质量如何。快速验证想法的可行性。数据清洗比抓取更耗时预计你会花60%以上的时间在数据清洗和标准化上。正则表达式是你的好朋友但也要学会适可而止不必追求100%的完美清洗对于研究分析80%的清洁度往往就够用了。关注“数据故事”而非“数据堆砌”收集了十万条提示词不是目的。目的是你能从中讲出什么故事比如“用户在使用日历插件时超过70%的请求都包含了‘会议’和‘安排’这两个词”这就是一个洞察。带着问题去分析数据。工具是辅助思维是核心scrape-chatgpt-plugin-prompts项目提供的是一种思路和工具雏形。真正的价值在于你如何利用这种思路去观察、理解并最终服务于你的产品设计、开发或研究工作。你可以将它改造成抓取其他AI产品提示词的工具或者与你自己的插件开发流程相结合。这个项目的魅力在于它处于一个快速变化的领域前沿。通过它你不仅是在学习数据抓取技术更是在近距离观察一场人机交互范式变革的早期细节。每一次抓取和分析都可能让你对如何构建更自然、更有用的AI工具有新的认识。
ChatGPT插件提示词抓取与分析:从数据洞察到AI应用开发
1. 项目概述与核心价值最近在折腾一些AI应用开发发现一个挺有意思的现象很多开发者想基于ChatGPT的插件生态做点东西但第一步就卡住了——不知道插件到底能怎么用或者说用户会怎么用。官方文档里给的例子往往比较基础而真正能激发灵感的是那些已经上线的、被真实用户使用过的插件以及它们背后那些五花八门的提示词Prompts。这就是我偶然发现simonw/scrape-chatgpt-plugin-prompts这个项目时眼前一亮的原因。这个项目直击了一个非常具体的痛点如何系统地、自动化地收集和分析ChatGPT插件在实际对话中被触发的真实提示词。它不是一个成品工具而是一个“元工具”的脚本集合或者说是一个技术探索的起点。它的核心价值在于通过逆向工程或数据抓取的方式为我们打开了一扇窗让我们能窥见插件生态中那些鲜活的、动态的用户意图和交互模式。对于AI应用开发者、产品经理甚至是研究人机交互的朋友来说这些真实的提示词数据是无价之宝。它们能告诉你用户真实需求用户到底想用插件解决什么问题他们的提问方式和你预设的一样吗插件能力边界哪些功能被高频使用哪些功能可能设计得过于复杂用户根本想不到去用提示词工程样本优秀的、能成功调用插件的提示词长什么样它们是如何结构化地表达需求的竞品与市场分析同类插件之间用户的使用场景和提问模式有何异同简单说这个项目提供了一套方法论和工具雏形帮助我们从“黑盒”的外部去观察和理解ChatGPT插件这个快速演进生态的内部运作。接下来我就结合自己的理解和一些扩展思路来拆解一下这个项目可能涉及的技术路径、实操难点以及我们能从中获得什么。2. 技术路径与实现思路拆解项目标题scrape-chatgpt-plugin-prompts已经点明了核心动作scrape抓取。但具体抓什么、从哪里抓、怎么抓就是技术实现上需要深入思考的地方了。根据当前ChatGPT的交互模式我推测并梳理了几种可能的技术路径。2.1 数据源分析与可行性评估首先我们需要明确“插件提示词”可能存在于哪些地方。这直接决定了抓取的策略和复杂度。1. 官方插件商店与描述页面这是最直观但信息量可能最有限的来源。OpenAI的插件商店会列出每个插件的名称、描述、认证信息以及一个“尝试”按钮。点击“尝试”通常会开启一个与ChatGPT的预置对话窗口里面可能包含一些示例性的提示词。抓取这些页面可以获取插件的基础元数据如名称、ID、描述和官方提供的少数几个示例提示。可行性高。这属于标准的网页抓取目标明确结构相对固定。技术手段使用requests或httpx库获取HTML然后用BeautifulSoup或lxml进行解析。需要注意处理可能存在的JavaScript动态加载内容可能需要用到Selenium或Playwright进行模拟浏览器操作。价值获取插件名录和基础示例是构建数据集的起点。2. 社区分享与第三方聚合网站国内外有一些开发者和爱好者会分享他们使用插件的心得、技巧和“咒语”即精心设计的提示词。这些内容可能发布在个人博客、GitHub Gist、Reddit、Twitter或专门的提示词分享网站上。可行性中到高但分散且噪音大。需要针对特定站点定制抓取规则且需要很强的文本清洗和去重能力。技术手段除了基础的网页抓取还需要用到更复杂的文本挖掘技术如关键词匹配“plugin”、“prompt”、“use case”、正文提取工具如readability、newspaper3k来从网页中剥离出核心内容。价值能获得经过人工筛选和优化的高质量提示词案例富含实践智慧。3. 客户端界面模拟与交互日志分析高难度/灰色地带理论上最丰富、最真实的提示词数据流存在于用户与ChatGPT的实时对话中特别是当插件被选择、调用时的上下文。但这部分数据是私密的且受到严格保护。可行性极低且涉及严重的法律和道德风险。任何尝试通过逆向工程客户端、拦截网络请求、注入脚本来获取用户私人对话数据的行为都是明确违反服务条款甚至可能触犯法律的。这个路径必须坚决摒弃。替代思路合规项目作者Simon Willison是一位资深的技术博主和开发者他更可能倡导的是通过公开、合规的方式收集数据。例如鼓励用户自愿、匿名地分享脱敏后的提示词片段需去除任何个人身份信息或者通过研究公开的API文档和沙箱环境来推断交互模式。2.2 核心抓取架构设计基于对公开数据源的抓取我们可以设计一个相对稳健的架构。这个架构的核心是“可扩展的抓取管道”。调度中心一个简单的脚本或使用Celery、APScheduler这样的工具定期触发抓取任务。频率需要合理避免对目标网站造成压力例如每天或每周执行一次。抓取器针对不同的数据源如插件商店、特定博客、Reddit版块编写独立的抓取模块。每个模块负责发送HTTP请求并处理响应状态码、重定向、反爬虫机制如速率限制、验证码。解析HTML/JSON响应提取目标数据插件名称、ID、描述、示例提示词、分享的提示词文本等。将提取的数据转换为结构化的格式如JSON。数据清洗与标准化原始抓取的数据往往很脏。需要清洗步骤去重根据插件ID和提示词内容哈希值去除完全重复的记录。标准化统一提示词的格式如去除多余空格、换行符。分类/打标尝试根据提示词内容或来源为其打上粗略的标签如“数据查询”、“内容生成”、“工具调用”等。存储将清洗后的结构化数据存储起来。对于初期探索或小规模数据一个SQLite数据库Simon Willison很喜欢用这个就足够了。字段可以设计为id,plugin_name,plugin_id,prompt_text,source_url,category,collected_at。分析与导出提供简单的查询接口或导出功能如导出为JSON Lines或CSV文件方便后续进行数据分析。注意在整个设计和实施过程中必须严格遵守robots.txt协议为请求设置合理的延迟如time.sleep(2)并使用真实的User-Agent字符串以体现对目标网站的尊重做一个负责任的网络公民。3. 实操构建一个基础的抓取示例我们以“抓取ChatGPT插件商店的插件基本信息及示例提示”为目标来构建一个最小可行版本。这里假设插件商店的页面结构是相对静态的。3.1 环境准备与依赖安装首先创建一个干净的Python虚拟环境并安装必要的库。我偏好使用httpx因为它支持HTTP/2且异步友好用BeautifulSoup做解析。# 创建并激活虚拟环境以macOS/Linux为例 python -m venv venv source venv/bin/activate # 安装核心依赖 pip install httpx beautifulsoup4 lxml # 可选用于处理更复杂动态页面的库 # pip install playwright # playwright install chromium3.2 编写核心抓取脚本我们创建一个名为scrape_plugin_store.py的脚本。由于我们无法得知真实的插件商店URL结构这里以模拟逻辑为例。import httpx from bs4 import BeautifulSoup import json import time from urllib.parse import urljoin import sqlite3 from datetime import datetime # 假设的插件商店列表页和详情页URL模式需根据实际情况替换 PLUGIN_LIST_URL https://chat.openai.com/plugins # 示例非真实地址 PLUGIN_DETAIL_BASE https://chat.openai.com/plugins/ def fetch_page(url): 获取网页内容处理基础错误 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 } try: # 使用httpx可以方便地设置超时和重试 with httpx.Client(timeout30.0, headersheaders) as client: resp client.get(url) resp.raise_for_status() # 如果状态码不是200抛出异常 return resp.text except httpx.RequestError as e: print(f请求错误: {e}) return None except httpx.HTTPStatusError as e: print(fHTTP状态错误: {e.response.status_code}) return None def parse_plugin_list(html): 从列表页解析出插件详情页链接列表 soup BeautifulSoup(html, lxml) plugin_links [] # 这里的选择器是假设的需要根据实际网页结构用浏览器开发者工具分析 # 例如每个插件卡片可能在一个带有特定class的a标签里 for card in soup.select(a.plugin-card): # 假设的选择器 href card.get(href) if href: # 拼接完整的详情页URL full_url urljoin(PLUGIN_LIST_URL, href) plugin_links.append(full_url) return plugin_links def parse_plugin_detail(html, detail_url): 从插件详情页解析插件名称、描述和示例提示 soup BeautifulSoup(html, lxml) plugin_data { name: , description: , example_prompts: [], detail_url: detail_url, scraped_at: datetime.utcnow().isoformat() } # 1. 解析插件名称 (假设在h1标签里) name_tag soup.find(h1) if name_tag: plugin_data[name] name_tag.get_text(stripTrue) # 2. 解析描述 (假设在某个div classdescription里) desc_tag soup.select_one(div.description) if desc_tag: plugin_data[description] desc_tag.get_text(stripTrue) # 3. 解析示例提示 (假设在code或特定class的div里) # 这里需要仔细观察页面示例提示可能在一个“Try it”区域 for prompt_area in soup.select(div.example-prompt): # 假设的选择器 prompt_text prompt_area.get_text(stripTrue) if prompt_text: plugin_data[example_prompts].append(prompt_text) return plugin_data def init_database(db_pathplugins.db): 初始化SQLite数据库 conn sqlite3.connect(db_path) c conn.cursor() c.execute( CREATE TABLE IF NOT EXISTS plugins ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, example_prompts TEXT, -- 存储为JSON字符串 detail_url TEXT UNIQUE, scraped_at TEXT ) ) conn.commit() conn.close() def save_to_database(plugin_data, db_pathplugins.db): 将插件数据存入数据库 conn sqlite3.connect(db_path) c conn.cursor() # 使用INSERT OR IGNORE防止重复插入基于detail_url c.execute( INSERT OR IGNORE INTO plugins (name, description, example_prompts, detail_url, scraped_at) VALUES (?, ?, ?, ?, ?) , ( plugin_data[name], plugin_data[description], json.dumps(plugin_data[example_prompts]), # 列表转为JSON字符串存储 plugin_data[detail_url], plugin_data[scraped_at] )) conn.commit() conn.close() def main(): print(初始化数据库...) init_database() print(开始抓取插件列表页...) list_html fetch_page(PLUGIN_LIST_URL) if not list_html: print(无法获取列表页退出。) return plugin_urls parse_plugin_list(list_html) print(f发现 {len(plugin_urls)} 个插件。) for i, url in enumerate(plugin_urls): print(f处理 [{i1}/{len(plugin_urls)}]: {url}) detail_html fetch_page(url) if detail_html: plugin_info parse_plugin_detail(detail_html, url) if plugin_info[name]: # 确保有基本数据 save_to_database(plugin_info) print(f 已保存: {plugin_info[name]}) else: print(f 警告: 未能从 {url} 解析出插件名称) else: print(f 错误: 无法获取详情页 {url}) # 礼貌性延迟避免请求过快 time.sleep(1) print(抓取完成) if __name__ __main__: main()脚本要点解析请求与错误处理fetch_page函数封装了HTTP请求并处理了网络错误和HTTP错误使主流程更健壮。选择器是关键parse_plugin_list和parse_plugin_detail函数中的CSS选择器如a.plugin-card,div.example-prompt是完全假设的。在实际操作中你必须使用浏览器的开发者工具F12仔细分析目标网页的真实HTML结构找到正确的标签和类名来定位数据。这是网页抓取中最耗时、最需要耐心的部分。数据存储这里使用了SQLite轻量且无需额外服务。将示例提示词列表转为JSON字符串存储便于后续查询和解析。延迟与礼貌time.sleep(1)是基本的反反爬虫策略也是对目标服务器资源的尊重。3.3 处理动态加载内容现代网页大量使用JavaScript动态加载数据。如果上述方法获取的HTML中找不到数据很可能数据是通过API异步获取的。这时需要换用Playwright或Selenium。# 使用Playwright的示例片段 from playwright.sync_api import sync_playwright def scrape_with_playwright(url): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) # 无头模式 page browser.new_page() page.goto(url) # 等待特定元素出现确保数据加载完成 page.wait_for_selector(div.plugin-list, timeout10000) # 假设的选择器 html page.content() browser.close() return html # 然后用这个html替换上面fetch_page获取的html进行解析。实操心得优先尝试分析网络请求。打开开发者工具的“网络”(Network)选项卡刷新页面过滤XHR/Fetch请求往往能找到直接返回结构化JSON数据的API接口。直接调用这些API比解析HTML更稳定、更高效。但这需要一定的逆向工程能力并且API接口可能随时变更。4. 从数据到洞察分析与应用场景抓取到数据只是第一步如何从这些看似杂乱的提示词中提炼出有价值的信息才是项目的精髓。4.1 基础数据分析方法将数据从SQLite中导出后我们可以用Python的pandas、matplotlib或Jupyter Notebook进行快速分析。import sqlite3 import pandas as pd import json from collections import Counter import re # 连接数据库并加载数据 conn sqlite3.connect(plugins.db) df pd.read_sql_query(SELECT * FROM plugins, conn) conn.close() # 将存储的JSON字符串转换回列表 df[example_prompts_list] df[example_prompts].apply(json.loads) # 1. 基础统计 print(f总共抓取了 {len(df)} 个插件。) # 计算每个插件的平均示例提示词数量 df[prompt_count] df[example_prompts_list].apply(len) print(f平均每个插件有 {df[prompt_count].mean():.2f} 个示例提示。) # 2. 词频分析简单的关键词提取 all_prompts [] for prompts in df[example_prompts_list]: all_prompts.extend(prompts) # 将所有提示词合并成一个长文本 all_text .join(all_prompts) # 简单的分词这里按空格分中文需要更复杂的分词库如jieba words re.findall(r\b\w\b, all_text.lower()) # 提取单词并转为小写 # 过滤掉常见的停用词如 the, a, an, in, on, for... stop_words set([the, a, an, in, on, for, to, of, and, is, at]) filtered_words [w for w in words if w not in stop_words and len(w) 3] word_freq Counter(filtered_words).most_common(20) print(最常见的20个词汇) for word, freq in word_freq: print(f {word}: {freq}) # 3. 插件功能分类基于描述关键词 def categorize_by_description(desc): desc_lower desc.lower() if any(word in desc_lower for word in [search, find, query, data]): return 数据查询 elif any(word in desc_lower for word in [generate, write, create, content]): return 内容生成 elif any(word in desc_lower for word in [calculate, convert, translate, tool]): return 工具助手 else: return 其他 df[category] df[description].apply(categorize_by_description) category_counts df[category].value_counts() print(\n插件功能分类统计) print(category_counts)4.2 高级应用场景挖掘基础分析能给出一个概貌但深度价值在于针对性的挖掘。场景一竞品插件对比分析假设你想开发一个“旅行规划”插件。你可以筛选出所有描述中包含“travel”、“trip”、“flight”、“hotel”的插件仔细研究它们的示例提示词。用户是倾向于问“帮我规划一个去东京的5天行程”这样的开放式问题还是“查找下周五从北京飞往东京的最便宜航班”这样的具体指令提示词中是否普遍包含了预算、人数、时间等约束条件哪些插件的提示词设计得更自然、更像真人对话这能为你设计自己插件的“自然语言理解”逻辑提供参考。场景二提示词模式Pattern归纳这是对开发者最有用的部分。通过大量样本你可以总结出调用某类插件的“最佳实践”提示词结构。 例如对于“数据查询”类插件模式可能是“使用 [插件名] 查找/搜索 [具体查询内容]条件包括 [条件1]、[条件2]并按照 [排序方式] 返回结果。”而对于“内容生成”类插件模式可能更偏向“扮演一个 [角色]以 [风格] 写一篇关于 [主题] 的 [文章类型]要求包含 [要点1]、[要点2]字数大约在 [字数] 左右。”将这些模式整理成文档或代码模板能极大提升你设计插件交互逻辑的效率。场景三发现“未满足的需求”有时用户会在提示词中“抱怨”或提出超出插件当前能力的请求。例如一个天气插件下的用户提问“告诉我明天下午会不会下雨如果下雨顺便推荐几个室内的活动。” 后半句“推荐室内活动”可能超出了该插件的设计范围。大量收集这类“边界性”提示词能帮助你发现用户潜在的、未被现有插件满足的复合型需求这可能就是新产品的机会点。5. 常见问题、伦理考量与避坑指南在实际操作这类项目时你会遇到技术和非技术上的各种挑战。5.1 技术性挑战与解决方案问题可能原因解决方案与建议抓取不到数据1. 网页结构已更新CSS选择器失效。2. 数据由JavaScript动态加载。3. 网站有反爬虫机制如验证码、IP封锁。1.定期维护将选择器作为配置项便于更新。2.使用无头浏览器换用Playwright/Selenium。3.分析网络请求直接调用数据API。4.遵守robots.txt设置合理延迟和轮换User-Agent。数据质量差1. 示例提示词数量少或质量低。2. 抓取了大量无关文本导航栏、广告。1.多源抓取不局限于官方商店增加社区源。2.精细化解析结合多个HTML标签和属性来精确定位目标区域。3.后清洗编写更复杂的文本清洗规则如基于长度、关键词的过滤。解析速度慢1. 同步请求导致I/O等待。2. 页面元素复杂解析耗时。1.异步抓取使用asyncioaiohttp/httpx并发请求。2.增量抓取只抓取新增或更新的插件记录最后抓取时间。数据存储混乱不同来源的数据格式不统一。1.设计弹性Schema在数据库中使用JSON字段存储可变部分。2.ETL管道在存储前增加一个“转换(Transform)”步骤将不同源的数据映射到统一模型。5.2 法律、伦理与合规性这是比技术问题更重要的红线必须严肃对待。尊重版权与数据所有权抓取的数据尤其是社区用户创作的提示词可能包含版权内容。切勿将抓取的数据用于商业用途或大规模公开分发特别是未脱敏的原始数据。本项目应定位为个人研究、学习和非商业的探索工具。严格遵守服务条款仔细阅读目标网站如OpenAI插件商店、Reddit、Twitter等的服务条款。明确禁止抓取的行为坚决不做。对于有公开API的站点优先使用API。隐私保护绝对不要尝试抓取任何个人隐私数据或非公开对话。我们关注的是公开的、示例性的、非个人的提示词信息。设置明确的免责声明如果你公开分享你的抓取代码或分析结果务必附带清晰的免责声明说明该项目仅用于教育研究目的数据来源于公开网络并鼓励用户尊重原始数据源的版权和条款。5.3 我的实操心得与建议从小处着手快速验证不要一开始就想构建一个覆盖全网的庞大系统。先从抓取一个你最感兴趣的插件类别开始写一个简单的脚本看看能拿到什么数据数据质量如何。快速验证想法的可行性。数据清洗比抓取更耗时预计你会花60%以上的时间在数据清洗和标准化上。正则表达式是你的好朋友但也要学会适可而止不必追求100%的完美清洗对于研究分析80%的清洁度往往就够用了。关注“数据故事”而非“数据堆砌”收集了十万条提示词不是目的。目的是你能从中讲出什么故事比如“用户在使用日历插件时超过70%的请求都包含了‘会议’和‘安排’这两个词”这就是一个洞察。带着问题去分析数据。工具是辅助思维是核心scrape-chatgpt-plugin-prompts项目提供的是一种思路和工具雏形。真正的价值在于你如何利用这种思路去观察、理解并最终服务于你的产品设计、开发或研究工作。你可以将它改造成抓取其他AI产品提示词的工具或者与你自己的插件开发流程相结合。这个项目的魅力在于它处于一个快速变化的领域前沿。通过它你不仅是在学习数据抓取技术更是在近距离观察一场人机交互范式变革的早期细节。每一次抓取和分析都可能让你对如何构建更自然、更有用的AI工具有新的认识。