There is a reason why the internet is packed with alternative approaches. I can't really think of any situation where you would be forced to use bash for this. Why not use one of the tools designed for the job?
Anyway, as far as I know there is no way of doing non-greedy matches using the
=~ operator. That's because it does not use bash's internal regex engine but your system's C one as defined in
man 3 regex. This is explained in
An additional binary operator, =~, is available, with the same prece‐
dence as == and !=. When it is used, the string to the right of the
operator is considered an extended regular expression and matched
accordingly (as in regex(3)).
You can, however, do more or less what you want (bearing in mind that this is really not a good way of parsing HTML files) with a slightly different regex:
string='<span class="circle"> </span>foo</span></span>'
regex='<span class="circle"> </span>([^<]+)</span>'
[[ $string =~ $regex ]];
The above will return
foo as expected.