Ответ 1
Подводя итог комментариям:
- select находит несколько экземпляров и возвращает список, find находит первый, поэтому они не делают то же самое. select_one будет эквивалентом find.
- Я почти всегда использую селектора css при цепочке тегов или с использованием tag.classname, если вы ищете один элемент без класса, который я использую find. По сути, это сводится к прецеденту и личным предпочтениям.
- Что касается гибкости, я думаю, что вы знаете ответ,
soup.select("div[id=foo] > div > div > div[class=fee] > span > span > a")
выглядел бы довольно уродливо, используя множественные вызовы find/find_all. - Единственная проблема с селекторами css в bs4 - очень ограниченная поддержка, nth-of-type - это единственный псевдо-класс, реализованный и привязывающий атрибуты, такие как [href] [src], также не поддерживается, как и многие другие части css селекторы. Но такие вещи, как [href=..] *, a [href ^ =], a [href $=] и т.д., Я думаю гораздо приятнее, чем
find("a", href=re.compile(....))
, но опять же это личное предпочтение.
Для производительности мы можем запускать некоторые тесты, я изменил код из ответа здесь, работая на 800 + html файлах, взятых из здесь, не является исчерпывающим, но должен дать ключ к удобочитаемости некоторых параметров и производительности:
Модифицированные функции:
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")
Теперь для таймингов:
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
Как я уже сказал, не исчерпывающий, но я думаю, что мы можем с уверенностью сказать, что селектора css определенно более эффективны.