Посторонние теги из веб-страниц для подзаголовков

#python #regex #web-scraping #beautifulsoup

Вопрос:

Я пытаюсь очистить страницу Википедии «Геном». Я хочу очистить только подзаголовки, такие как «Происхождение термина», «Секвенирование и картирование», «Вирусные геномы», «Прокариотические геномы», «Эукариотические геномы», включая подзаголовки под этим, Размер генома и т. Д. и т. Д. . Для этого я написал следующий код, чтобы сделать это:

     def filter_headers(self, web_soup):
        # Grabs the headers from the web page
        """
        :param web_soup: the raw web soup from the webpage
        :return: header_soup: the headers in text form
        """
        # TO DO: how to separate out just the main body content while including the
        # title header
        # Find all tags with a pattern like h1,h2,h3,h4...
        headers = read_page_soup.find_all(re.compile(r'hd '))

        return headers
 

Проблема в том, что независимо от того, насколько конкретно я отфильтровываю свои теги, я все равно получаю те же результаты с посторонними заголовками из меню навигации, такими как [Личные инструменты, пространства имен, Варианты, Представления…Инструменты,Печать/Экспорт, В Других Проектах, Языках]. Например, я попробовал это первым:

 soup = read_page_soup.find(re.compile(r'hd '), {'class': 'mw-body-content'})
    results = sr.filter_headers(soup)
    for result in results:
        print(result.text)
 

Затем я попробовал это, фильтруя через класс вывода mw-синтаксического анализатора, вот так:

 soup = read_page_soup.find(re.compile(r'hd '), {'class': 'mw-parser-content'})
    results = sr.filter_headers(soup)
    for result in results:
        print(result.text)
 

Я этого не понимаю. Дивы, над которыми я нависаю, даже не выделяют боковую панель википедии, когда я наведу на них курсор. Я хочу иметь возможность найти решение, которое работает для многих страниц Википедии, чтобы я мог очистить их все с одинаковыми результатами. Затем в будущем я надеюсь распространить это на другие веб-страницы, такие как Ars Technica. Так что, если кто-нибудь также может дать какие-либо справедливые предупреждения, я думаю, об использовании этого подхода для некоторого рудиментарного обхода веб-страниц. Если я не в состоянии разобраться в этом в каждом конкретном случае для какого-то рудиментарного обхода веб-страниц без приложения для обхода веб-страниц, дайте мне знать.

Ответ №1:

Вы пытаетесь получить тег заголовка, но оба класса, которые вы описываете, таковы divs . Страницы Википедии , которые я тестировал, обычно имеют три mw-body-content класса (все они есть div , то же самое происходит с одним mw-parser-output классом).:

 <div id="siteNotice" class="mw-body-content">
<div class="mw-indicators mw-body-content">
<div id="bodyContent" class="mw-body-content">
 

Чтобы получить несколько заголовков страниц, вы можете использовать mw-body-content класс, который у вас уже есть, но переключитесь на find_all него и выберите третий элемент. С этого момента логика, используемая внутри filter_headers с find_all и регулярное выражение для header , кажется, дает ожидаемый результат.

 from bs4 import BeautifulSoup
import requests
import re

source = requests.get('https://en.wikipedia.org/wiki/Genome').text
read_page_soup = BeautifulSoup(source, 'lxml')

def filter_headers(web_soup):
    headers = web_soup.find_all(re.compile(r'hd '))
    return headers

soup = read_page_soup.find_all("div", {'class': 'mw-body-content'})[2]
results = filter_headers(soup)
for result in results:
    hN = int(result.name[1:])*3-3
    print(f"{result.name} {'-'*hN} {result.text}")
 

Выход

 h2 --- Contents
h2 --- Origin of term[edit]
h2 --- Sequencing and mapping[edit]
h2 --- Viral genomes[edit]
h2 --- Prokaryotic genomes[edit]
h2 --- Eukaryotic genomes[edit]
h3 ------ Coding sequences[edit]
h3 ------ Noncoding sequences[edit]
h4 --------- Tandem repeats[edit]
h4 --------- Transposable elements[edit]
h5 ------------ Retrotransposons[edit]
h5 ------------ DNA transposons[edit]
h2 --- Genome size[edit]
h3 ------ Genome size due to transposable elements[edit]
...
...
 

Комментарии:

1. Я думаю, что это сработало, когда я изменил свой синтаксический анализатор с html на lxml и получал из запросов вместо чего-то более специализированного, такого как urlopen из запросов

2. Кроме того, считаете ли вы, что такой поэтапный подход к очистке веб-страниц будет хорошо работать для базового обхода веб-страниц?

3. @pancham2016 Для базового обхода веб-страниц я думаю, что этот подход подходит. Вам просто нужно обратить внимание на сайты, которые dynamically загружают данные с помощью javascript . В этом контексте вам придется использовать что-то вроде Селена для получения данных.

Ответ №2:

Не могли бы вы просто взять эту информацию из li элементов в списке содержимого #toc li ? Есть два дочерних spans элемента , на каждого li , содержащих нумерацию маркеров и имя:

 import requests
from bs4 import BeautifulSoup as bs

r = requests.get('https://en.wikipedia.org/wiki/Genome')
soup = bs(r.content, 'lxml')
contents = [i.select_one('.tocnumber').text   ' '   i.select_one('.toctext').text  for i in soup.select('#toc li')]