☰
openJiuwen WebPageParser 网页解析器:HTTP 抓取、文本提取与 metadata 输出全解析
2026/10/15 16:41:08 网站建设 项目流程
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

本文围绕 openJiuwen(agent-core-java)检索索引模块中的WebPageParser展开,系统讲解普通网页 URL 的抓取与可读文本提取实现,包括类继承与构造方式、URL 校验规则、微信公众号链接的排除逻辑、<article>/<body>提取策略、metadata 输出以及测试验证。读完本文,你将能够直接使用WebPageParser将网页 URL 解析为可用于知识库索引的Document,并理解它与WeChatArticleParser、AutoLinkParser之间的路由协作关系。

一、WebPageParser 在解析器体系中的定位

openJiuwen 的检索模块(retrieval子模块)在建立知识库索引前,需要先把各类原始输入(网页链接、本地文件)解析为统一的Document文本对象。这一职责由retrieval/src/main/java/com/openjiuwen/retrieval/indexing/processor/parser包下的解析器家族承担:

  • WebPageParser:负责抓取普通网页 URL并提取可读文本;
  • WeChatArticleParser:专门处理微信公众号文章链接(mp.weixin.qq.com),是WebPageParser的子类;
  • HTMLFileParser:解析本地.html/.htm文件,同时是WebPageParser的直接父类,承载了 HTML 文本提取的核心逻辑;
  • AutoLinkParser/AutoParser:按 URL 特征将输入路由到具体解析器。

其中WebPageParser位于 WebPageParser.java,其 Javadoc 明确说明它是 Python 版openjiuwen/core/retrieval/indexing/processor/parser/web_page_parser.py中同名类的 Java 移植实现。它只处理 HTTP/HTTPS 网页,不处理微信公众号文章链接——遇到此类链接会直接抛错并提示改用WeChatArticleParser。

二、类继承关系与构造方法

2.1 继承链

文档中类声明为public class WebPageParser extends Parser。在源码中它的直接父类实际上是HTMLFileParser:

public class WebPageParser extends HTMLFileParser

而HTMLFileParser继承自抽象基类Parser(Parser.java),因此WebPageParser与Parser是间接继承关系。整个继承链为:

Parser(抽象基类,实现 Processor<List<Document>>) └── HTMLFileParser(HTML 文本提取核心逻辑) └── WebPageParser(网页 URL 抓取) └── WeChatArticleParser(微信公众号文章)

Parser基类提供了parse(...)系列同步入口和parseAsync(...)异步接口,并定义了supports(String doc)判定方法与process(Object... args)处理入口。

2.2 构造方法

WebPageParser提供多个构造入口(WebPageParser.java):

构造方法说明
public WebPageParser()默认构造,使用默认超时DEFAULT_TIMEOUT = 30.0秒、默认 User-Agent,并基于此创建HttpClient
public WebPageParser(double timeout, String userAgent)指定请求超时(秒)与 User-Agent,内部创建HttpClient,连接超时取max(1, round(timeout))秒
public WebPageParser(HttpClient httpClient)注入自定义HttpClient(此构造定义于父类HTMLFileParser体系外,实际由子类化使用时提供)
protected WebPageParser(double timeout, String userAgent, HttpClient httpClient)完全自定义版本,供子类(如WeChatArticleParser)使用

关键默认常量定义在父类 HTMLFileParser.java:

public static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; public static final double DEFAULT_TIMEOUT = 30.0d;

默认构造创建的HttpClient使用 JDK 自带的java.net.http.HttpClient,连接超时同样为 30 秒(Duration.ofSeconds(Math.max(1L, Math.round(timeout))))。注意超时值最小会被钳制到 1 秒,避免非法的零值或负值。

三、URL 判定规则:supports 方法

WebPageParser.supports(String doc)决定该解析器是否接受某个输入(WebPageParser.java):

@Override public boolean supports(String doc) { if (doc == null || !HTTP_URL_PATTERN.matcher(doc.strip()).find()) { return false; } return !isWechatArticleUrl(doc); }

