仅使用 Xpath 提取 <br> 标签内部分文本的文本

Posted

技术标签:

【中文标题】仅使用 Xpath 提取 <br> 标签内部分文本的文本【英文标题】:Extract text where a part of text inside <br> tag using only Xpath 【发布时间】:2021-10-21 01:04:03 【问题描述】:

我只想从这里抓取字符串:

                  <br>

                5 Brown Circle<br>

                Alabaster,

                AL &nbsp;&nbsp;

                35007

我需要深入了解如何从 html doc 之后的上述部分中提取文本:

<tr class="prem-tr" id="10425" role="row">
                    <td>
                        <h4><a class="prem-result-link" href="/Search/Details/10425">Graham &amp; Associates, CPAs</a></h4>

                        <a href="tel:+(205) 663-6673">(205) 663-6673</a>
                        <br>

                        5 Brown Circle<br>

                        Alabaster,

                        AL &nbsp;&nbsp;

                        35007

                        <div class="row result-btmRow">
                            <div class="col-sm-4">
                                <span class="result-dist"><small>Distance: 0.00 miles</small></span>
                            </div><!-- col6 -->
                            <div class="col-sm-8 result-actions">
                                <a id="WebsiteURL" class="visit-site" href="http://grahamandassociates.net" target="_blank">Visit Website</a>&nbsp;&nbsp;

                                <a class="send-email" href="/Search/Details/10425">Send a Message</a>
                            </div><!-- /col6 -->
                        </div><!-- /row -->
                    </td>
                </tr>

预期输出:5 Brown Circle, Alabaster, AL 35007 仅使用 xpath 以及解释。

在 css 选择器中,它工作正常。谁能解释下面的代码?谢谢

" ".join([" ".join(el.root.strip().split()) for el in sel.css("td::text") if el.root.strip()])

【问题讨论】:

展示你的尝试 看看elementTree 【参考方案1】:

我不会说这是一个很好的解决方案,但如果要求只使用 XPath 1.0...

normalize-space(translate(concat(//td/text()[4], //td/text()[5]),"\xa0", ""))

稍微分解一下并在 iPython 中使用 lxml.etree 进行演示:

td 的所有子文本节点都可以用//td/text() 选择。这不包括姓名和电话号码,因为它们是后代而不是孩子。

In [73]: root.xpath('//td/text()')
Out[73]: 
['\n                        ',
 '\n\n                        ',
 '\n                        ',
 '\n\n                        5 Brown Circle',
 '\n\n                        Alabaster,\n\n                        AL \xa0\xa0\n\n                        35007\n\n                        ',
 '\n                    ']

理想情况下,我们可以连接所有这些字符串并使用normalize-space() 规范化空格,但这很尴尬,因为在XPath 1.0 中,我们只有concat() 可供我们使用,它只需要两个参数。在 Python 中使用join() 处理这个问题会更好,但是因为我们感兴趣的只有两个文本节点,所以我们可以使用concat() 来连接集合中的第四个和第五个文本节点,以获得纯 XPath 解决方案。

In [74]: root.xpath('concat(//td/text()[4], //td/text()[5])')
Out[74]: '\n\n                        5 Brown Circle\n\n                        Alabaster,\n\n                        AL \xa0\xa0\n\n                        35007\n\n                        '

现在我们可以申请normalize-space() 来清理空白。


In [75]: root.xpath('normalize-space(concat(//td/text()[4], //td/text()[5]))')
Out[75]: '5 Brown Circle Alabaster, AL \xa0\xa0 35007'              '

差不多了。现在我们只需要在规范化空格之前用translate() 去掉不间断的空格字符。

In [79]: root.xpath('normalize-space(translate(concat(//td/text()[4], //td/text()[5]),"\xa0", ""))')
Out[79]: '5 Brown Circle Alabaster, AL 35007'

请注意,因为这是 Python,所以我们必须使用 \xa0 而不是 &amp;nbsp;&amp;#160; 来表示不间断空格字符。

【讨论】:

为什么我们在这里使用翻译方法?你能解释一下吗? 当然可以。 translate() 在字符串中搜索单个字符的实例并将它们替换为其他单个字符。如果未指定字符,则仅删除原始字符。所以在这里,translate() 用于擦除不间断的空格字符(原始的&amp;nbsp;),以便结果中的空格符合您的要求。 (normalize-space() 处理大多数空格,但不能处理不间断的空格字符。) 好吧,你已经连接了 text 。你能详细说明一下哪个部分表示 html 文档中的文本吗? 谢谢。它工作正常,但我不只理解串联 4 和 5 。你能告诉我你是怎么做到的吗? 姓名和电话号码是孙子,不是直子。伟大的!解释。这次。我已经明白了一切。非常感谢。【参考方案2】:

&amp;nbsp 实体和未关闭的&lt;br&gt; 标记的处理可能会因您使用的 XPath 处理器而异,但以下将产生所请求的确切结果:

//td/text()[string-length(normalize-space(.)) > 0]/normalize-space(translate(.,'&#160;',''))

在哪里

//td 选择所有 td 节点(示例中只有一个), /text() 选择作为td 的直接子级的所有文本节点, 谓词[string-length(normalize-space(.)) &gt; 0] 消除了在去除前导/尾随空格后为零长度字符串的任何文本节点, /normalize-space(translate(.,'&amp;#160;',''))nbsp 字符替换为空字符,并消除剩余文本节点的前导/尾随空格。

【讨论】:

太棒了!但是有个小问题:这里的(.)是什么意思,为什么我们用 去掉&nbsp作为替换。 &amp;nbsp 实体是实际字符,您没有在预期的输出中显示它们。 ' ' 只是数字参考(参见***.com/questions/3274315/…)。在 XPath 中,点指的是上下文项。例如,在表达式text()[string-length(normalize-space(.)) &gt; 0] 中,点指的是应用谓词的 text() 节点。 不工作怎么办?您收到什么错误或意外输出? 我更多地尝试了我的答案,并注意到它实际上返回了两个文本节点而不是一个。如果您的 XPath 处理器支持具有 string-join() 函数的 XPath 版本,则可以将其作为一个字符串返回,但您的确切空白要求可能难以实现。

以上是关于仅使用 Xpath 提取 <br> 标签内部分文本的文本的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Response.xpath 中排除特定标签(<br>)?

如何从 XML 中仅提取标签名称(而不是值),最终使用 XPath

XPath表达式(阿里云大学)

XPath 选择内文

使用 xpath 从最新节点中提取元素值

使用 beautifulsoup 提取换行符之间的文本(例如 <br /> 标签)