1. 项目概述一个技能打通三大内容平台最近在折腾AI Agent智能体的朋友估计都遇到过同一个头疼的问题想让Agent帮你查点东西尤其是查查小红书上的探店攻略、公众号里的深度文章或者知乎上的专业回答发现它根本“看”不到这些内容。你可能会尝试给Agent装各种网页访问插件但结果往往是权限不足、页面结构解析失败或者干脆返回一堆乱码。这感觉就像给了助手一双眼睛但它却看不懂中文世界的“墙”。这个痛点催生了一个非常具体的需求有没有一个统一的“技能”Skill能让Agent像我们一样顺畅地浏览并理解小红书、公众号和知乎这三个平台的内容答案是肯定的。今天要聊的就是如何通过集成一个强大的Web访问与解析Skill彻底解决这个问题。这个Skill的核心价值在于它不是一个简单的网页抓取工具而是一个具备平台感知能力、动态渲染支持和智能内容提取的中间层。对于任何需要构建能处理中文互联网信息的Agent开发者来说这几乎是必备的基础设施。简单来说这个Skill让Agent获得了在特定围墙花园内“合法浏览”的能力。它知道面对小红书时该如何模拟App请求获取完整的图文笔记面对公众号时如何绕过反爬机制抓取文章正文面对知乎时又如何处理动态加载的回答和评论。你不再需要为每个平台单独开发适配器一个技能全搞定。2. 核心需求与方案选型背后的逻辑为什么我们需要一个专门的Skill来做这件事直接调用通用的网页爬虫API不行吗这里面的门道得从这三个平台的技术特点说起。2.1 三大平台的“围墙”分析小红书内容呈现严重依赖客户端渲染。你在浏览器里直接打开一个小红书笔记链接很可能只能看到一个要求你打开App的页面或者一个内容不全的静态页。真正的图文、标签、用户信息都需要通过模拟App或特定接口请求才能拿到。通用爬虫在这里基本失效。微信公众号这是另一个反爬重灾区。公众号文章页的源码里正文内容通常不是直接可见的HTML而是被编码或通过JavaScript动态加载的。此外公众号有严格的频率限制和验证机制粗暴的抓取很容易触发风控导致IP被封。知乎知乎的网页端虽然相对友好但其问答、文章等内容也存在动态加载尤其是评论区和高赞回答。更复杂的是知乎对数据的结构化程度要求高你不仅需要拿到文本最好还能区分出问题、回答、作者、赞同数等字段。一个通用的fetch函数或基础爬虫库如requestsBeautifulSoup无法应对这些复杂情况。你需要的是一个能处理JavaScript渲染、模拟特定客户端行为、解析非标准HTML结构的解决方案。2.2 方案选型为什么是“Skill”而非“Plugin”在AI Agent的语境里“Skill”和“Plugin”插件概念有时会混用但侧重点不同。Plugin更偏向于提供一个功能接口而Skill则更强调一种“能力”或“技艺”它往往封装了更复杂的逻辑和决策过程。对于内容获取这个任务我们需要的正是一种“能力”Agent应该能根据用户给出的一个小红书链接自主判断“哦这是小红书我需要用对应的解析策略”然后执行一系列操作模拟请求、解析DOM、提取关键信息、格式化输出而不是仅仅调用一个简单的get_webpage(url)函数。因此一个成熟的Web Access Skill通常会包含以下模块平台识别器根据URL域名xiaohongshu.com, mp.weixin.qq.com, zhihu.com自动分派任务。请求引擎针对不同平台配置不同的HTTP头User-Agent、Cookie等、请求参数甚至使用无头浏览器如Playwright或Selenium进行渲染。内容解析器每个平台有独立的解析规则XPath、CSS选择器或正则表达式用于精准提取标题、正文、图片、作者、发布时间等字段。数据格式化器将提取的杂乱信息整理成Agent容易理解和使用的结构化数据如JSON。市面上有一些开源项目或工具包旨在解决这个问题例如名称中带有“Claw”抓取字样的工具它们通常集成了针对多个网站的解析规则。在选择具体技术方案时我们需要评估其维护状态、平台覆盖度以及是否易于集成到我们的Agent框架中。3. 技能核心Web访问与解析引擎的实现要实现“一个技能查三家”关键在于构建一个健壮且可扩展的Web访问与解析引擎。下面我以一个假设的、集成了Playwright和定制化解析规则的Skill为例拆解其核心实现思路。请注意以下代码为概念演示实际项目需要更完善的错误处理和配置管理。3.1 基础架构基于Playwright的无头浏览器对于小红书和部分复杂的公众号页面JavaScript渲染是绕不开的坎。Playwright是一个优秀的浏览器自动化库支持Chromium、Firefox和WebKit能完美模拟真实用户行为。# skill_web_access/core.py import asyncio from typing import Dict, Any, Optional from playwright.async_api import async_playwright, Browser, Page import re class WebAccessEngine: def __init__(self): self.browser: Optional[Browser] None self.playwright None # 平台解析器映射 self.parsers { xiaohongshu: self._parse_xiaohongshu, weixin: self._parse_weixin, zhihu: self._parse_zhihu, default: self._parse_generic } # 平台特定的请求配置 self.platform_headers { xiaohongshu: {User-Agent: Mozilla/5.0 (iPhone; ...)}, # 模拟移动端 weixin: {User-Agent: Mozilla/5.0 (Windows NT ...)}, zhihu: {User-Agent: Mozilla/5.0 (Macintosh; ...)} } async def start(self): 启动Playwright浏览器实例 self.playwright await async_playwright().start() # 使用Chromium可配置为 headlessFalse 用于调试 self.browser await self.playwright.chromium.launch(headlessTrue) async def fetch_content(self, url: str) - Dict[str, Any]: 主方法获取并解析URL内容 if not self.browser: await self.start() platform self._identify_platform(url) page await self.browser.new_page() # 设置平台特定的请求头 headers self.platform_headers.get(platform, {}) if headers: await page.set_extra_http_headers(headers) try: # 导航到页面等待网络空闲或特定元素出现 await page.goto(url, wait_untilnetworkidle, timeout30000) # 针对某些平台可能需要额外滚动或等待 await self._platform_specific_wait(page, platform) # 获取页面HTML html_content await page.content() # 调用对应的解析器 parser self.parsers.get(platform, self.parsers[default]) parsed_data await parser(html_content, page) return { success: True, platform: platform, url: url, data: parsed_data } except Exception as e: return { success: False, platform: platform, url: url, error: str(e) } finally: await page.close() def _identify_platform(self, url: str) - str: 根据URL识别平台 if xiaohongshu in url: return xiaohongshu elif mp.weixin.qq.com in url: return weixin elif zhihu.com in url: return zhihu else: return default3.2 平台专属解析器的实现要点每个平台的解析器是技能的核心“手艺”。这里以公众号为例展示其复杂性。# skill_web_access/parsers.py from bs4 import BeautifulSoup import json import re class WeixinParser: staticmethod async def parse(html: str, page) - Dict[str, Any]: 解析微信公众号文章。 公众号的正文可能藏在特定的JavaScript变量或特定的HTML结构中。 soup BeautifulSoup(html, html.parser) result {title: , author: , publish_time: , content: } # 方法1尝试从页面内嵌的JSON数据中提取 script_tags soup.find_all(script) for script in script_tags: if script.string and window.msg_cdn_url in script.string: # 使用正则表达式提取JSON数据 json_match re.search(rwindow\.msg_cdn_url\s*\s*([^]), script.string) # 这里简化处理实际需要解析更复杂的结构来获取正文 pass # 方法2通过特定的CSS选择器获取如果页面结构稳定 # 公众号正文通常在一个id为js_content的div里 content_div soup.find(div, idjs_content) if content_div: # 清理内容移除不必要的标签和属性 for elem in content_div.find_all([script, style, iframe]): elem.decompose() # 获取纯文本或保留部分格式的文本 result[content] content_div.get_text(stripTrue, separator\n) # 获取标题和作者 title_meta soup.find(meta, propertyog:title) author_meta soup.find(meta, propertyog:article:author) result[title] title_meta[content] if title_meta else soup.title.string if soup.title else result[author] author_meta[content] if author_meta else # 有时发布时间在特定的meta标签或div中 time_meta soup.find(meta, propertyog:article:published_time) if not time_meta: # 尝试其他选择器 time_div soup.find(div, class_re.compile(publish_time)) if time_div: result[publish_time] time_div.get_text(stripTrue) else: result[publish_time] time_meta[content] return result注意公众号的解析策略需要持续更新因为腾讯会不时调整其页面结构。一个更稳健的方法是结合多种选择器并准备一个降级方案比如当无法精确提取时至少返回整个文章区域的主要文本。对于小红书解析重点在于获取笔记正文、图片和标签。小红书的页面数据通常通过接口获取直接解析HTML可能只能得到骨架。更高级的做法是拦截页面发出的网络请求找到包含笔记数据的API响应。这需要在Playwright中监听网络请求。# 在小红书解析器中可以尝试监听请求 async def _parse_xiaohongshu(self, html: str, page): # 初始化数据容器 note_data {desc: , images: [], tags: []} # 监听响应寻找包含笔记数据的接口 def handle_response(response): if /api/sns/web/v1/note in response.url: try: data response.json() # 从data中提取描述、图片列表等 note_data[desc] data.get(data, {}).get(note, {}).get(desc, ) # ... 其他字段提取 except: pass page.on(response, handle_response) # 重新加载页面或触发某些动作以让页面发出请求 await page.reload(wait_untilnetworkidle) # 等待一段时间确保请求被捕获 await asyncio.sleep(2) # 如果通过接口获取失败降级到HTML解析 if not note_data[desc]: soup BeautifulSoup(html, html.parser) # 使用CSS选择器尝试从HTML中提取可能不完整 # ... return note_data4. 集成到AI Agent框架以Claude Code为例有了Web访问引擎下一步就是让它成为AI Agent的一个“Skill”。不同的Agent框架如LangChain、AutoGen、Claude Code等集成方式略有不同但核心思想一致将引擎封装成一个可供Agent调用的工具函数。4.1 创建Skill函数假设我们使用一个支持函数调用Function Calling的Agent框架。我们需要定义一个清晰的函数规范描述这个Skill的能力、输入和输出。# skill_web_access/skill.py from .core import WebAccessEngine import json engine WebAccessEngine() # 定义Skill的函数描述用于让Agent理解何时调用它 WEB_FETCH_SKILL { name: fetch_web_content, description: 获取指定URL的网页内容并智能解析小红书、微信公众号、知乎等平台的结构化信息。适用于需要查阅网络文章、笔记、问答的场景。, parameters: { type: object, properties: { url: { type: string, description: 需要获取内容的网页链接必须是完整的URL以http://或https://开头。 }, extract_only: { type: boolean, description: 是否仅提取核心内容如正文忽略评论、侧边栏等。默认为True。, default: True } }, required: [url] } } # Skill的具体实现函数 async def fetch_web_content(url: str, extract_only: bool True) - str: Agent可调用的技能函数。 返回一个格式化的字符串便于Agent阅读和理解。 result await engine.fetch_content(url) if not result[success]: return f获取网页内容失败。平台{result[platform]}错误{result[error]} data result[data] platform result[platform] # 根据平台和提取要求格式化输出 output_lines [f【来源{platform}】{result[url]}] if platform weixin: output_lines.append(f标题{data.get(title, N/A)}) output_lines.append(f作者{data.get(author, N/A)}) output_lines.append(f发布时间{data.get(publish_time, N/A)}) output_lines.append(--- 正文摘要 ---) # 如果仅提取核心可以截取前N个字符作为摘要 content data.get(content, ) if extract_only and len(content) 500: content content[:500] ...内容已截断 output_lines.append(content) elif platform xiaohongshu: output_lines.append(f笔记描述{data.get(desc, N/A)}) if data.get(images): output_lines.append(f包含 {len(data[images])} 张图片。) if data.get(tags): output_lines.append(f标签{, .join(data[tags])}) elif platform zhihu: output_lines.append(f问题{data.get(question, N/A)}) output_lines.append(f回答者{data.get(author, N/A)}) output_lines.append(f赞同数{data.get(voteup_count, N/A)}) output_lines.append(--- 回答内容 ---) content data.get(content, ) if extract_only and len(content) 600: content content[:600] ... output_lines.append(content) else: output_lines.append(f标题{data.get(title, soup.title.string if soup in locals() else N/A)}) output_lines.append(--- 页面主要内容 ---) # 通用页面的简单提取 content data.get(content, ) if extract_only and len(content) 800: content content[:800] ... output_lines.append(content) return \n.join(output_lines)4.2 在Claude Code中注册与使用在Claude Code或类似环境中你需要将这个技能注册为Agent可用的工具。具体步骤取决于框架但通常包括导入技能函数将fetch_web_content函数引入到你的Agent工作区。注册工具在初始化Agent时将WEB_FETCH_SKILL函数描述和fetch_web_content实现函数作为工具提供给Agent。Agent调用当用户提出“帮我查查小红书上的某款咖啡机评测”时Agent会自主判断需要调用fetch_web_content技能并传入对应的URL。# 示例在Claude Code环境中配置Agent from claude_code import Agent, Tool from skill_web_access.skill import fetch_web_content, WEB_FETCH_SKILL # 创建工具对象 web_fetch_tool Tool.from_function( funcfetch_web_content, descriptionWEB_FETCH_SKILL[description], nameWEB_FETCH_SKILL[name], parametersWEB_FETCH_SKILL[parameters] ) # 创建Agent并赋予工具 agent Agent( nameResearch_Assistant, instructions你是一个研究助手擅长从网上查找并总结信息。当你需要获取网页内容时请使用提供的工具。, tools[web_fetch_tool] ) # 现在Agent在对话中就能使用这个技能了 # 用户”给我总结一下这篇文章https://mp.weixin.qq.com/s/xxxxx“ # Agent会自动调用 fetch_web_content(urlhttps://...) 并基于返回的内容进行总结。5. 部署、优化与避坑指南将这样一个Skill投入生产环境会面临稳定性、性能和合规性挑战。以下是一些实战经验总结。5.1 部署考量容器化与资源管理由于依赖Playwright这样的无头浏览器该Skill对运行环境有一定要求。Docker容器化是最佳实践。# Dockerfile FROM mcr.microsoft.com/playwright/python:v1.40.0-noble WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 安装Playwright所需的浏览器 RUN playwright install chromium CMD [python, your_agent_main.py]提示在云服务器或容器中运行无头浏览器时确保分配足够的内存至少1GB否则在加载复杂页面时容易崩溃。另外考虑使用--shm-size参数增加Docker容器的共享内存这对Playwright的稳定性很重要。5.2 性能优化与缓存策略频繁启动浏览器和访问外部网站速度慢且不友好。必须实施缓存和并发控制。请求缓存对相同的URL在一定时间如5分钟内直接返回缓存结果。可以使用redis或diskcache。from diskcache import Cache cache Cache(./web_cache) cache.memoize(expire300) # 缓存5分钟 async def cached_fetch_content(url): return await engine.fetch_content(url)并发池限制限制同时打开的浏览器页面数避免资源耗尽。可以使用信号量asyncio.Semaphore。class WebAccessEngine: def __init__(self, max_concurrent3): self.semaphore asyncio.Semaphore(max_concurrent) async def fetch_content(self, url): async with self.semaphore: # 控制并发 # ... 原有的fetch逻辑超时与重试网络请求必须设置合理的超时如30秒并对可重试的错误如网络波动实现指数退避重试机制。5.3 合规与伦理红线这是最重要的一部分。在开发和使用此类技能时必须严格遵守尊重robots.txt在访问任何网站前检查其robots.txt文件遵守禁止爬取的规则。控制访问频率对同一域名实施严格的请求间隔如每秒1-2次避免对目标服务器造成压力。这既是道德要求也能有效防止IP被封。仅用于合法目的该技能应用于信息聚合、个人研究、内容摘要等合法场景。绝对禁止用于批量盗取内容、恶意抓取、绕过付费墙或进行任何可能违反平台服务条款的行为。用户透明如果Agent使用了该技能获取信息应在回复中明确注明来源例如“根据小红书用户XXX的笔记...”或“根据微信公众号‘YYY’的文章...”。5.4 常见问题排查FAQQ 技能返回“页面加载超时”或空白内容。A 首先检查目标URL是否可正常访问。其次可能是页面加载过慢或依赖的某些资源如特定字体、第三方脚本阻塞。尝试增加wait_until的超时时间或将networkidle改为domcontentloaded。对于极度动态的页面可能需要等待特定元素出现await page.wait_for_selector(.content-area, timeout10000)。Q 解析公众号成功了但拿到的正文是乱码或不全。A 公众号文章可能使用了图片替代文字防爬手段。此时仅靠HTML解析不够。可以尝试1) 使用Playwright对页面进行截图然后使用OCR库如pytesseract识别文字但这很重。2) 寻找未被图片化的摘要部分。更务实的做法是接受这种限制并在结果中标注“部分内容可能因平台防护措施无法获取”。Q 运行一段时间后IP被小红书/知乎封禁了。A 立即停止请求并检查你的访问频率是否过高。考虑1) 使用代理IP池轮换请求。2) 大幅降低请求频率模拟人类浏览的随机间隔。3) 检查请求头是否完整模拟了普通浏览器特别是User-Agent,Accept-Language,Referer等。Q 技能在Docker中运行报错提示找不到浏览器。A 确保Docker镜像基于Playwright官方镜像如mcr.microsoft.com/playwright/python它包含了所有依赖。如果使用自定义镜像务必运行playwright install chromium安装浏览器二进制文件。6. 技能扩展与未来展望一个基础的Web访问技能搭建完成后你可以根据需求对其进行增强支持更多平台同样的架构可以轻松扩展支持微博、豆瓣、B站专栏、新闻网站等。只需为新增平台编写对应的_identify_platform逻辑和解析器即可。内容总结与摘要在获取原始内容后可以集成一个文本摘要模型如本地运行的ChatGLM、Qwen或调用大模型API让Agent不仅“看到”内容还能自动提炼要点。这需要将Skill升级为多步骤工作流。信息结构化存储将抓取到的文章、笔记存入向量数据库如ChromaDB,Milvus让Agent具备长期记忆和检索能力实现“上次你帮我查过的那篇关于咖啡机的文章”这样的对话。身份模拟与登录对于需要登录才能查看的内容如某些知乎回答技能可以集成安全的凭证管理在合规的前提下模拟登录状态。但此功能涉及敏感信息需极其谨慎并确保用户知情同意。我个人在多个Agent项目中集成此类技能的经验是稳定性远比功能丰富度重要。初期不必追求完美解析所有细节优先保证核心功能获取标题和正文在90%的情况下稳定工作。然后通过日志密切监控失败案例逐步优化解析策略。这个技能一旦调校稳定将成为你Agent工具箱里最常用、也最提升效率的利器之一。它真正打破了AI与鲜活中文互联网信息之间的那层隔阂。