【问题标题】:Parsing multiple tables with BeautifulSoup使用 BeautifulSoup 解析多个表
【发布时间】:2017-11-01 07:52:53
【问题描述】:

我在使用 BeautifulSoup 解析表数据时遇到问题,尽管我尝试了许多解决方案,发现 hereherehere。我不想再问,但也许我的问题是独一无二的,这就是为什么上述解决方案没有奏效,或者我只是个白痴。

所以我正在尝试从water.weather.gov 检索任何给定河流的洪水触发器。我正在使用Mississippi river data,因为它拥有最活跃的测量站。每个电台都有 4 个我想要获得的阶段触发器:ActionFloodModerateMajor。我实际上已经能够提取这些类别的表格数据当有数值时,但是在表格数据为“不可用”的情况下,该行将被跳过,这样当我将值放在正确的阶段时,它们不会与适当的站触发器对齐。

我尝试提取的表格数据如下所示:

<div class="box_square">        <b><b>Flood Categories (in feet)</b><br>
</b>
        <table width="150" cellspacing="0" cellpadding="0" border="0">
        <tbody>
            <tr><td nowrap="">Not Available</td></tr>
        </tbody>

<div class="box_square">        <b><b>Flood Categories (in feet)</b><br>
</b>
        <table width="150" cellspacing="0" cellpadding="0" border="0">
        <tbody>
            <tr style="display:'';line-height:20px;background-color:#CC33FF;color:black">
                <td scope="col" nowrap="">Major Flood Stage:</td>
                <td scope="col">18</td>
            </tr>
            <tr style="display:'';line-height:20px;background-color:#FF0000;color:white">
                <td scope="col" nowrap="">Moderate Flood Stage:</td>
                <td scope="col">15</td>
            </tr>
            <tr style="display:'';line-height:20px;background-color:#FF9900;color:black">
                <td scope="col" nowrap="">Flood Stage:</td>
                <td scope="col">13</td>
            </tr>
            <tr style="display:'';line-height:20px;background-color:#FFFF00;color:black">
                <td scope="col" nowrap="">Action Stage:</td>
                <td scope="col">12</td>
            </tr>
            <tr style="display:none;line-height:20px;background-color:#906320;color:white">
                <td scope="col" nowrap="">Low Stage (in feet):</td>
                <td scope="col">-9999</td>
            </tr>
        </tbody>
        </table><br></div>

最后一个Low Stage不是必须的,我已经过滤掉了。这是我拥有的代码,它将使用适当的值填充 alert_list,但没有必要的 Not Available

alert_list = []
alert_values = []
alerts = soup.findAll('td', attrs={'scope':'col'})
for alert in alerts:
    alert_list.append(alert.text.strip()) 

a_values = alert_list[1::2]
alert_list.clear()
major_lvl = a_values[::5]
moderate_lvl = a_values[1::5]
flood_lvl = a_values[2::5]
action_lvl = a_values[3::5]

结果:

>>> major_lvl
['18', '26', '0', '11', '0', '17', '17', '18', '0', '683', '16', '0', '20', '16', '18', '665', '661', '18', '651', '645', '15.5', '636', '20', '631', '22', '21', '20.5', '21.5', '20', '20', '20.5', '13.5', '18', '18', '20', '18.5', '17', '14', '18', '19', '25', '25', '25', '26', '25', '24', '22', '25', '33', '34', '29', '34', '40', '40', '0', '0', '0', '42', '42', '0', '0', '0', '0', '0', '44', '47', '43', '35', '46', '52', '55', '0', '44', '57', '50', '57', '64', '40', '34', '26', '20']

我只是注意到实际上 Not Available 标签没有被抓取的原因是因为它在 tr 标签下,而不是 td .我如何添加它以使我的值对齐?

【问题讨论】:

    标签: python web-scraping beautifulsoup html-table


    【解决方案1】:

    你也可以用一个函数来做。在您的情况下,只有您想要的行具有 style 属性。您可以浏览所有标签,并只接受 trstyle 的标签。

    >>> from bs4 import BeautifulSoup
    >>> soup = BeautifulSoup(open('weather.htm'), 'lxml')
    >>> def acceptable(tag):
    ...     return tag.name=='tr' and tag.has_attr('style')
    ... 
    >>> for tag in soup.find_all(acceptable):
    ...     tag.text.replace('\n', '').split(':')
    ...     
    ['Major Flood Stage', '18']
    ['Moderate Flood Stage', '15']
    ['Flood Stage', '13']
    ['Action Stage', '12']
    ['Low Stage (in feet)', '-9999']
    

    编辑,回应评论:

    省略 acceptable 并使用它。

    >>> for tag in soup.find_all('tr'):
    ...     if tag.has_attr('style'):
    ...         tag.text.replace('\n', '').split(':')
    ...     elif 'not available' in tag.text.lower():
    ...         tag.text
    ...     else:
    ...         pass
    ...     
    'Not Available'
    ['Major Flood Stage', '18']
    ['Moderate Flood Stage', '15']
    ['Flood Stage', '13']
    ['Action Stage', '12']
    ['Low Stage (in feet)', '-9999']
    

    【讨论】:

    • 我非常喜欢这个解决方案,因为我将使用多条河流,每条河流都有自己的阶段和警报。但是,您的解决方案仍然不会在未报告数据的工作站中返回 Not Available 值。我在我正在抓取的 OP 中包含了密西西比河的链接,以帮助更好地可视化舞台警报。并非所有电台都在报告数据,这就是我遇到问题的地方。
    • 太棒了!感谢比尔的帮助!所以我实际上是在自学 Python,而且我正在把它作为一个项目来做。我想知道 has.attrtext.replace 语法是 BeautifulSoup 库的一部分还是内置的。您能否提供一些文档,我可以查看以了解更多如何以及何时使用它们。再次感谢您的帮助!
    • 我们都在“学习 Python”。 (相信我。)无论如何,has.attr 是 BeautifulSoup 的一部分,text.replace 是纯 Python。要使用 BeautifulSoup,您应该完全熟悉 Python 字符串及其正则表达式模块。我只是阅读(并重新阅读和重新阅读)这些随附的文档。对于 BS 有 crummy.com/software/BeautifulSoup/bs4/doc 我觉得很糟糕,我必须说,还有无数的例子在这里。
    【解决方案2】:

    如果您只对scope=col 所在的那些列感兴趣,您可以使用css selector 漂亮地完成此操作。

    In [24]: soup = BS(html, "html.parser")
    
    In [25]: major_list = [td.get_text(strip=True) for td in soup.select("tr > td:nth-of-type(2)[scope=col]")[:-1]]
    
    In [26]: major_list
    Out[26]: ['18', '15', '13', '12']
    

    要获取列旁边的所有行,您需要先select 行,然后为每一行检索列中的数据。

    for tr in soup.select("div[class=box_square] tr"):
        print([td.get_text(strip=True) for td in tr.find_all("td")])
    

    【讨论】:

    • 我喜欢您的简单解决方案,但由于某种原因,我无法产生相同的结果:['', 'Home', '18', '15', '13', '12'],而且我仍然不确定如何包含 Not Available 来自未报告数据的电台的标签。谢谢!
    • 感谢您的编辑。这也很有效,所以我投了赞成票。我给了 Bill 解决方案投票,因为使用我的代码当前的结构方式更容易将 not available 标记分配给正确的站。或者至少在我看来是这样的。我仍然感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    • 2012-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 2020-04-03
    相关资源
    最近更新 更多