diff --git a/parsel/selector.py b/parsel/selector.py index 7b8a5552..cc365265 100644 --- a/parsel/selector.py +++ b/parsel/selector.py @@ -140,6 +140,16 @@ def extract_first(self, default=None): return default get = extract_first + @property + def attrib(self): + """Return the attributes dictionary for the first element. + If the list is empty, return an empty dict. + """ + for x in self: + return x.attrib + else: + return {} + class Selector(object): """ @@ -330,6 +340,12 @@ def remove_namespaces(self): # remove namespace declarations etree.cleanup_namespaces(self.root) + @property + def attrib(self): + """Return the attributes dictionary for underlying element. + """ + return dict(self.root.attrib) + def __bool__(self): """ Return ``True`` if there is any real content selected or ``False`` diff --git a/tests/test_selector.py b/tests/test_selector.py index 3ccc0c47..c5926a72 100644 --- a/tests/test_selector.py +++ b/tests/test_selector.py @@ -101,6 +101,34 @@ def test_simple_selection_with_variables_escape_friendly(self): lng=lt)], [u'a']) + def test_accessing_attributes(self): + body = u""" + + + + + + """ + sel = self.sscls(text=body) + self.assertEquals({'lang': 'en', 'version': '1.0'}, sel.attrib) + self.assertEquals({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul')[0].attrib) + + # for a SelectorList, bring the attributes of first-element only + self.assertEquals({'id': 'some-list', 'class': 'list-cls'}, sel.css('ul').attrib) + self.assertEquals({'class': 'item-cls', 'id': 'list-item-1'}, sel.css('li').attrib) + self.assertEquals({}, sel.css('body').attrib) + self.assertEquals({}, sel.css('non-existing-element').attrib) + + self.assertEquals( + [{'class': 'item-cls', 'id': 'list-item-1'}, + {'class': 'item-cls active', 'id': 'list-item-2'}, + {'class': 'item-cls', 'id': 'list-item-3'}], + [e.attrib for e in sel.css('li')]) + def test_representation_slice(self): body = u"

".format(50 * 'b') sel = self.sscls(text=body)