In BeautifulSoup 4.7.0+, how can I select all elements that don't contain the specified text in one of their properties

Viewed 201

I want to select all anchor tags that do not contain mailto: in their href property.

Up until version 4.7.0 of BeautifulSoup, I was able to use this code:

links = soup.select("a[href^=mailto:]")

Version 4.7.0 of BeautifulSoup replaced their CSS selector implementation with SoupSieve, which is supposed to be more modern and complete.

Unfortunately, the above code now throws this error:

soupsieve.util.SelectorSyntaxError: Malformed attribute selector

What is the drop-in replacement for that code? What is the proper way to target those same elements?

2 Answers

It appears that the colon in the href's value just needed to be escaped.

You can do that by escaping the individual character:

soup.select("a[href^=mailto\\:]")

Or by quoting the whole value:

soup.select('a[href^="mailto:"]')

IE < 8 doesn't recognize that escape \: so worth knowing you can also use code points. Also, you want to negate to exclude with :not (bs4 4.7.1+)

from bs4 import BeautifulSoup as bs

html = '''
<html>
 <head></head>
 <body>
  <a href="mailto:NotMe@somewhere.com" target="_blank">Nada</a> 
  <a href="https://www.address.com" target="_blank">Tada</a>
 </body>
</html>
'''
soup = bs(html, 'lxml')
print(soup.select('[href]:not([href*=mailto\\3A])'))

N.B. In browser would be [href*=mailto\3A]

Related