【问题标题】:Convert HTML table with a header to Json - Python将带有标题的 HTML 表转换为 Json - Python
【发布时间】:2020-05-14 21:07:37
【问题描述】:

假设我有以下 HTML 表格:

<table>
  <tr>
    <th>Name</th>
    <th>Age</th>
    <th>License</th>
    <th>Amount</th>
  </tr>
  <tr>
    <td>John</td>
    <td>28</td>
    <td>Y</td>
    <td>12.30</td>
  </tr>
  <tr>
    <td>Kevin</td>
    <td>25</td>
    <td>Y</td>
    <td>22.30</td>
  </tr>
  <tr>
    <td>Smith</td>
    <td>38</td>
    <td>Y</td>
    <td>52.20</td>
  </tr>
  <tr>
    <td>Stewart</td>
    <td>21</td>
    <td>N</td>
    <td>3.80</td>
  </tr>
</table>

我想将此表转换为 JSON,可能采用以下格式:

data= [
  { 
    Name: 'John',         
    Age: 28,
    License: 'Y',
    Amount: 12.30
  },
  { 
    Name: 'Kevin',         
    Age: 25,
    License: 'Y',
    Amount: 22.30
  },
  { 
    Name: 'Smith',         
    Age: 38,
    License: 'Y',
    Amount: 52.20
  },
  { 
    Name: 'Stewart',         
    Age: 21,
    License: 'N',
    Amount: 3.80
  }
];

我看到了另一个与上述类似的例子,我找到了here。 但是,鉴于该答案,有几件事我无法解决。它们是:

  • 它被限制在表格上的两行。如果我添加额外的行,我会收到错误:

print(json.dumps(OrderedDict(table_data))) ValueError: 值太多 解包(预计 2 个)

  • 不考虑表格的标题行。

这是我目前的代码:

html_data = """
<table>
  <tr>
    <th>Name</th>
    <th>Age</th>
    <th>License</th>
    <th>Amount</th>
  </tr>
  <tr>
    <td>John</td>
    <td>28</td>
    <td>Y</td>
    <td>12.30</td>
  </tr>
  <tr>
    <td>Kevin</td>
    <td>25</td>
    <td>Y</td>
    <td>22.30</td>
  </tr>
  <tr>
    <td>Smith</td>
    <td>38</td>
    <td>Y</td>
    <td>52.20</td>
  </tr>
  <tr>
    <td>Stewart</td>
    <td>21</td>
    <td>N</td>
    <td>3.80</td>
  </tr>
</table>
"""

from bs4 import BeautifulSoup
from collections import OrderedDict
import json

table_data = [[cell.text for cell in row("td")]
                         for row in BeautifulSoup(html_data, features="lxml")("tr")]

print(json.dumps(OrderedDict(table_data)))

但我收到以下错误:

print(json.dumps(OrderedDict(table_data))) ValueError: 需要超过 0 个要解压的值

编辑 如果 HTML 中只有一个表格,则下面的答案非常有效。如果有两张桌子怎么办?例如:

<html>
    <body>
        <h1>My Heading</h1>
        <p>Hello world</p>
        <table>
            <tr>
                <th>Name</th>
                <th>Age</th>
                <th>License</th>
                <th>Amount</th>
            </tr>
            <tr>
                <td>John</td>
                <td>28</td>
                <td>Y</td>
                <td>12.30</td>
            </tr>
            <tr>
                <td>Kevin</td>
                <td>25</td>
                <td>Y</td>
                <td>22.30</td>
            </tr>
            <tr>
                <td>Smith</td>
                <td>38</td>
                <td>Y</td>
                <td>52.20</td>
            </tr>
            <tr>
                <td>Stewart</td>
                <td>21</td>
                <td>N</td>
                <td>3.80</td>
            </tr>
        </table>
        <table>
            <tr>
                <th>Name</th>
                <th>Age</th>
                <th>License</th>
                <th>Amount</th>
            </tr>
            <tr>
                <td>Rich</td>
                <td>28</td>
                <td>Y</td>
                <td>12.30</td>
            </tr>
            <tr>
                <td>Kevin</td>
                <td>25</td>
                <td>Y</td>
                <td>22.30</td>
            </tr>
            <tr>
                <td>Smith</td>
                <td>38</td>
                <td>Y</td>
                <td>52.20</td>
            </tr>
            <tr>
                <td>Stewart</td>
                <td>21</td>
                <td>N</td>
                <td>3.80</td>
            </tr>
        </table>
    </body>
</html>

如果我在下面的代码中插入这个,只有第一个表显示为 JSON 输出。

【问题讨论】:

    标签: python html json


    【解决方案1】:

    你可以使用soup.find_all:

    from bs4 import BeautifulSoup as soup
    s = soup(html, 'html.parser').table
    h, [_, *d] = [i.text for i in s.tr.find_all('th')], [[i.text for i in b.find_all('td')] for b in s.find_all('tr')]
    result = [dict(zip(h, i)) for i in d]
    

    输出:

    [{'Name': 'John', 'Age': '28', 'License': 'Y', 'Amount': '12.30'}, {'Name': 'Kevin', 'Age': '25', 'License': 'Y', 'Amount': '22.30'}, {'Name': 'Smith', 'Age': '38', 'License': 'Y', 'Amount': '52.20'}, {'Name': 'Stewart', 'Age': '21', 'License': 'N', 'Amount': '3.80'}]
    

    【讨论】:

    • 非常感谢!这也正是我想要的。我只“批准”了另一个答案,因为输出以更友好的方式显示。
    【解决方案2】:

    这段代码完全符合你的要求

    from bs4 import BeautifulSoup
    import json
    
    xml_data = """
    [[your xml data]]"""
    
    
    if __name__ == '__main__':
        model = BeautifulSoup(xml_data, features='lxml')
        fields = []
        table_data = []
        for tr in model.table.find_all('tr', recursive=False):
            for th in tr.find_all('th', recursive=False):
                fields.append(th.text)
        for tr in model.table.find_all('tr', recursive=False):
            datum = {}
            for i, td in enumerate(tr.find_all('td', recursive=False)):
                datum[fields[i]] = td.text
            if datum:
                table_data.append(datum)
    
        print(json.dumps(table_data, indent=4))
    

    【讨论】:

    • 如果您有两个不同的表,您将如何修改它?如果我将两个表一个接一个地放在“xml_data”变量中,它只会返回第一个表的 JSON 输出。
    • 在开始时你循环for table in model.find_all("table"):,然后用table替换model的每一个后续出现
    • 你的“开始”是什么意思?这应该直接在第一个循环之前吗?它应该包含两个 for 循环吗?我已经编辑了我的原始帖子以获取更多详细信息。也许您可以直接编辑您的答案?
    • 我在这里创建了一个新问题:stackoverflow.com/q/60379358/3480297
    猜你喜欢
    • 1970-01-01
    • 2021-07-05
    • 2019-06-13
    • 2020-12-29
    • 2018-11-27
    • 2019-07-21
    • 2015-09-09
    • 1970-01-01
    • 2019-08-21
    相关资源
    最近更新 更多