scrapy 选择器错误:发生异常:AttributeError 'str'对象没有属性'attrib'

jhkqcmku  于 2022-11-09  发布在  其他
关注(0)|答案(1)|浏览(212)

我正在this site上的table上试着刮第三场网球比赛的分数。
我需要从这个html元素中将匹配分数解析为单独的集合分数:

html = (
    '''
    <td class="day-table-score">
        <a data-override-transition="" data-ga-category="" data-ga-action="Click" data-ga-label="" data-use-ga="true" class="not-in-system">
            <!-- Determine set tie break score --> 76 <sup>8</sup>
            <!-- Determine set tie break score --> 61
        </a>
    </td>
    '''
)

为了模拟HtmlResponse对象的刮擦,我在这篇文章中使用了以下代码:

from scrapy.http.response.html import HtmlResponse

response = HtmlResponse("xyz", body=html, encoding="utf-8")

我可以使用以下xpath获得匹配分数的原始文本字符串:

response.xpath("normalize-space()").get()

然而,构建一个解析器来解释网球比赛中存在的所有不同版本的抢七将是一项相当繁重的工作。更容易的是能够根据它总是位于上标元素中来识别抢七得分。由于顺序很重要,我能想到的最好的解决方案是循环遍历每一行文本。确定该行是文本还是sup标签,然后将其分配给一个设置得分容器。

{"set_1_score": 76, "set_1_tiebreak_score": 8, "set_2_score": 61, "set_2_tiebreak_score": None}

因为在这个过程中要跟踪集合编号等会有一些额外的复杂性,所以我将代码简化为只打印一行文本是否实际上是一个sup标签。从HtmlResponse对象中,我可以看到有一个attrib属性,我认为这是我所需要的。但是,我似乎无法访问它。下面的代码:

text_lines = response.xpath("//text()")
for tl in text_lines:
    print(tl.attrib)

给出以下错误:

Exception has occurred: AttributeError
'str' object has no attribute 'attrib'

这是特别奇怪的,因为当我运行type(tl)时,它实际上是一个Selector对象,而不是str
下面也是tl对象的详细信息:

special variables:
function variables:
class variables:
attrib: 'Traceback (most recent call last):\n  File "/Users/philipjoss/.vscode/extensions/ms-python.python-2022.18.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py", line 162, in _get_py_dictionary\n    def _get_py_dictionary(self, var, names=None, used___dict__=False):\n  File "/Users/<me>/opt/miniconda3/envs/capra/lib/python3.9/site-packages/parsel/selector.py", line 387, in attrib\n    @property\nAttributeError: \'str\' object has no attribute \'attrib\'\n'
namespaces: {'re': 'http://exslt.org/reg...xpressions', 'set': 'http://exslt.org/sets'}
response: None
root: '\n        '
text: 'Traceback (most recent call last):\n  File "/Users/<me>/.vscode/extensions/ms-python.python-2022.18.1/pythonFiles/lib/python/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_resolver.py", line 162, in _get_py_dictionary\n    def _get_py_dictionary(self, var, names=None, used___dict__=False):\nAttributeError: text\n'
type: 'html'
_css2xpath: <bound method Selector._css2xpath of <Selector xpath='//text()' data='\n        '>>
_csstranslator: <parsel.csstranslator.HTMLTranslator object at 0x7fbfca5cf850>
_default_namespaces: {'re': 'http://exslt.org/reg...xpressions', 'set': 'http://exslt.org/sets'}
_default_type: None
_expr: '//text()'
_get_root: <bound method Selector._get_root of <Selector xpath='//text()' data='\n        '>>
_lxml_smart_strings: False
_parser: <class 'lxml.html.HTMLParser'>
_tostring_method: 'html'

我想这是一个由两部分组成的问题:
1.为什么我会得到我是的错误?看起来parsel包中可能有问题?
1.这是循环遍历文本元素的不同行以构建词典的最佳方式吗?

bfhwhh0e

bfhwhh0e1#

出现此错误的原因是,当您使用.../text() xpath选择器时,返回值可能是一个选择器,但该选择器只是从查询中提取的字符串的 Package 器。而纯文本字符串没有任何属性。
我认为一个类似但更好的解决方案是,在迭代每个td.day-table-score的直接子元素选择器时,测试<sup>元素是否存在。如果它不是一个标签,那么可以假设它是纯文本,并将其指定为新集合的得分,否则可以将其指定为字典中前一个集合的平局决胜者。
例如:

import scrapy

class MySpider(scrapy.Spider):
    name = "myspider"
    start_urls = ["https://www.atptour.com/en/scores/archive/waikoloa/574/2000/results"]

    def parse(self, response):
        for tag in response.css("td.day-table-score a"):
            score = {}  # keeps track of score for this table row
            for elem in tag.xpath('./text()|sup'):   
                if elem.re('<sup>'):
                    val = elem.xpath('./text()').get().strip()
                    s = len(score)
                    score[f"set_{s}"]["tiebreaker"] = val
                else:
                    s = len(score) + 1
                    val = elem.get().strip()
                    if val:
                        score[f"set_{s}"] = {"score": val, "tiebreaker": None}
            yield score  # the final score collection

OUTPUT

{'set_1': {'score': '75', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '46', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}, 'set_3': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '76', 'tiebreaker': '8'}, 'set_2': {'score': '61', 'tiebreaker': None}}
{'set_1': {'score': '63', 'tiebreaker': None}, 'set_2': {'score': '75', 'tiebreaker': None}}
{'set_1': {'score': '61', 'tiebreaker': None}, 'set_2': {'score': '61', 'tiebreaker': None}}
{'set_1': {'score': '62', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '76', 'tiebreaker': '1'}, 'set_2': {'score': '63', 'tiebreaker': None}}
{'set_1': {'score': '63', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '62', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '63', 'tiebreaker': None}, 'set_2': {'score': '67', 'tiebreaker': '2'}, 'set_3': {'score': '63', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '67', 'tiebreaker': '2'}, 'set_2': {'score': '62', 'tiebreaker': None}, 'set_3': {'score': '75', 'tiebreaker': None}}
{'set_1': {'score': '36', 'tiebreaker': None}, 'set_2': {'score': '76', 'tiebreaker': '5'}, 'set_3': {'score': '61', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '63', 'tiebreaker': None}}
{'set_1': {'score': '61', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '46', 'tiebreaker': None}, 'set_2': {'score': '76', 'tiebreaker': '10'}, 'set_3': {'score': '76', 'tiebreaker': '4'}}
{'set_1': {'score': '63', 'tiebreaker': None}, 'set_2': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '63', 'tiebreaker': None}}
{'set_1': {'score': '64', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '60', 'tiebreaker': None}, 'set_2': {'score': '36', 'tiebreaker': None}, 'set_3': {'score': '64', 'tiebreaker': None}}
{'set_1': {'score': '75', 'tiebreaker': None}, 'set_2': {'score': '61', 'tiebreaker': None}}
{'set_1': {'score': '62', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}
{'set_1': {'score': '61', 'tiebreaker': None}, 'set_2': {'score': '62', 'tiebreaker': None}}

相关问题