0
import requests
from bs4 import BeautifulSoup
response = requests.get('https://www.forbes.ru/')
response = response.content
html = BeautifulSoup(response, 'html.parser')
res = html.find_all(class_='_3g9mx_3B1eS')
res2 = [c.text for c in res ]
print(res2)
Larik
  • 11
  • Пожалуйста, уточните вашу конкретную проблему или приведите более подробную информацию о том, что именно вам нужно. В текущем виде сложно понять, что именно вы спрашиваете. – Space Researcher Nov 30 '22 at 15:17
  • Я хочу вывести информацию в виде текста (во вкладке 'новости' на сайте) но при запуске выводится только это: [] – Larik Nov 30 '22 at 15:22
  • Вы, для начала, сделайте дебаг вашего кода и посмотрите содержимое переменной res. – Dmitry Nekrasov Nov 30 '22 at 16:03
  • Значит тегов с указанным классом в html нет. – insolor Dec 01 '22 at 05:52

1 Answers1

2

Нет, это вообще не так делается‥ Попробую разжевать.

Где брать данные?

Давай, для начала, откроем страницу forbes.ru и посмотрим что там происходит. По комментариям вижу, что есть желание получить новости из вкладки, какой вкладки? На сайте есть колонка с новостями слева. Про неё речь?

Допустим про неё, поехали. Крутим эту колонку, видим scroll, видим, что в конце появляется кружок загрузки и‥ подгружаются новые новости. Так вот, пишем себе в заметки, — это парсер. Парсер для статичных страниц либо же для того, что мы получаем через requests или более окольными путями.

Для понимания, идём в developer tools в любом браузере, например в Chrome жмём F12 и идём в вкладку Network, смотрим самую первую запись при фильтре All, а именно www.forbes.ru, правее выбираем вкладку Preview.

Как новости видит BeautifulSoap

Вот таким вот образом увидит страницу с обычным GET request как в вопросе, обращающимся по адресу.
Да, там есть новости, заголовок даже распарсить можно:

import requests
from bs4 import BeautifulSoup
import re
response = requests.get('https://www.forbes.ru/')
soup = BeautifulSoup(response.text, 'lxml')
soup.find_all("p", string=re.compile(r"Новости"))
Вывод:
[<p class="_1YaJk">
       Новости
     </p>]

Толку только от этого мало будет. Да, можем пойти в parent, искать сиблинги, выйти в итоге на div, ul и li, но они будут пустыми, потому что страница грузится динамически и то что получает на покушать с обычного request наглядно показано выше на скрине.

Пытливый ум начнёт разбираться, а где же происходит динамическая прогрузка и как нам её выцепить. Можно, конечно, воспользоваться Selenium или Scrapy, но это неинтересно и это не путь война.
Можно попробовать заблокировать cdn.forbes.ru и увидеть, что новости больше не грузятся, это потому что с этого адреса прилетают JS скрипты, которые расфасовывают информацию по DOM.
А берут они информацию из waterfall.forbes.ru, который тоже для наглядности можно попробовать заблокировать и убедиться, что новости будут пустыми, при этом весь остальной сайт останется рабочим. В общем, источник нашли.

Websocket

Что это за request? 101, о божечки, да это же !
Все любят веб сокеты, а если вы их ещё не любите, то самое время полюбить.
На самом деле весь путь описанный выше проходить необязательно, это лишь для наглядности и в целях обучения. В вкладке Network есть фильтр WS, можно заглянуть туда сразу и посмотреть, использует ли сайт сокеты.

Итак, убедились, что использует. Выбираем его и смотрим вкладку Messages в Chrome или Response в лисичке. Смотрим для того, чтобы научиться с этим сокетом общаться. Браузер отправляет basic, получает словарь с массивом внутри.
Прокручиваем новости вниз до кружка загрузки, видим, что теперь отправляется more. Всё это можно потыркать:

Как смотреть сообщения сокетов

Поздравляю, мы научились общаться с сокетом, осталось только переложить всё это дело на код:

from websocket import create_connection
from datetime import datetime
import json

def get_news_from_socket(request, socket): socket.send(request) response = socket.recv() out = json.loads(response) return out['data']['base'] if request == 'basic' else out['data']

ws = create_connection('wss://waterfall.forbes.ru/ws')

data = [] data += get_news_from_socket('basic', ws) data += get_news_from_socket('more', ws)

ws.close()

for d in data: print('{}\r\n{}\r\n{}\r\n'.format(datetime.fromtimestamp(int(d['time'])) , d['title'] , 'https://forbes.ru/'+d['url']))

Вывод:
2022-12-04 17:17:35
ОПЕК+ сохранила план ограничения добычи нефти на фоне введения потолка цен 
https://forbes.ru/biznes/482023-opek-sohranila-plan-ogranicenia-dobyci-nefti-na-fone-vvedenia-potolka-cen

2022-12-04 16:02:13 Власти рекомендовали госкомпаниям проявить «сдержанность» в тратах на Новый год https://forbes.ru/society/482022-vlasti-rekomendovali-goskompaniam-proavit-sderzannost-v-tratah-na-novyj-god

...

2022-12-01 11:00:00 Устройство с умом: как технологии помогают развивать инклюзивность в образовании https://forbes.ru/forbeslife/481747-ustrojstvo-s-umom-kak-tehnologii-pomogaut-razvivat-inkluzivnost-v-obrazovanii

2022-12-01 10:15:03 Театральные премьеры и книжная ярмарка: афиша с 1 по 7 декабря https://forbes.ru/forbeslife/481824-teatral-nye-prem-ery-i-kniznaa-armarka-afisa-s-1-po-7-dekabra

По хорошему тут всё надо оборачивать в async и т.д., но для простоты понимания данного решения оставляю всё максимально просто.

  1. create_connection('wss://waterfall.forbes.ru/ws') — создаём подключение к сокету.
    Адрес берём из вкладки Headers.

  2. ws.send() — отправка сообщения сокету. Первый раз 'basic', все последущие разы 'more'.

  3. ws.recv() — принимаем ответное сообщение от сокета. Вот тут асинхронность не помешала бы, но пусть так будет.

  4. Далее парсим всё это дело через json.loads(), потому что это json, ключи опять же смотрим в браузере, на этапе когда тыркали сокет.

  5. ws.close() — не забываем закрыть сокет.
    Он, в общем то, закроется сам по timeout, но так как мы вежливые люди, закроем его принудительно.

Послесловие

В зависимости от того, сколько ты новостей хочешь получить, можешь отправлять 'more' несколько раз и получать новый массив с более давними новостями, но сильно не увлекайся, а то форбс даст по рогам

DiMithras
  • 2,658