规则可归纳为两条:

  1. 必须是 HTTP/HTTPS 链接:使用HTTP_URL_PATTERN = ^https?://\S+(CASE_INSENSITIVE),即必须以http://或https://开头且后面紧跟非空白字符;
  2. 必须排除微信公众号文章链接:使用WECHAT_MP_URL_PATTERN识别mp.weixin.qq.com及其子域下的/s路径文章。

对应的 URL 正则定义(WebPageParser.java):

public static final Pattern HTTP_URL_PATTERN = Pattern.compile("^https?://\\S+", Pattern.CASE_INSENSITIVE); public static final Pattern WECHAT_MP_URL_PATTERN = Pattern.compile( "^https?://(?:mp\\.weixin\\.qq\\.com|.*?\\.weixin\\.qq\\.com)/s\\b.*", Pattern.CASE_INSENSITIVE );

isWechatArticleUrl(String url)是公开静态方法,可直接调用。测试用例 WebPageParserTest.java 验证了以下边界行为:

  • https://example.com/path、HTTP://X.Y/z(大小写不敏感)→ 判定为 HTTP URL;
  • ftp://example.com、not-a-url→ 判定为非法;
  • https://mp.weixin.qq.com/s/abc123、http://foo.weixin.qq.com/s?x=1→ 判定为微信文章;
  • https://example.com/article→ 不是微信文章;
  • 空字符串、纯空白、null输入 →supports均返回false。

四、parse 流程:校验、抓取、解析

4.1 主流程

parse的异步实现parseAsync(WebPageParser.java)会从options中读取可覆盖配置:

double effectiveTimeout = optionAsDouble(safeOptions, "timeout", timeout); String effectiveUserAgent = optionAsString(safeOptions, "user_agent", optionAsString(safeOptions, "userAgent", userAgent));

然后调用核心方法parseUrl(url, docId, effectiveTimeout, effectiveUserAgent),内部执行三步流水线(WebPageParser.java):

validateUrl(url); String effectiveId = docId == null || docId.isBlank() ? url : docId; return downloadHtml(url, requestTimeout, requestUserAgent) .thenCompose(html -> HTMLFileParser.parseHtml(html, effectiveId, url)) .thenApply(documents -> { /* 写入 source_url metadata */ });

第一步:validateUrl校验。静态方法validateUrl(WebPageParser.java)执行两项检查:

  • 非 HTTP/HTTPS 或空白 URL → 抛出"Not a valid HTTP URL: '<url>'";
  • 微信文章 URL → 抛出"Use WeChatArticleParser for WeChat URLs"。

第二步:downloadHtml抓取。使用注入的HttpClient.sendAsync发起GET请求(WebPageParser.java):

  • 请求超时与User-Agent均可在构造或options中覆盖;
  • HTTP 状态码>= 400视为失败,抛出"Web page request failed: <status> for <url>";
  • 请求构建或发送异常统一包装为fetchError返回失败CompletableFuture。

第三步:HTMLFileParser.parseHtml解析。将抓取到的 HTML 字符串交给父类的静态解析逻辑,产出Document列表(通常为 1 个),随后补充source_url元数据并打印解析日志。

4.2 docId 的默认行为

parse系列方法中,若docId为空或空白,会默认使用 URL 本身作为文档 ID:

String effectiveId = docId == null || docId.isBlank() ? url : docId;

测试用例testParseDefaultsDocumentIdToUrlAndUsesOptions明确断言了该行为,并同时验证了options中的timeout(5.5秒)与user_agent("CustomAgent")能正确传递到抓取层。

五、文本提取规则:优先 article,退回 body

