Всем привет, сегодня я вам покажу и расскажу, как можно легко написать парсер для сбора лучших статей дня в виде json файла, в формате, "Название статьи": "ссылка". Кто не понял о каких лучших статьях дня я говорю, вот ссылка "https://habr.com/ru/top/daily/".

Итак, библиотеки, которые будут нам нужны (вставляем в командную строку, либо в терминале или куда вам удобно):

pip install beautifulsoup4
pip install requests
pip install fake-useragent
pip install lxml

Инициализируем модули в наш заранее созданный проект, т.е. файл с расширением py.

from bs4 import BeautifulSoup
import random
import json
import requests
import datetime
from fake_useragent import UserAgent

Создаем переменную с модулем fake_useragent, чтобы мы могли потом использовать для генерации user-agent:

ua = UserAgent()

Определяем заголовки (для того чтобы сервер сайта мог понять, как он должен отправить ответ, а также помогает серверу определить отправителя запроса)

headers = {
    'accept': 'application/json, text/plain, */*',
    'user-Agent': ua.google,
}

Создаем словарь, где будут храниться название и ссылка на каждую статью:

article_dict = {}

Создаем цикл для сбора со всех страниц, а не с одной (с 1 по 3, т.к. страниц с ссылками в день, как я понимаю всего 3).

for i in range(1, 4):

Указываем url c форматирование кода, где i - номер страницы, которое вставляться при каждом проходе цикла.

url = f'https://habr.com/ru/top/daily/page{i}/'

Отправляем get запрос на сайт, указывая в первом аргументе - переменную с url сайта, во-втором заголовки. Атрибут text, нужен чтобы получить текстовое содержанием html страницы.

req = requests.get(url, headers=headers).text

Теперь с помощью BeautifulSoup соберем весь html код страницы.

soup = BeautifulSoup(req, 'lxml')

Если попробовать вывести такой код с помощью print(soup), выведется весь html код страницы.

Далее, используя наш "soup" созданный в прошлом шаге, с помощью метода find_all собираем все ссылки с помощью тега "a" в первом аргументе, во-втором, с помощью F12, ищем класс у всех ссылок наших статей, как мы видим это - tm-article-snippet__title-link.

a - это тег, после точки - класс тега.
a - это тег, после точки - класс тега.
all_hrefs_articles = soup.find_all('a', class_='tm-article-snippet__title-link')

Класс указывается с нижним подчеркиванием, т.к. это ключевое (зарезервированное) слово в Python.

Создаем еще один цикл, где мы будем проходиться по всем статьям собранных в переменной all_hrefs_articles.

for article in all_hrefs_articles:

В теге "a" с классом "tm-article-snippet__title-link" находится еще один тег "span" c нашими именами ссылок, получим его с помощью метода find.

article_name = article.find('span').text # собираем названия статей

Теперь получим ссылку на статью, указываем что это f строка, и с помощью get запроса в скобочках получаем атрибут "href" - основной атрибут тега "a".

article_link = f'https://habr.com{article.get("href")}'

Получается ссылка, например: "https://habr.com/ru/company/tinkoff/blog/715604/"

Теперь указываем пару ключ - значение для названия и ссылку на статью (для нашего словаря):

article_dict[article_name] = article_link

Выходим из обоих циклов. С помощью конструкции "with open" создаем файл articles_ + дата и время создания файла с помощью модуля datetime, который мы импортировали, файл создаем с расширением .json (ну мне так удобнее), следующее мы указываем 'w', что означает, что нужно создать файл с таким-то названием и вписать следующий код, который мы укажем внутри файла, также указываем кодировку " encoding ='utf-8' ", чтобы файл мог отобразить русские символы.

with open(f"articles_{datetime.datetime.now().strftime('%d_%m_%Y')}.json", "w", encoding='utf-8') as f: 

Создаем конструкцию try, except (если нет ошибок при парсинге, выводится try, если выводится ошибка при парсинге => except)

try:

except:
  

В try, мы "говорим", чтобы в json файл отправлялись данные, 1 - словарь с нашими статьями , 2 - имя файла, куда отправлять данные (в открытии файла мы указали в конце его как f, чтобы с ним можно было работать), 3 - отступы (я сделал 4 для удобства чтения, можно указать свое), 4 - экранирование ASCII символов, и следующей строкой вывод, что статьи успешно были получены.

print('Статьи были успешно получены')

В except, мы выводим, что статьи не удалось получить и нужно искать проблемы в коде.

print('Статьи не удалось получить')

В конечном итоге, должно получиться что-то похожее:

from bs4 import BeautifulSoup
import random
import json
import requests
import datetime
from fake_useragent import UserAgent

ua = UserAgent()

