gpt4 book ai didi

python - Scrapy选择器 "a::text"和 "a::text"之间的区别

转载 作者:太空狗 更新时间:2023-10-30 00:42:42 25 4
gpt4 key购买 nike

我创建了一个刮板,以从网页上获取一些产品名称。运行正常。我已经使用CSS选择器来完成这项工作。但是,我唯一不了解的是选择器a::texta ::text之间的区别(请不要忽略后者中a::text之间的空间)。当我运行脚本时,无论选择哪个选择器,我都会得到完全相同的结果。

import requests
from scrapy import Selector

res = requests.get("https://www.kipling.com/uk-en/sale/type/all-sale/?limit=all#")
sel = Selector(res)
for item in sel.css(".product-list-product-wrapper"):
title = item.css(".product-name a::text").extract_first().strip()
title_ano = item.css(".product-name a ::text").extract_first().strip()
print("Name: {}\nName_ano: {}\n".format(title,title_ano))

如您所见, titletitle_ano都包含相同的选择器,并在后者中留出空格。尽管如此,结果始终是相同的。

我的问题是:两者之间是否有实质性区别?我应该何时使用前者?何时使用后者?

最佳答案

有趣的观察!我花了过去的两个小时研究这个问题,结果发现,它所带来的不仅仅是眼神。
如果您来自CSS,则可能希望以与a::texta::first-linea::first-lettera::before相同的方式编写a::after。没有惊喜。
另一方面,标准选择器语法建议a ::text::text元素的后代的a伪元素匹配,使其等效于a *::text。但是,.product-list-product-wrapper .product-name a没有任何子元素,因此按权利,a ::text应该不匹配。它确实匹配的事实表明Scrapy没有遵循语法。
Scrapy使用Parsel(基于cssselect本身)将选择器转换为XPath,即::text的来源。考虑到这一点,让我们研究一下Parsel如何实现::text:

>>> from parsel import css2xpath
>>> css2xpath('a::text')
'descendant-or-self::a/text()'
>>> css2xpath('a ::text')
'descendant-or-self::a/descendant-or-self::text()'
因此,就像cssselect一样,跟随后代组合器的所有内容都将转换为 descendant-or-self轴,但是由于文本节点是DOM中元素节点的适当子代,因此 ::text被视为独立节点,并直接转换为 text(),后者与 descendant-or-self一起使用轴,匹配 a元素后代的任何文本节点,就像 a/text()匹配 a元素的任何文本节点子代(子代也是子代)一样。
令人震惊的是,即使将显式的 *添加到选择器中,也会发生这种情况:
>>> css2xpath('a *::text')
'descendant-or-self::a/descendant-or-self::text()'
但是, descendant-or-self轴的使用意味着 a ::text可以匹配 a元素中的所有文本节点,包括嵌套在 a中的其他元素中的所有文本节点。在以下示例中, a ::text将匹配两个文本节点: 'Link '后跟 'text':
<a href="https://example.com">Link <span>text</span></a>
因此,尽管Scrapy的 ::text的实现严重违反了Selectors语法,但似乎是非常有意地这样做的。
实际上,Scrapy的其他伪元素 ::attr() 1的行为类似。当没有任何子元素时,以下选择器都与属于 id元素的 div属性节点匹配:
>>> css2xpath('div::attr(id)')
'descendant-or-self::div/@id'
>>> css2xpath('div ::attr(id)')
'descendant-or-self::div/descendant-or-self::*/@id'
>>> css2xpath('div *::attr(id)')
'descendant-or-self::div/descendant-or-self::*/@id'
...但是 div ::attr(id)div *::attr(id)将匹配 id的后代中的所有 div属性节点及其自己的 id属性,例如以下示例:
<div id="parent"><p id="child"></p></div>
当然,这是一个不太合理的用例,因此必须怀疑这是否是 ::text实现的意外副作用。
将伪元素选择器与将任何简单选择器替换为伪元素的选择器进行比较:
>>> css2xpath('a [href]')
'descendant-or-self::a/descendant-or-self::*/*[@href]'
这样可以使用附加的隐式 descendant-or-self::*/*轴将后代组合器正确转换为 child,从而确保从未在 [@href]元素上测试 a谓词。
如果您不熟悉XPath,选择器,甚至不熟悉Scrapy,这似乎都非常令人困惑和不知所措。因此,这是何时使用一个选择器而不是另一个选择器的摘要:
  • 如果您的a::text元素仅包含文本,或者您仅对该a元素的顶级文本节点而不是其嵌套元素感兴趣,请使用a
  • 如果您的a ::text元素包含嵌套元素,并且您要提取此a元素内的所有文本节点,请使用a
    如果您的a ::text元素仅包含文本,则可以使用a,但是其语法令人困惑,因此,为了保持一致,请改为使用a::text

  • 1有趣的是, ::attr()出现在(从2021年开始被废弃) Non-element Selectors spec中,正如您所期望的,它的行为与Selectors语法一致,从而使其在Scrapy中的行为与规范不一致。另一方面,规范中明显缺少 ::text;基于此答案,我认为您可以对原因做出合理的猜测。

    关于python - Scrapy选择器 "a::text"和 "a::text"之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48556919/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com