3 个不稳定版本

0.2.0 2020年9月23日
0.1.1 2020年2月16日
0.1.0 2020年2月11日

#1819 in 网页编程

Download history 57/week @ 2024-03-15 82/week @ 2024-03-22 137/week @ 2024-03-29 77/week @ 2024-04-05 53/week @ 2024-04-12 64/week @ 2024-04-19 49/week @ 2024-04-26 41/week @ 2024-05-03 44/week @ 2024-05-10 65/week @ 2024-05-17 43/week @ 2024-05-24 52/week @ 2024-05-31 34/week @ 2024-06-07 45/week @ 2024-06-14 63/week @ 2024-06-21 21/week @ 2024-06-28

171 每月下载量
用于 5 个 crates (4 直接)

MIT 许可证

32KB
612

Workflow Status

easy-scraper

专注于易用的 HTML 提取库。

在这个库中,匹配模式被描述为 HTML DOM 树。您可以编写直观的模式并轻松提取所需内容。

示例

use easy_scraper::Pattern;

let doc = r#"
<!DOCTYPE html>
<html lang="en">
    <body>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
        </ul>
    </body>
</html>
"#;

let pat = Pattern::new(r#"
<ul>
    <li>{{foo}}</li>
</ul>
"#).unwrap();

let ms = pat.matches(doc);

assert_eq!(ms.len(), 3);
assert_eq!(ms[0]["foo"], "1");
assert_eq!(ms[1]["foo"], "2");
assert_eq!(ms[2]["foo"], "3");

语法

DOM 树

DOM 树是有效的模式。您可以在 DOM 树中编写占位符。

<ul>
    <li>{{foo}}</li>
</ul>

如果模式是文档的子集,则匹配模式。

如果文档是

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

这些树是它的子集。

<ul>
    <li>1</li>
</ul>
<ul>
    <li>2</li>
</ul>
<ul>
    <li>3</li>
</ul>

因此,匹配结果是

[
    { "foo": "1" },
    { "foo": "2" },
    { "foo": "3" },
]

子节点

由于子集规则,子节点与任何后代匹配。

例如,这个模式

<div>
    <li>{{id}}</li>
</div>

与这个文档匹配。

<div>
    <ul>
        <li>1</li>
    </ul>
</div>

兄弟节点

为了避免无用的匹配,兄弟节点仅限于匹配同一父节点的连续子节点。

例如,这个模式

<ul>
    <li>{{foo}}</li>
    <li>{{bar}}</li>
</ul>

不匹配此文档。

<ul>
    <li>123</li>
    <div>
        <li>456</li>
    </div>
</ul>

对于此文档,

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

匹配结果是

[
    { "foo": "1", "bar": "2" },
    { "foo": "2", "bar": "3" },
]

{ "foo": 1, "bar": 3 } 不包含,因为没有连续的子节点。

您可以在模式中写入 ... 来指定兄弟节点之间的允许节点。

<ul>
    <li>{{foo}}</li>
    ...
    <li>{{bar}}</li>
</ul>

此模式的匹配结果是

[
    { "foo": "1", "bar": "2" },
    { "foo": "1", "bar": "3" },
    { "foo": "2", "bar": "3" },
]

如果您想将兄弟节点作为子序列而不是连续子串进行匹配,您可以使用 subseq 模式。

<table>
    <tr><th>AAA</th><td>aaa</td></tr>
    <tr><th>BBB</th><td>bbb</td></tr>
    <tr><th>CCC</th><td>ccc</td></tr>
    <tr><th>DDD</th><td>ddd</td></tr>
    <tr><th>EEE</th><td>eee</td></tr>
</table>

对于此文档,

<table subseq>
    <tr><th>AAA</th><td>{{a}}</td></tr>
    <tr><th>BBB</th><td>{{b}}</td></tr>
    <tr><th>DDD</th><td>{{d}}</td></tr>
</table>

此模式匹配。

[
    {
        "a": "aaa",
        "b": "bbb",
        "d": "ddd"
    }
]

属性

您可以在模式中指定属性。当模式的属性是文档属性的子集时,属性模式匹配。

此模式

<div class="attr1">
    {{foo}}
</div>

与此文档匹配。

<div class="attr1 attr2">
    Hello
</div>

您还可以在属性中编写占位符。

<a href="{{url}}">{{title}}</a>

此文档的匹配结果是

<a href="https://www.google.com">Google</a>
<a href="https://www.yahoo.com">Yahoo</a>

[
    { "url": "https://www.google.com", "title": "Google" },
    { "url": "https://www.yahoo.com", "title": "Yahoo" },
]

部分文本节点模式

您可以在文本节点中编写任意位置的占位符。

<ul>
    <li>A: {{a}}, B: {{b}}</li>
</ul>

此文档的匹配结果是

<ul>
    <li>A: 1, B: 2</li>
    <li>A: 3, B: 4</li>
    <li>A: 5, B: 6</li>
</ul>

[
    { "a": "1",  "b": "2" },
    { "a": "3",  "b": "4" },
    { "a": "5",  "b": "6" },
]

您还可以在属性位置编写占位符。

<ul>
    <a href="/users/{{userid}}">{{username}}</a>
</ul>

此文档的匹配结果是

<ul>
    <a href="/users/foo">Foo</a>
    <a href="/users/bar">Bar</a>
    <a href="/users/baz">Baz</a>
</ul>

[
    { "userid": "foo",  "username": "Foo" },
    { "userid": "bar",  "username": "Bar" },
    { "userid": "baz",  "username": "Baz" },
]

整个子树模式

模式 {{var:*}} 与整个子树作为字符串匹配。

<div>{{body:*}}</div>

此文档的匹配结果是

<body>
    Hello
    <span>hoge</span>
    World
</body>

[
    { "body": "Hello<span>hoge</span>World" }
]

空白字符

几乎在所有位置都会忽略空白字符。

限制

  • 整个子树模式必须是父节点唯一的元素。

这是有效的

<div>
    {{foo:*}}
</div>

有无效的

<div>
    hoge {{foo:*}}
</div>
<ul>
    <li></li>
    {{foo:*}}
    <li></li>
<ul>

许可证:MIT

依赖项

~6–12MB
~146K SLoC