WebPageParser抓取到 HTML 后,真正的文本提取逻辑由父类HTMLFileParser完成(HTMLFileParser.java),这也是文档所述"优先提取<article>,否则退回<body>"的底层实现,实际规则更细致:

  1. 标题提取:优先取<meta property="og:title">的content属性,其次取<title>标签文本,两者皆空时 metadata 中标题记为(无标题);
  2. 主内容定位:按MAIN_CONTENT_SELECTORS选择器列表依序尝试:
    List.of("article", "main", "[role=\"main\"]", ".article-body", ".post-content", ".content", ".entry-content", ".post-body", "#content", ".main-content")

    选中节点的文本长度需超过 100 字符才会被采用;若都不满足,则在<body>下遍历article / main / div / section标签,寻找文本长度超过 200 的节点;最终仍无匹配则退回整个<body>;

  3. 文本清洗:getTextFromElement会对选中节点做克隆,然后:
    • 剥离script、style标签;
    • 按文本节点逐行拼接,[ \t]+压缩为单个空格,多个换行压缩为\n\n;
    • 最终trim()去除首尾空白;
  4. 内容质检:解析出的纯文本为空或长度小于 50 字符时,抛出"Article content too short or empty after parsing html",防止索引无意义内容。

六、输出 metadata 与 Document

解析完成后生成的每个Document都携带结构化 metadata。测试testParseReturnsDocumentWithMetadata(WebPageParserTest.java)断言了以下三个字段:

metadata 键值来源
source_url传入的网页 URLparseUrl在解析后统一写入(见 WebPageParser.java)
titleog:title或<title>提取结果,空则(无标题)父类HTMLFileParser.extractTitle
source_type"web_page"父类parseHtmlToDocuments(HTMLFileParser.java)

这三个字段的语义分别用于记录网页来源、展示标题与标识文档类型,是后续索引与检索环节筛选文档的重要依据。对比来看,WeChatArticleParser产出的source_type为"wechat_article",正文节点则固定提取div#js_content(WeChatArticleParser.java)。

七、静态便捷方法:parseWebPageUrl

除实例方法外,WebPageParser还提供模块级便捷入口(对应 Python 版的parse_web_page_url别名):

public static CompletableFuture<List<Document>> parseWebPageUrl(String url) public static CompletableFuture<List<Document>> parseWebPageUrl(String url, String docId)

内部实现即new WebPageParser().parseUrl(url, docId)(WebPageParser.java)。这意味着当不需要自定义超时、User-Agent 或 HttpClient 时,可以一行代码完成网页解析,返回值是与parse一致的可等待CompletableFuture<List<Document>>。

八、options 参数一览

parse(doc, docId, llmClient, options)的options映射支持以下键(见 WebPageParser.java):

键类型默认值作用
timeout数字或数字字符串构造时传入的timeout(默认 30.0 秒)覆盖 HTTP 请求超时;解析失败时回退默认值
user_agent字符串构造时传入的userAgent(默认 Chrome UA)覆盖请求头User-Agent
userAgent字符串同上与user_agent等价的驼峰写法,优先级低于user_agent

参数解析辅助方法optionAsDouble与optionAsString位于同文件底部:数字直接取doubleValue(),字符串会尝试Double.parseDouble,解析失败时静默回退默认值。

九、错误处理:统一包装为 RETRIEVAL_INDEXING_FETCH_ERROR

WebPageParser的所有抓取与解析失败最终都会通过fetchError(...)包装为框架统一异常(WebPageParser.java),使用StatusCode.RETRIEVAL_INDEXING_FETCH_ERROR并携带error_msg详情。典型失败场景包括:

  • 非法 URL(非 HTTP/HTTPS);
  • 微信文章 URL 误用本解析器;
  • HTTP 状态码 ≥ 400;
  • 请求构建或网络异常;
  • HTML 内容过短或主内容提取失败。

测试用例验证:testParseWechatUrlRaises断言微信 URL 抛出的异常status正是RETRIEVAL_INDEXING_FETCH_ERROR;testInvalidUrlRaisesFetchError断言错误消息包含"Not a valid HTTP URL";testDownloadFailureCompletesWithFetchError通过子类覆写downloadHtml模拟抓取失败,确认失败会以CompletionException(cause 为BaseError)形式传播。

