Найдите тег, используя текст, который он содержит, с помощью BeautifulSoup

Я пытаюсь извлечь из Интернета некоторые части этой страницы: https://markets.businessinsider.com/stocks/bp-stock с помощью BeautifulSoup для поиска некоторого текста, содержащегося в заголовке таблицы h2

когда я делаю:

data_table = soup.find('h2', text=re.compile('RELATED STOCKS')).find_parent('div').find('table')

Он правильно получает таблицу, которую я ищу.

Когда я пытаюсь получить таблицу Analyst Opinion, используя аналогичную строку, она возвращает None:

data_table = soup.find('h2', text=re.compile('ANALYST OPINIONS')).find_parent('div').find('table')

Я предполагаю, что в html-коде могут быть какие-то специальные символы, которые позволяют re функционировать должным образом. Я тоже пробовал это:

data_table = soup.find('h2', text=re.compile('.*?STOCK.*?INFORMATION.*?', re.DOTALL))

безуспешно.

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

Любая идея будет высоко оценена. Лучший


person Je Je    schedule 20.08.2019    source источник


Ответы (1)


Вы можете использовать селектор CSS, чтобы найти <table>:

import requests
from bs4 import BeautifulSoup

url = 'https://markets.businessinsider.com/stocks/bp-stock '

soup = BeautifulSoup(requests.get(url).text, 'lxml')

table = soup.select_one('div:has(> h2:contains("Analyst Opinions")) table')

for tr in table.select('tr'):
    print(tr.get_text(strip=True, separator=' '))

Отпечатки:

2/26/2018 BP Outperform RBC Capital Markets
9/22/2017 BP Outperform BMO Capital Markets

Подробнее о селекторах CSS здесь.


РЕДАКТИРОВАТЬ: для метода, нечувствительного к кансе, вы можете использовать bs4 API с регулярными выражениями (обратите внимание на flags=re.I). Это эквивалент метода .select() выше:

import re
import requests
from bs4 import BeautifulSoup

url = 'https://markets.businessinsider.com/stocks/bp-stock '

soup = BeautifulSoup(requests.get(url).text, 'lxml')

h2 = soup.find(lambda t: t.name=='h2' and re.findall('analyst opinions', t.text, flags=re.I))
table = h2.find_parent('div').find('table')

for tr in table.select('tr'):
    print(tr.get_text(strip=True, separator=' '))
person Andrej Kesely    schedule 20.08.2019
comment
Большое спасибо. Из любопытства задался вопросом, почему re.compile работает для некоторых, но не для всех таблиц... Но, глядя на ваш код, он все равно кажется намного более элегантным. - person Je Je; 20.08.2019
comment
@NonoLondon "Analyst Opinions" не только в верхнем регистре - я думаю, он только стилизован CSS, чтобы он отображался в верхнем регистре. Вы можете попробовать добавить flags=re.I к своему регулярному выражению, чтобы игнорировать регистр. - person Andrej Kesely; 20.08.2019
comment
поэтому я попробовал: data_table = soup.find('h2', text=re.compile('Analyst.*?Opinions', flags=re.I)) и data_table = soup.find('h2', text=re.compile('Analyst Opinions', flags=re.I)) и оба возвращают None - person Je Je; 20.08.2019
comment
@NonoLondon Мне удалось найти тег <h2> с помощью этой команды: data_table = soup.find(lambda tag: tag.name=='h2' and re.findall(r'analyst opinion', tag.text, flags=re.I)) - person Andrej Kesely; 20.08.2019
comment
Я заметил, что метод contains чувствителен к регистру, есть ли способ сделать его нечувствительным к регистру? - person Je Je; 28.08.2019