Pour résumer les commentaires :
-
sélectionner trouve plusieurs instances et renvoie une liste, trouver trouve le premier, afin qu'ils ne fassent pas la même chose. select_one serait l'équivalent de trouver .
- J'utilise presque toujours des sélecteurs css lors de l'enchaînement de balises ou de l'utilisation de tag.classname Si je cherche un seul élément sans classe, j'utilise trouver . Il s'agit essentiellement d'un cas d'utilisation et d'une préférence personnelle.
- En ce qui concerne la flexibilité, je pense que vous connaissez la réponse,
soup.select("div[id=foo] > div > div > div[class=fee] > span > span > a")
serait assez laid si l'on utilisait plusieurs find/find_all appels.
- Le seul problème avec les sélecteurs css dans bs4 est le support très limité, nième du type est la seule pseudo classe implémentée et le chaînage d'attributs comme a[href][src] n'est pas non plus supporté comme beaucoup d'autres parties des sélecteurs css. Mais des choses comme a[href \=..]* , a[href^=] , a[href$=] etc. sont, à mon avis, beaucoup plus agréables que les
find("a", href=re.compile(....))
mais là encore, il s'agit d'une préférence personnelle.
Pour des raisons de performance, nous pouvons effectuer quelques tests, j'ai modifié le code à partir d'un fichier réponse ici fonctionnant sur 800+ fichiers html provenant de ici Il n'est pas exhaustif mais devrait donner une idée de la lisibilité de certaines options et des performances :
Les fonctions modifiées sont les suivantes :
from bs4 import BeautifulSoup
from glob import iglob
def parse_find(soup):
author = soup.find("h4", class_="h12 talk-link__speaker").text
title = soup.find("h4", class_="h9 m5").text
date = soup.find("span", class_="meta__val").text.strip()
soup.find("footer",class_="footer").find_previous("data", {
"class": "talk-transcript__para__time"}).text.split(":")
soup.find_all("span",class_="talk-transcript__fragment")
def parse_select(soup):
author = soup.select_one("h4.h12.talk-link__speaker").text
title = soup.select_one("h4.h9.m5").text
date = soup.select_one("span.meta__val").text.strip()
soup.select_one("footer.footer").find_previous("data", {
"class": "talk-transcript__para__time"}).text
soup.select("span.talk-transcript__fragment")
def test(patt, func):
for html in iglob(patt):
with open(html) as f:
func(BeautifulSoup(f, "lxml")
Passons maintenant aux horaires :
In [7]: from testing import test, parse_find, parse_select
In [8]: timeit test("./talks/*.html",parse_find)
1 loops, best of 3: 51.9 s per loop
In [9]: timeit test("./talks/*.html",parse_select)
1 loops, best of 3: 32.7 s per loop
Comme je l'ai dit, ce n'est pas exhaustif, mais je pense que nous pouvons dire sans risque que les sélecteurs css sont définitivement plus efficaces.