本文档介绍了自定义解析器的使用案例。
下面的HTML片段采用了下列章节中的示例解析指令来进行了解析。
示例HTML
<body>
<div id="products">
<div class="product" id="shoes">
<div class="title">Shoes</div>
<div class="price">223.12</div>
<div class="description">
<ul>
<li class="description-item">Super</li>
</ul>
</div>
</div>
<div class="product" id="pants">
<div class="title">Pants</div>
<div class="price">60.12</div>
<div class="description">
<ul>
<li class="description-item">Amazing</li>
<li class="description-item">Quality</li>
</ul>
</div>
</div>
<div class="product" id="socks">
<div class="title">Socks</div>
<div class="price">123.12</div>
<div class="description">
<ul>
<li class="description-item">Very</li>
<li class="description-item">Nice</li>
<li class="description-item">Socks</li>
</ul>
</div>
</div>
</div>
</body>
最低限度
示例1. 使用XPath选择鞋类描述项目。
{
"shoes_description": {
"_fns": [
{
"_fn": "xpath",
"_args": [
".//div[@id='shoes']//li[@class='description-item']/text()"
]
}
]
}
}
xpath
函数将找到一个单独的项目,并将其作为一个字符串放在列表中:
{
"shoes_description": [
"Super"
]
}
此处描述了确切的xpath
函数行为。
嵌套解析指令
使用案例:您想解析所有与鞋子相关的信息。此外,解析的结果应该能够显示所提供的HTML的文档结构。
您的目标是示例HTML的这一部分:
<div class="product" id="shoes">
<div class="title">Shoes</div>
<div class="price">223.12</div>
<div class="description">
<ul>
<li class="description-item">Super</li>
</ul>
</div>
</div>
而您希望解析后的结果具有以下结构:
{
"shoes": {
"title": "Shoes",
"price": "223.12",
"description": [
"Super"
]
}
}
解析指令如下。
示例2. 解析指令是用来解析鞋子信息。
{
"shoes": {
"title": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@id='shoes']/div[@class='title']/text()"]
}
]
},
"price": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@id='shoes']/div[@class='price']/text()"]
}
]
},
"description": {
"_fns": [
{
"_fn": "xpath",
"_args": ["//div[@id='shoes']//li[@class='description-item']/text()"]
}
]
}
}
}
xpath_one
的工作原理与xpath
类似,但它不会返回所有匹配项的列表,而是返回第一个匹配项。
在上面的示例中,鞋子属性是在最外层指令范围内定义的唯一属性。鞋子属性包含了嵌套的解析指令。
鞋子指令范围没有定义管线(_fns
属性缺失)。
这意味着在标题、 价格和描述范围内定义的管线将使用待解析文档作为管线的输入。
在示例2中,您可以看到XPath表达式中重复出现的//div[@id='shoes']
。通过在鞋子范围内定义一个管线,可以出现避免重复的情况:
示例3. 定义一个在鞋子范围指令内的管线,以避免XPath表达式重复。
{
"shoes": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@id='shoes']"]
}
],
"title": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["./div[@class='title']/text()"]
}
]
},
"price": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["./div[@class='price']/text()"]
}
]
},
"description": {
"_fns": [
{
"_fn": "xpath",
"_args": [".//li[@class='description-item']/text()"]
}
]
}
}
}
通过示例3中提供的解析指令,自定义解析器将:
从处理shoes._fns
管线开始,它将输出鞋子HTML元素;
采用shoes._fns
管线输出,并将其作为管线的输入,在标题、价格和描述范围内进行定义;
其结果将与示例2的结果相同:
{
"shoes": {
"title": "Shoes",
"price": "223.12",
"description": [
"Super"
]
}
}
示例2和示例3的主要区别是,在示例3中,管线被定义为鞋子的范围。这个额外的管线选择了鞋子的元素,并将其传递给指令层次结构中更深的管线。
嵌套对象的列表
使用案例:之前,您只想解析鞋子的信息。现在您要解析HTML中所有产品的信息。
示例HTML将再次被用于待解析文档。
如果您希望您的解析结果看起来像这样:
{
"products": [
{
"title": "Shoes",
"price": "223.12",
"description": [
"Super"
]
},
{
"title": "Pants",
"price": "60.12",
"description": [
"Amazing",
"Quality"
]
},
{
"title": "Socks",
"price": "123.12",
"description": [
"Very",
"Nice",
"Socks"
]
}
]
}
解析指令将如下所示:
示例4. 解析在HTML文档中发现的所有产品。
{
"products": {
"_fns": [
{
"_fn": "xpath",
"_args": ["//div[@class='product']"]
}
],
"_items": {
"title": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["./div[@class='title']/text()"]
}
]
},
"price": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["./div[@class='price']/text()"]
}
]
},
"description": {
"_fns": [
{
"_fn": "xpath",
"_args": [".//li[@class='description-item']/text()"]
}
]
}
}
}
}json
解析指令的结构与示例3中的结构相似。然而,有两个主要的例外情况:
在products._fns
管线中使用了xpath
而不是xpath_one
。product._fns
管线现在将输出一个与所提供的XPath表达式相匹配的所有元素列表(一个产品元素的列表)。
_items
保留的属性用来表明您希望通过迭代products._fns
管线输出的每个项目来形成一个列表,并在管线范围内分别向下传递/处理每个列表项目。
如果在示例4的解析指令中没有使用_items
的保留属性,则解析后的结果如下:
{
"products": {
"title": [
"Shoes",
"Pants",
"Socks"
],
"price": [
"223.12",
"60.12",
"123.12"
],
"description": [
[
"Super"
],
[
"Amazing",
"Quality"
],
[
"Very",
"Nice",
"Socks"
]
]
}
}
_items
是用于指定自定义解析器必须通过独立的列表项而不是整个列表的解析指令。
从一个列表中选择第N个元素
本章节中展示了管线的灵活性。同样的问题可以用不同的方式来处理。
可以利用多个选项从一个任意值列表中选择第N个元素。
此处再次以示例HTML为例。您有多种选择来选择第2个产品。
选项1
您可以利用XPath []选择器并在XPath表达式中定义选择。
示例5. 使用XPath []选择器选择第2个价格。
{
"second_price": {
"_fns": [
{
"_fn": "xpath",
"_args": [
"(//div[@class='price'])[2]/text()"
]
}
]
}
}
结果:
{
"second_price": [
"60.12"
]
}
选项2
您也可以使用xpath
函数来找到所有的价格,并将其输送到函数select_nth
中, 从提取的价格列表中选择第n个元素。
示例6. 使用`select_nth`函数选择第2个值。
{
"second_price": {
"_fns": [
{
"_fn": "xpath",
"_args": [
"//div[@class='price']/text()"
]
},
{
"_fn": "select_nth",
"_args": 1
}
]
}
}
结果:
{
"second_price": "60.12"
}
注意select_nth
函数是如何从列表中返回一个项目的,而xpath
函数即使找到一个项目,返回的也是一个项目列表。
选项3
您可以对任何列表类型使用select_nth
,包括HTML元素的列表:
示例7. 选择所有带有class="product"
的产品HTML元素==> 从列表中选择第2个产品元素==>从选定的产品HTML元素中提取价格文本。
{
"second_price": {
"_fns": [
{
"_fn": "xpath",
"_args": ["//div[@class='product']"]
},
{
"_fn": "select_nth",
"_args": 1
},
{
"_fn": "xpath",
"_args": ["./div[@class='price']/text()"]
}
]
}
}
结果:
{
"second_price": ["60.12"]
}
错误处理
当给出以下HTML片段时:
<div class="product" id="shoes">
<div class="title">Nice Shoes</div>
<div class="price">223.12</div>
<div class="description">Super</div>
</div>
并试图用下面的解析指令来解析它:
{
"product": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@id='shoes']"]
}
],
"price": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@class='price']/text()"]
}
]
},
"title": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@class='title']/text()"]
}
]
},
"description": {
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@class='description']/text()"]
},
{
"_fn": "convert_to_float"
}
]
}
}
}
自定义解析器将返回一个解析结果,其中价格和标题将被正常解析,但由于convert_to_float
函数未能将字符串转换为浮点数,因此该描述未能被解析:
{
"product": {
"price": "223.12",
"title": "Shoes",
"description": null
},
"_warnings": [
{
"_fn": "convert_to_float",
"_fn_idx": 1,
"_msg": "Failed to process function.",
"_path": ".product.description"
}
]
}
在默认情况下,所有的错误都被视为警告,并被放在_warnings
列表中。如果您想在解析一个字段时忽略错误,则他们可以用"_on_error": "suppress"
参数来抑制警告/错误:
{
"product": {
...,
"description": {
"_on_error": "suppress",
"_fns": [
{
"_fn": "xpath_one",
"_args": ["//div[@class='description']/text()"]
},
{
"_fn": "convert_to_float"
}
]
}
}
}
这将产生以下结果:
{
"product": {
"price": "223.12",
"title": "Shoes",
"description": null
}
}
数组的数组
自定义解析器允许在解析的结果中出现N维数组。作为示例,让我们使用下面的HTML片段:
<div class="row">
<div class="column">1</div>
<div class="column">2</div>
<div class="column">3</div>
</div>
<div class="row">
<div class="column">4</div>
<div class="column">5</div>
<div class="column">6</div>
</div>
<div class="row">
<div class="column">7</div>
<div class="column">8</div>
<div class="column">9</div>
</div>
假如您想解析文档,使其结果是一个3x3的二维整数数组:
{
"table": [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
}
如需把HTML解析为上面的JSON,则您可以使用下面的解析指令:
{
"table": {
"_fns": [
{
"_fn": "xpath",
"_args": ["//div[@class='row']"]
},
{
"_fn": "xpath",
"_args": [".//div[@class='column']/text()"]
},
{
"_fn": "convert_to_int"
}
]
}
}