Essayer de faire correspondre des liens qui contiennent certains textes. Je fais

links = soup.find_all('a',href=lambda x: ".org" in x)

Mais cela renvoie un argument TypeError: de type 'NoneType' n'est pas itérable.

La bonne façon de procéder est apparemment

links = soup.find_all('a',href=lambda x: x and ".org" in x)

Pourquoi le x and supplémentaire est-il nécessaire ici?

3
shem 15 avril 2018 à 12:38

3 réponses

Meilleure réponse

Il y a une raison simple: l'une des balises <a> de votre code HTML n'a pas de propriété href.


Voici un exemple minimal qui reproduit l'exception:

html = '<html><body><a>bar</a></body></html>'
soup = BeautifulSoup(html, 'html.parser')

links = soup.find_all('a', href=lambda x: ".org" in x)
# result:
# TypeError: argument of type 'NoneType' is not iterable

Maintenant, si nous ajoutons une propriété href, l'exception disparaît:

html = '<html><body><a href="foo.org">bar</a></body></html>'
soup = BeautifulSoup(html, 'html.parser')

links = soup.find_all('a', href=lambda x: ".org" in x)
# result:
# [<a href="foo.org">bar</a>]

Ce qui se passe, c'est que BeautifulSoup essaie d'accéder à la propriété <a> de la balise href, et cela renvoie None lorsque la propriété n'existe pas:

html = '<html><body><a>bar</a></body></html>'
soup = BeautifulSoup(html, 'html.parser')

print(soup.a.get('href'))
# output: None

C'est pourquoi il est nécessaire d'autoriser les None valeurs dans votre lambda. Puisque None est une valeur fausse, le code x and ... empêche l'exécution du côté droit de l'instruction and lorsque x est None, comme vous pouvez le voir ici:

>>> None and 1/0
>>> 'foo.org' and 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

Cela s'appelle court-circuitage.


Cela dit, x and ... vérifie la véracité de x, et None n'est pas la seule valeur considérée comme fausse. Il serait donc plus correct de comparer x à None comme ceci:

lambda x: x is not None and ".org" in x
2
Aran-Fey 15 avril 2018 à 09:59

Le x supplémentaire évite le problème que vous avez rencontré, à savoir TypeError: argument of type 'NoneType'. Essayez d'appeler la fonction lambda avec None comme argument:

>>> f = lambda x: ".org" in x
>>> f
<function <lambda> at 0x7f5dd1215ea0>
>>> f(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: argument of type 'NoneType' is not iterable
>>> f('abcd.org/blah')
True

Le premier x de x and ".org" in x teste si x est "véridique". Si c'est le reste de l'expression est évaluée. Si ce n'est pas "véridique", par ex. il s'agit de None, alors la deuxième partie de l'expression and est court-circuitée et non exécutée. Cela évite de tenter d'effectuer l'opération in, évitant ainsi le problème.

0
mhawke 15 avril 2018 à 09:47

Le problème est qu'une balise <a ...> peut ne pas avoir de partie href=... et dans ce cas, vous obtenez un None (qui ne peut pas être utilisé avec l'opérateur in).

0
6502 15 avril 2018 à 09:51