Маневры между тегами при парсинге с Python
Рассмотрим, как парсить элементы, для извлечения которых недостаточно обратиться лишь к одному тегу. Подобная ситуация может возникнуть, когда искомый набор значений содержится в одинаковых по названию и типам тегах. Разумным выходом зачастую является идентификация целевых элементов по родительским и дочерним тегам.
Освоим нюансы такой навигации на конкретном примере. Пусть перед нами стоит задача извлечь сведения о проведенном поединке между бойцами UFC (сайт - www.ufcstats.com).
Чтобы понять, в каких тегах содержатся искомые элементы следует навести курсор на элемент и нажать исследовать (в Firefox):
И первая строка описаний поединка и вторая содержатся в тегах <p> с атрибутом class="b-fight-details__text", а конкретные элементы а тегах <i>:
Чтобы извлечь нужные значения, следует обратиться к содержимому тегов <i> с классом b-fight-details__label:
Для парсинга элемента можно извлечь его имя с помощью метода get_text, а чтобы получить свойство - обратиться к родителю, извлечь содержимое с get_text и заменить имя на пустую строку. Загрузим страницу и проделаем описанную работу (часть шагов рассматривалась ранее):
Найдем все теги с <i> с классом b-fight-details__label и исследуем первый:
а теперь посмотрим на родителя:
Этот алгоритм поможет скачать практически все нужные элементы, поэтому облечем часть действий в функцию, которая получает тег, содержащий в тексте значение свойства и хотя бы один вложенный тег (первый из которых - с названием свойства, например Method:):
def get_dict_tags_text(parent_tag): first_child = [it for it in parent_tag if isinstance(it, bs4.element.Tag)][0] key = first_child.get_text().strip() value = parent_tag.get_text().strip().replace(key,'').strip() return (key, value)
Вторая строка (где поле Details:) сложнее, так как она может иметь несколько видов:
такой, когда дело дошло до решения судей:
Для решения задачи потребуется переместится на два уровня наверх, если только один вложенный тег, то вызвать функцию get_dict_tags_text, иначе для всех "подтегов" склеить результаты голосования судей опять же с помощью get_dict_tags_text:
Загрузим новую ссылку (используйте другой url, который был выше закомментирован), извлечем все теги с <i> с классом b-fight-details__label, рассмотрим элемент прародителя нашего свойства Details:
Теперь для этого элемента осталось пройтись по списку дочерних тегов:
f_det_res_t = bsObj.findAll('i',{'class':'b-fight-details__label'}) det_res = {} for i,f_det_res_tag in enumerate(f_det_res_t): key = f_det_res_tag.get_text().strip() if not key=='Details:': k, v = get_dict_tags_text(f_det_res_tag.parent) det_res[k] = v else: det_tag = f_det_res_tag.parent.parent childs = list(det_tag.children) childs = [it for it in childs if isinstance(it, bs4.element.Tag)] if len(childs)==1: k, v = get_dict_tags_text(det_tag) det_res[k] = v else: s = '' for child_tag in childs[1:]: s = s + ':'.join(get_dict_tags_text(child_tag))+';' det_res[key] = s det_res