十、与微信解析器及自动路由的协作

10.1 分工:WebPageParser vs WeChatArticleParser

WeChatArticleParser继承自WebPageParser(WeChatArticleParser.java),复用其抓取能力,但覆盖了三处关键行为:

  • supports只接受微信文章 URL(isWechatArticleUrl判定);
  • 正文提取固定使用div#js_content节点;
  • source_type输出为"wechat_article"而非"web_page"。

两者的validateUrl互斥:WebPageParser拒绝微信 URL(提示改用WeChatArticleParser),WeChatArticleParser则只接受微信 URL。

10.2 自动路由:AutoLinkParser 与 AutoParser

在索引管线中,通常无需手动选择解析器,而是使用自动路由。AutoLinkParser(AutoLinkParser.java)内置两条默认路由(按顺序匹配,微信路由优先):

Route.lazyPattern(WECHAT_MP_URL_PATTERN, WECHAT_ARTICLE_PARSER_CLASS), Route.lazyPattern(HTTP_URL_PATTERN, WEB_PAGE_PARSER_CLASS)

匹配到的路由通过反射懒加载对应解析器实例并委托执行。AutoParser则在上层统一判断输入是 URL 还是本地文件路径,URL 交给AutoLinkParser,文件路径交给AutoFileParser(AutoParser.java)。对应的路由测试位于 AutoLinkParserTest.java 与 AutoParserTest.java。

因此,面向知识库索引的完整调用链可以概括为:AutoParser→AutoLinkParser→(微信 URL 走WeChatArticleParser,其余 HTTP/HTTPS 走WebPageParser)→HTMLFileParser.parseHtml→Document(含source_url/title/source_typemetadata),解析产物随后进入 chunker 切分与 indexer 建索引阶段。

十一、快速使用示例

结合上述 API,一个最小可用的网页解析调用如下:

import com.openjiuwen.core.retrieval.common.Document; import com.openjiuwen.retrieval.indexing.processor.parser.WebPageParser; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; public class WebPageParseDemo { // 方式一:静态便捷方法,默认 30 秒超时与 Chrome UA public static CompletableFuture<List<Document>> parseByStatic(String url) { return WebPageParser.parseWebPageUrl(url); } // 方式二:自定义 options 覆盖超时与 UA public static CompletableFuture<List<Document>> parseWithOptions(String url) { WebPageParser parser = new WebPageParser(); return parser.parse(url, "web_doc_001", null, Map.of("timeout", 10.0, "user_agent", "MyCrawler/1.0")); } }

返回的Document可通过getText()获取清洗后的正文,通过getMetadata()读取source_url、title、source_type,作为后续切分、向量化与索引入库的输入。

十二、小结

WebPageParser是 openJiuwen 网页类知识摄入的关键一环:它以正则校验锁定 HTTP/HTTPS 输入、以HttpClient异步完成抓取、以HTMLFileParser的article → body降级策略与空白压缩规则产出干净正文,最终统一输出携带source_url/title/source_type = "web_page"的Document。它与WeChatArticleParser的职责互斥分工、与AutoLinkParser的自动路由协作,共同构成了"链接进来、文本出去"的标准化解析入口,为知识库构建与检索提供了可靠的上游保障。相关实现与测试均可在retrieval子模块中进一步查阅:

  • 实现:WebPageParser.java、HTMLFileParser.java、Parser.java
  • 协作:WeChatArticleParser.java、AutoLinkParser.java、AutoParser.java
  • 测试:WebPageParserTest.java、AutoLinkParserTest.java、AutoParserTest.java
  • 人工智能
  • AI Agent
  • Agent 框架
  • 工具调用
  • Agent 工作流
  • RAG
  • MCP Clients
  • 强化学习

【免费下载链接】agent-core-java

openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。

项目地址:https://gitcode.com/openJiuwen/agent-core-java
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询