"has:()" and "not:()" pseudo-classes behaves otherwise when used within BeautifulSoup

Viewed 425

I'm trying to figure out how css pseudo-classes like not:() and has:() work in the following cases.

The following selector is not supposed to print 27A-TAX DISTRICT 27A but it does print it:

from bs4 import BeautifulSoup

htmlelement = """
<tbody>
  <tr style="">
     <td><a>27A-TAX DISTRICT</a> 27A</td>
  </tr>

  <tr style="">
     <td><strong>Parcel Number</strong> 720</td>
  </tr>
</tbody>
"""
soup = BeautifulSoup(htmlelement,"lxml")
item = soup.select_one("tr:not(a)").text
print(item)

On the other hand, the following selector is supposed to print I should be printed but it throws AttributeError error.

from bs4 import BeautifulSoup

htmlelement = """
<p class="vital">I should be printed</p>
<p>I should not be printed</p>
"""
soup = BeautifulSoup(htmlelement,"lxml")
item = soup.select_one("p:has(.vital)").text
print(item)

Where I'm going wrong and how can I make them work?

1 Answers

Unfortunately, your understanding of what :not() and :has() does is most likely not correct.

In your first example, you use:

soup.select_one("tr:not(a)").text

The way you are using it will select every tr. This is because it is saying "I want a tr tag that is not an a tag. tr tags are never a tags so your code always grabs the text of any tr tag, including the one that contains 27A-TAX DISTRICT.

If you want tr tags that don't have a tags, then you could use:

soup.select_one("tr:not(:has(a))").text

What this says is "I want a tr tag that does not have a descendant a tag".

For more info read:


This leads us to your second issue. :has() is a relational selector. In your second example, you used:

soup.select_one("p:has(.vital)").text

:has() looks ahead at either children, descendants, or sibling (depending on the syntax you use) to determine if the tag is the the one you want.

So what you were saying was "I want a p tag that has a descendant tag with the class vital". None of your p tags even have descendants, so there is no way one could have a vital class. What you want is actually more simple:

soup.select_one("p.vital").text

What this says is "I want a p tag that also has a class vital."

For more info read:

Related