我想从链接列表中提取每个HTML表。我使用的代码如下:
wget -O - "https://example.com/section-1/table-name/financial-data/" | xmllint --html --xpath '//*[@id="financial-data"]/div/table/tbody' - 2>/dev/null >> /Applications/parser/output.txt
这很好地效果很好,但是,鉴于这不是我要提取的唯一表格,它会使我难以确定哪个 financial-data 属于哪个表。在这种情况下,它将仅解析一张表格附加到SDTOUT看起来像这样的输出文件的表:
<tbody>
<tr class="text-right">
<td class="text-left">Sep 08, 2017</td>
<td>4605.16</td>
<td>4661.00</td>
<td>4075.18</td>
<td>4228.75</td>
<td>2,700,890,000</td>
<td>76,220,200,000</td>
</tr>
<tr class="text-right">
<td class="text-left">Sep 07, 2017</td>
<td>4589.14</td>
<td>4655.04</td>
<td>4491.33</td>
<td>4599.88</td>
<td>1,844,620,000</td>
<td>75,945,000,000</td>
</tr>
...
</tbody>
但是我正在寻找:
<tbody>
<tr class="text-right">
<td>TABLE-NAME</td>
<td class="text-left">Sep 08, 2017</td>
<td>4605.16</td>
<td>4661.00</td>
<td>4075.18</td>
<td>4228.75</td>
<td>2,700,890,000</td>
<td>76,220,200,000</td>
</tr>
<tr class="text-right">
<td>TABLE-NAME</td>
<td class="text-left">Sep 07, 2017</td>
<td>4589.14</td>
<td>4655.04</td>
<td>4491.33</td>
<td>4599.88</td>
<td>1,844,620,000</td>
<td>75,945,000,000</td>
</tr>
...
</tbody>
table-name 是特定资产的名称。可以使用出现在表所在的同一URL中的XPath /html/body/div[3]/div/div[1]/div[3]/div[1]/h1/text()
提取名称,或者是从链接本身/table-name/
。
我无法弄清楚语法。
nb :我故意省略了wget命令中的 -q
标志,因为我想查看执行脚本时终端中正在发生的事情。
谢谢!
update
根据@danielhaley,这可以通过XMLStarlet完成,但是,当我阅读文档时,我找不到有关如何使用它的示例。
正确的语法是什么?我是否首先必须通过xmllint --html --xpath
解析HTML表,然后应用xmlstarlet
?
这是我到目前为止发现的:
-i or --insert <xpath> -t (--type) elem|text|attr -n <name> -v (--value) <value>
-a or --append <xpath> -t (--type) elem|text|attr -n <name> -v (--value) <value>
新更新
根据此链接,我遇到了像这样轻松添加子节点的脚本:
wget -O - "https://example.com/section-1/table-name/financial-data/" |
xmllint --html --xpath '//*[@id="financial-data"]/div/table/tbody' - 2>/dev/null |
xmlstarlet ed --subnode "/tbody/tr" --type elem -n td -v "Hello World" >> /Applications/parser/output.txt
将以下内容写入stdout:
<tbody>
<tr class="text-right">
<td class="text-left">Sep 08, 2017</td>
<td>4605.16</td>
<td>4661.00</td>
<td>4075.18</td>
<td>4228.75</td>
<td>2,700,890,000</td>
<td>76,220,200,000</td>
<td>Hello World</td>
</tr>
<tr class="text-right">
<td class="text-left">Sep 07, 2017</td>
<td>4589.14</td>
<td>4655.04</td>
<td>4491.33</td>
<td>4599.88</td>
<td>1,844,620,000</td>
<td>75,945,000,000</td>
<td>Hello World</td>
</tr>
...
</tbody>
到目前为止很好,但是,这重现了一些默认文本,称为文本字符串使用选项-v
,即在这种情况下,在这种情况下是" Hello World"。我希望用资产的实际名称替换此文本字符串。如前所述, table-name 在表所在的同一页面中找到,可以通过其他XPath访问,因此我尝试了以下代码:
wget -O - "https://example.com/section-1/table-name/financial-data/" |
header=$(xmllint --html --xpath '/html/body/div[3]/div/div[1]/div[3]/div[1]/h1' -) |
xmllint --html --xpath '//*[@id="financial-data"]/div/table/tbody' - 2>/dev/null |
xmlstarlet ed --subnode "/tbody/tr" --type elem -n td -v "$header" >> /Applications/parser/output.txt
在这里,您可以清楚地看到我尝试声明一个包括资产名称的变量$header
。这不起作用,并使我的输出文件空白,可能是因为声明是错误的,或者管道的语法不正确。
我如何将根据XPath(引用资产名称的名称)插入新创建的子节点<td>
?变量是我想出的第一件事。可以做否则吗?
您应该尝试在将输出附加到output.txt
之前插入附加列。确保所需的表格存储在变量中。您想做
tbl=testtbl
echo "<tbody>
<tr class="text-right">
<td class="text-left">Sep 08, 2017</td>
<td>4605.16</td>
<td>4661.00</td>
<td>4075.18</td>
<td>4228.75</td>
<td>2,700,890,000</td>
<td>76,220,200,000</td>
</tr>
<tr class="text-right">
<td class="text-left">Sep 07, 2017</td>
<td>4589.14</td>
<td>4655.04</td>
<td>4491.33</td>
<td>4599.88</td>
<td>1,844,620,000</td>
<td>75,945,000,000</td>
</tr>
" | sed 's#.*<tr.*#&n <td>'"${tbl}"'</td>#'
在sed
命令中,普通斜线被"#"代替,因此您不要逃脱</td>
中的斜线。
当您使用apporox的文件alltables.txt
时。1160桌,您货车制作这样的循环:
while IFS= read -r tbl; do
wget -O - "https://example.com/section-1/table-name/financial-data/" |
xmllint --html --xpath '//*[@id="financial-data"]/div/table/tbody' - 2>/dev/null |
sed 's#.*<tr.*#&n <td>'"${tbl}"'</td>#' >> /Applications/parser/output.txt
done < alltables.txt
您可能可以使用XMLStarlet中的ed
(edit)命令来执行此操作,但是我不太了解XMLSTARLET,足以给您一个简单的答案。
另外,就像您说的那样,看起来您必须通过XMLLINT将HTML传递或使用fo
XMLSTARLET命令,然后再将其传递给XMLSTARLET ed
。它看起来不像ed
支持--html
。
我要做的是将XMLSTARLET tr
(变换)命令与XSLT样式表一起使用。
这很冗长,但是比试图用Regex解析HTML/XML要安全得多。扩展也容易得多。
这是XSLT。我添加了评论以帮助您了解发生了什么。
XSLT 1.0 (stylesheet.xsl)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<!--Parameter to capture the table name. This is set on the command line.-->
<xsl:param name="tablename"/>
<!--Identity transform. Will basically output attributes/nodes without
change if not matched by a more specific template.-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--Template matching the root element. I do this to narrow the scope of what's
being processed.-->
<xsl:template match="/*">
<!--Process tbody.-->
<xsl:apply-templates select=".//*[@id='financial-data']/div/table/tbody"/>
</xsl:template>
<!--Match tr elements so we can add the new td with the table name.-->
<xsl:template match="tr">
<!--Output the tr element.-->
<xsl:copy>
<!--Process any attributes.-->
<xsl:apply-templates select="@*"/>
<!--Create new td element.-->
<td><xsl:value-of select="$tablename"/></td>
<!--Process any children of tr.-->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
命令行
wget -O - "https://example.com/section-1/table-name/financial-data/" |
xml tr --html stylesheet.xsl -p tablename="/html/body/div[3]/div/div[1]/div[3]/div[1]/h1"
我能够通过在本地HTML文件而不是wget
上使用cat
在本地测试。让我知道您是否要我将测试文件/结果添加到我的答案中。
此脚本有效,但效率低下;它需要一些编辑:
name_query="html/body/div[3]/div/div[1]/div[3]/div[1]/h1/text()"
# Use xargs to TRIM result.
header=$(wget -O - "https://example.com/section-1/name-1/financial-data/" |
xmllint --html --xpath "$name_query" - 2>/dev/null |
xargs)
wget -O - "https://example.com/section-1/name-1/financial-data/" |
xmllint --html --xpath '//*[@id="financial-data"]/div/table/tbody' - 2>/dev/null |
xmlstarlet ed --subnode "/tbody/tr" --type elem -n td -v "$header" >> /Applications/parser/output.txt
这提出了两个请求:
- 获取名称并将其传递到可变
$header
- 获取表并附加子节点
<td>$header</td>
因此,这将以下内容写入我的 output.txt 文件:
<tbody>
<tr class="text-right">
<td class="text-left">Sep 08, 2017</td>
<td>4605.16</td>
<td>4661.00</td>
<td>4075.18</td>
<td>4228.75</td>
<td>2,700,890,000</td>
<td>76,220,200,000</td>
<td>Name 1</td>
</tr>
<tr class="text-right">
<td class="text-left">Sep 07, 2017</td>
<td>4589.14</td>
<td>4655.04</td>
<td>4491.33</td>
<td>4599.88</td>
<td>1,844,620,000</td>
<td>75,945,000,000</td>
<td>Name 1</td>
</tr>
...
</tbody>
它相对较慢,因为这实际上只能使用一个请求完成,但我不知道如何。