Лайфхак для разбора контента на странице с Python
Одной из наиболее муторных вещей при написании парсера/скрапера является реализация функции скачивания содержимого из заданных мест на странице. Процесс поиска последовательности тегов для перехода к интересуемой информации сталкивается с различными трудностями, одной из которых является дублирование тегов в разных местах веб-страницы.
Например, продолжим разбор страницы детализированной статистики поединков в рамках UFC, имеющей следующий вид:
Ранее мы вывели информацию из верхней части этой страницы (смотри статью), теперь рассмотрим как выглядит нижняя половина:
То есть информация о нанесенных ударах - общая, по каждому раунду, а также с разбивкой на области повреждения, находятся в тегах с одинаковым названием 'section' и с полем 'class'='b-fight-details__section js-fight-section'}. При этом они также содержат таблицы с одинаковыми названиями ячейками (тег th). Если мы последуем прежней стратегии поиска и, например, попытаемся получить содержание полей из сектора с заголовком "TOTALS" на картинке, то получим смесь полей (включая, например, названия из подтаблицы "SIGNIFICANT STRIKES"):
delay = 1
url = 'http://www.ufcstats.com/fight-details/524b49a676498c6d'
html = net_scrape.get_url_delay(delay=delay, url = url).text
bsObj = BeautifulSoup(html, 'lxml')
total_res = bsObj.findAll('th',{'class':'b-fight-details__table-col'})
[item.get_text().strip() for item in total_res]
В этом случае лучше работать с участками страницы раздельно. Так, в нашем случае мы могли бы получить секции и загружать информацию из каждой по отдельности либо задать объемлющую таблицу для наших ячеек (если такой же другой нет, конечно). Для этого я написал небольшую функцию (реализована как метод класса), которая возвращает определенный набор тегов, содержащихся в некотором контейнере с заданными параметрами. На входе она принимает url или объект из библиотеки BeautifulSoup (страница или тег), а также словарь или строку с описанием тега контейнера - его имени, свойства (например, 'class') и значения, а также аналогичную информацию для описания содержимых тегов. Если вторым или третьим параметром передается строка, то нужно разделить все слова запятыми и она автоматически будет преобразована в словарь:
Если теперь поискать ту же информацию, но задав объемлющий контейнер при помощи нашей функции, то получим ожидаемый результат:
total_res = ItemsParser.get_items_list_from2tags(bsObj,'tr,class,b-fight-details__table-row', 'th,class,b-fight-details__table-col', delay)
[item.get_text().strip() for item in total_res]