Как можно извлечь данные с такого веб-сайта?

Я понимаю, что мой вопрос может показаться не самым удачным, но я действительно изо всех сил пытаюсь разобраться в данной задаче. Я даже обращался к современным ИИ для помощи в создании алгоритма, однако безуспешно. 

Мне необходимо собрать названия школ и адреса их веб-сайтов. Я сталкиваюсь с двумя типами веб-страниц, которые могут быть следующими: 
1. Страницы, содержащие списки городов или районов 
2. Страницы с адресами школ в конкретном городе или районе 

Проще говоря, адрес к нужной странице может выглядеть как: Уфа->Дема->*Адреса школ или как: Янаул->*Адреса школ. Таким образом, иерархия всегда произвольная, но адреса школ всегда находятся на конечном уровне. 

На страницах со списками городов/районов/республик я ищу все теги *a внутри тега *div, а на страницах с адресами школ обращаю внимание на теги *a в таблице. 

Теперь я не знаю, как реализовать это рекурсивно, избегая множества вложенных циклов. Есть ли у кого-либо идеи по этому поводу?
  • 6 декабря 2024 г. 16:53
Ответы на вопрос 2
Ваше задание по извлечению данных с веб-сайтов можно решить с помощью веб-скрейпинга, который включает в себя рекурсивное обход всех необходимых страниц. Для реализации вашей задачи в Python можно использовать библиотеки `requests` и `BeautifulSoup`. Примерный алгоритм может выглядеть следующим образом:

1. Начинаем с основной страницы, содержащей списки городов/районов.
2. Извлекаем ссылоки на города/районы и рекурсивно переходим к ним.
3. Повторяем шаг 2 до тех пор, пока не достигнем страницы, содержащей адреса школ.
4. На странице с адресами школ извлекаем название и ссылки на веб-сайты школ.

Вот пример кода, который демонстрирует этот процесс:

```python
import requests
from bs4 import BeautifulSoup

def scrape_school_links(url, visited=None):
    if visited is None:
        visited = set()  # Для отслеживания посещенных страниц

    if url in visited:
        return
    visited.add(url)

    try:
        response = requests.get(url)
        response.raise_for_status()  # Проверка ошибок HTTP
    except requests.RequestException as e:
        print(f"Ошибка получения {url}: {e}")
        return

    soup = BeautifulSoup(response.text, 'html.parser')

    # Проверяем на наличие ссылок на школы
    school_links = []
    table = soup.find('table')  # Предполагаем, что адреса школ в таблице
    if table:
        for row in table.find_all('tr'):
            # Извлекаем название школы и ссылку на сайт
            columns = row.find_all('td')
            if len(columns) > 1:
                school_name = columns[0].get_text(strip=True)
                school_url = columns[1].find('a')['href'] if columns[1].find('a') else None
                if school_url:
                    school_links.append((school_name, school_url))

        print(f"Найдены школы на {url}: {school_links}")
    
    # Проверяем наличие ссылок на города/районы
    divs = soup.find_all('div')  # Предполагаем, что ссылки на города/районы внутри div
    for div in divs:
        for a in div.find_all('a', href=True):
            next_url = a['href']
            if next_url.startswith('/'):  # Относительный путь
                next_url = f"{url}/{next_url}"
            scrape_school_links(next_url, visited)  # Рекурсивный вызов

# Пример начального URL
start_url = "http://example.com/cities"  # Замените на реальный URL
scrape_school_links(start_url)
```

### Пояснения к коду:
- **requests.get** используется для получения HTML-кода страницы.
- **BeautifulSoup** позволяет парсить HTML и извлекать данные.
- В **scrape_school_links** реализован рекурсивный обход, который сначала собирает ссылки на школы, а затем ищет города/районы.
- Использование **set()** для хранения уже посещённых URL позволяет избежать бесконечных циклов.

### Важно:
1. Убедитесь, что у вас есть разрешение на скрейпинг данных с сайта.
2. Обратите внимание на правила `robots.txt` сайта, который вы собираетесь скрейпить.
3. Настройте задержки между запросами, чтобы не перегружать сервер.

Этот код служит общим шаблоном, и вам, возможно, потребуется адаптировать его в зависимости от структуры конкретного сайта.
import requests
from bs4 import BeautifulSoup

def parse_table(div, results):
    # Ищем таблицы внутри данного div
    tables = div.find_all('table')
    for table in tables:
        rows = table.find_all('tr')
        for row in rows:
            cells = row.find_all('td')
            if cells:
                # Извлекаем название и ссылку
                name = cells[0].get_text(strip=True)
                link = cells[0].find('a')['href'] if cells[0].find('a') else None
                if link:
                    results[name] = link

def recursive_parse(url, visited, results):

    if url in visited:
        return
    visited.add(url)
    print('Парсим URL:', url)

    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    # Ищем все div с itemscope
    divs = soup.find_all('div', itemscope=True)
    for div in divs:
        parse_table(div, results)  # Парсим таблицы внутри найденного div

    # Ищем все ссылки на подстраницы
    links = soup.find_all('a', href=True)
    for link in links:

        sub_url = link['href']
        # Проверяем, что ссылка ведет на подстраницу и не является текущим URL
        if 'respublika_bashkortostan' in sub_url and sub_url.startswith('http') and sub_url != url:
            recursive_parse(sub_url, visited, results)

# Начальная URL
start_url = 'https://russiaschools.ru/respublika_bashkortostan/'
visited_urls = set()
results_dict = {}
recursive_parse(start_url, visited_urls, results_dict)


for name, link in results_dict.items():
    print(f'Название: {name}, Ссылка: {link}')


З.Ы. я не знаю могут ли быть одинаковые наименования (например МБДОУ ДС №5) в разных районах, поэтому или просто выводите на экран или записывайте вместе с названием района.
Похожие вопросы