自定义 Recipe
Aneiang.Pa 支持四种 Recipe 来源,新增平台无需修改框架代码:
| 方式 | 适用场景 | 写法 |
|---|---|---|
| 内置 YAML | 开箱即用 | 20 个平台已嵌入 dll |
| 外部 YAML/JSON | 配置外置,社区共享 | Pa.Configure(c => c.UseRecipesFolder(...)) |
| Builder DSL | 动态构造、运行时定义 | Pa.Define("Name", b => b.Get(...).ParseHtml(...)) |
| 特性标注 | 强类型、IDE 友好 | [Recipe][Get][Container][Selector] 标注类 |
YAML Recipe
最推荐的方式。写一份 .yaml 文件即可新增平台:
# recipes/news/cnbeta.yaml
name: CnBeta
category: News
display_name: CnBeta 头条
fetch:
url: https://www.cnbeta.com.tw/
headers:
User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
parse:
type: html
container: ".items-area .item"
fields:
title: { selector: "h2 a", trim: true }
url: { selector: "h2 a", attr: href }
desc: { selector: ".desc" }
加载并使用:
Pa.Configure(c => c.UseRecipesFolder("./recipes"));
var data = await Pa.Source("CnBeta").GetAsync();
YAML 字段说明
| 字段 | 说明 | 必填 |
|---|---|---|
name | 数据源唯一标识 | 是 |
category | 分类(News / Tech / Lottery 等) | 是 |
display_name | 显示名称 | 是 |
fetch.url | 请求地址 | 是 |
fetch.headers | 自定义请求头 | 否 |
fetch.method | HTTP 方法(默认 GET) | 否 |
parse.type | 解析类型(html / json) | 是 |
parse.container | 容器选择器 | 是 |
parse.fields | 字段映射 | 是 |
JSON API Recipe
如果目标站点返回 JSON:
name: HackerNews
category: News
display_name: Hacker News
fetch:
url: https://hn.algolia.com/api/v1/search?tags=front_page
parse:
type: json
items: "$.hits"
fields:
title: "$.title"
url: "$.url"
points: "$.points"
变量占位
Recipe 中可以使用 {variable} 占位,由中间件动态填充:
name: MyAPI
category: Custom
display_name: 需要 Token 的 API
fetch:
url: https://api.example.com/items?token={token}
headers:
Authorization: "Bearer {token}"
Builder DSL
在 C# 代码中动态构造 Recipe:
Pa.Define("MyAPI", b => b
.Category("Custom")
.DisplayName("我的 API")
.Get("https://api.example.com/items?page={pageNo}")
.Header("Authorization", "Bearer xxx")
.ParseJson(p => p
.Items("$.data")
.Field("id", "id")
.Field("title", "title")
.Field("url", "url")));
var data = await Pa.Source("MyAPI").WithPaging(1, 30).GetAsync();
Builder DSL 解析 HTML
Pa.Define("MyHtml", b => b
.Category("News")
.DisplayName("自定义 HTML")
.Get("https://example.com/news")
.ParseHtml(p => p
.Container(".news-item")
.Field("title", f => f.Selector("h2 a").Trim())
.Field("url", f => f.Selector("h2 a").Attr("href"))
.Field("date", f => f.Selector(".date"))));
特性标注
用 C# 特性直接标注 POCO 类,适合强类型场景:
[Recipe("MyTrending", Category = "Tech")]
[Get("https://github.com/trending")]
[Container("article.Box-row")]
public class Repo
{
[Selector("h2 a")] public string? Title { get; set; }
[Selector("h2 a", Attr = "href", Base = "https://github.com")] public string? Url { get; set; }
[Selector("span[itemprop=programmingLanguage]")] public string? Lang { get; set; }
}
Pa.DiscoverFromLoadedAssemblies();
var data = await Pa.Source("MyTrending").GetAsync<Repo>();
贡献 Recipe
只需 3 步:
- 在
recipes/目录新建.yaml文件 - 填写
name/fetch/parse三个部分 - 提交 PR
新增平台不需要写 C# 代码 —— 提交一份 YAML 文件就能让全世界的 .NET 用户用上你贡献的平台。