通过Bash使用XMLStarlet将元素(<;td>;)插入HTML表的每一行

通过Bash使用XMLStarlet将元素(<;td>;)插入HTML表的每一行,bash,xpath,wget,xmlstarlet,xmllint,Bash,Xpath,Wget,Xmlstarlet,Xmllint,我想从链接列表中提取每个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 但是,由于这不是我想要提取的唯一一个表,因此很

我想从链接列表中提取每个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
但是,由于这不是我想要提取的唯一一个表,因此很难确定哪个财务数据属于哪个表,因此这一方法非常有效。在这种情况下,它将只解析附加到输出文件的一个表,其中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>
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
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

新更新

根据这一点,我遇到了这样一个脚本,它可以很容易地添加一个子节点:

<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>
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
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
将以下内容写入标准输出:

<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>
在这里,您可以清楚地看到,我试图声明一个变量
$header
,该变量应包括资产的名称。这不起作用,并使输出文件为空,可能是因为声明错误或管道的语法不正确


如何将相应的XPath(引用资产名称)插入新创建的子节点
?变量是我想到的第一件事;是否可以用其他方法完成?

在将输出附加到
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>#'

您可能可以在xmlstarlet中实现这一点,但我对xmlstarlet了解不够,无法给您一个简单的答案

另外,正如您所说,在将HTML传递给xmlstarlet
ed
之前,您必须通过xmllint或使用。它看起来不像
ed
支持
--html

我要做的是将xmlstarlet与XSLT样式表一起使用

这是非常冗长的,但它比尝试更安全。它也更容易扩展

这是XSLT。我添加了一些评论,试图帮助您了解正在发生的事情

XSLT1.0(stylesheet.xsl)


我可以通过在本地html文件上使用
cat
而不是
wget
在本地测试这一点。如果您想让我将测试文件/结果添加到我的答案中,请告诉我。

此脚本工作正常,但效率低下;它需要一些编辑:

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
  • 获取表并附加子节点
    $header
  • 因此,这会将以下内容写入我的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>
    
    
    2017年9月8日
    4605.16
    4661
    4075.18
    4228.75
    2,700,890,000
    76,220,200,000
    名字1
    2017年9月7日
    4589.14
    4655.04
    4491.33
    4599.88
    1,844,620,000
    75,945,000,000
    名字1
    ...
    

    这相对比较慢,因为实际上只能使用一个请求来完成,但我不知道怎么做。

    您正在重定向到
    >/Applications/parser/output.txt
    。重定向到
    >/Applications/parser/table name.txt
    对您来说是一种解决方案吗?Hi@WalterA我不想更改文件本身的名称,相反,考虑到表被附加到名为output.txt的文件中,我想为该文件中的每个表添加某种标题,以便识别每个表。wget命令将把大约1160多个不同的表附加到一个文件中,其中只包含表信息,这就是为什么知道哪个表属于哪个数据集很方便的原因。我建议使用TABLE-NAME作为新行,如
    TABLE-NAME
    。你能帮我吗?你能用xmlstarlet代替xmllint吗?嗨@DanielHaley我通过自制软件安装了xmlstarlet,所以我能用它代替。我正在看文档,我必须说我对这个有点陌生。你有什么想法?也许你可以提供一个答案;这对我真的很有帮助!根据,它说有一个命令
    -a
    -append
    ,但没有提供示例。我假设它可能类似于
    -d
    -I
    。我是否必须首先解析出表,然后应用
    -a
    命令将表名附加到每个
    的末尾?我不介意在开头或结尾,只要每行都包含名称。另一方面,如何解析多个XPath?也就是说,一个XPath表示表,另一个表示同一网页中的名称。您好@WalterA谢谢您的回答:)我正在寻找一些更短、更简单的东西,比如
    xmlstarlet
    。您熟悉它吗?您需要一个变量
    tbl
    和一些循环用于所有解决方案。我不知道
    xmlstarlet
    。例如,您知道如何将
    xmllint--html--xpath'/html/body/div[3]/div/div[1]/div[3]/div[1]/h1'
    传递给变量吗?或者您是否建议通过命令
    -v
    使用另一种方法?嗨@DanielHaley谢谢您的支持。我在我的问题中加入了我迄今为止的发现,这使我非常接近我的结果。您能否更新您的答案,说明如何进行更换?如果你能帮我完成这最后一步,我将很乐意接受你的解决方案!致以最良好的祝愿:)关于剧本有什么消息吗?你有改进的建议吗@丹尼尔哈利
    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
    
    <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>