headers = {
    'accept': 'application/json, text/plain, */*',
    'user-Agent': ua.google,
}

article_dict = {}

for i in range(1, 4):
    url = f'https://habr.com/ru/top/daily/page{i}/'

    req = requests.get(url, headers=headers).text

    soup = BeautifulSoup(req, 'lxml')
    all_hrefs_articles = soup.find_all('a', class_='tm-article-snippet__title-link') # получаем статьи

    for article in all_hrefs_articles: # проходимся по статьям
        article_name = article.find('span').text # собираем названия статей
        article_link = f'https://habr.com{article.get("href")}' # ссылки на статьи
        article_dict[article_name] = article_link

with open(f"articles_{datetime.datetime.now().strftime('%d_%m_%Y')}.json", "w", encoding='utf-8') as f: 
    try:    
        json.dump(article_dict, f, indent=4, ensure_ascii=False)
        print('Статьи были успешно получены')
    except:
        print('Статьи не удалось получить')

Комментарии (14)


  1. sshikov
    00.00.0000 00:00

    Во-первых, я начинающий программист.

    Хм. Ну давайте без обид, но это — в десятых.

    Во-первых, нужно бы пояснить, что вы понимаете под лучшими статьями (строго говоря, это вообще субъективно, если от статьи с высоким рейтингом лично меня тошнит — она плохая или хорошая?). Вы, кстати, вообще не описали как следует, как отбираются именно лучшие статьи, и почему получается именно за день. Неявно, из URL, можно догадаться, что вы отбираете топ Хабра. То есть своего критерия лучшести у вас нет. И судя по всему, вы даже рейтинги статей, авторов, их карму, и т.п. показатели не вытаскивали. И своего анализа провести не сможете.

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

    Ну и в третьих, это настолько рутинная задача, что для ее решения (в намного более сложных форматах, а аутентификацией, капчей и прочим) придумано масса инструментов. И подобные задачи тут описаны тысячи раз. Так что третьим пунктом должна бы быть причина, почему именно вам нужно написать еще одну статью не эту изъезженную тему? То что вы начинающий — не повод сразу начинать публиковать свои решения, даже если для вас они новые и интересные. Читателям-то почему это должно быть интересно?


    1. hezerford Автор
      00.00.0000 00:00

      Спасибо, за комментарий! Я учту моменты и постараюсь исправиться и становиться лучше, если ответить на первое, то это выбираю не я, а есть раздел на Хабре, где можно посмотреть лучшие статьи за день, а насчет цели, она была в том, чтобы собрать все статьи вместе и проанализировать какие статьи тебе интересны, возможно перейти и прочитать. Ещё раз благодарю вас за потраченное время!


    1. Kitmod_py
      00.00.0000 00:00

      Не очень корректный комментарий.

      1. Есть категория читателей Хабра, которым это действительно может быть важно, полезно и интересно.

      2. По Вашему, публикацией статей необходимо заниматься исключительно после того как разработаешь собственную ОС? В заголовках статей есть критерий сложности, который стоит учитывать при прочтении и комментировании


  1. BlackDiver
    00.00.0000 00:00
    +9

    А хорошо было бы, если бы на Хабре появился отдельный проект (или раздел) «для новичков и начинающих». Чтобы молодые таланты могли тоже постить не сложные, но полезные и хорошие технические статьи при этом не ловя из минусомета за простоту.


    1. hezerford Автор
      00.00.0000 00:00
      +1

      Да, было бы неплохо, это моя первая статья, и решил попробовать себя в данной сфере, отличная идея, но немного сомневаюсь что ее нужно писать в комментарий к посту)


  1. denisstfu
    00.00.0000 00:00

    А может попробовать нащупать API?


    1. hezerford Автор
      00.00.0000 00:00

      Добрый день, пока что не овладел этой технологией, но взял на заметку, спасибо.



  1. GT-onizuka
    00.00.0000 00:00
    -1

    Статья просто отличная Вы молодец. Очень подробно все описали. На этом примере становится многое понятно. Мне очень понравилось, спасибо Вам большое!


  1. night_admin
    00.00.0000 00:00
    +1

    soup = BeautifulSoup(req, "lxml")

    Если я правильно помню, для использования lxml его надо отдельно установить (pip install lxml). Чем не угодил html.parser?


    1. hezerford Автор
      00.00.0000 00:00
      +1

      Да, он быстрее, и спасибо за помощь, исправил.


    1. Mandarinnnnn
      00.00.0000 00:00
      +1

      lxml быстрее


  1. MARVIIN
    00.00.0000 00:00
    +1

    Неплохо, но использование RSS гораздо проще, чем парсить всё DOM дерево


    1. hezerford Автор
      00.00.0000 00:00

      Спасибо за совет, взял себе на заметку!