Попытка очистить весь текст «a» из определенных «td» с использованием python и bs4

#python #html #web-scraping #beautifulsoup

#python #HTML #веб-очистка #beautifulsoup

Вопрос:

Я пытаюсь очистить https://www.betexplorer.com/soccer/england/premier-league/fixtures / чтобы извлечь текст, содержащийся в теге ‘a’, в частности, в таблице с классом «table-main», затем для каждой строки внутри него. Первый td содержит текст с двумя названиями команд, с классом td «h-text-left». Не уверен, связана ли проблема с моим циклом или нет, но сообщение об ошибке, которое я получаю, похоже, что я неправильно использую bs4 в своей последней строке в цикле.

Я могу очистить каждый tr в таблице с помощью класса «table-main», а затем, кроме того, каждый td с классом «h-text-left». Я нахожусь в тупике, хотя при попытке извлечь только элементы «a», даже не текст «a».

 import requests
from bs4 import BeautifulSoup

headers = {'User-Agent':
           'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36'}

r = requests.get('https://www.betexplorer.com/soccer/england/premier-league/fixtures/', headers=headers)

c = r.content

soup = BeautifulSoup(c)

fixture_table = soup.find('table', attrs = {'class': 'table-main'})

for tr in soup.find_all('tr'):
    match_tds = tr.find_all('td', attrs = {'class': 'h-text-left'})
    matches = match_tds.find_all('a')
  

Последняя строка, когда я пытаюсь найти все теги «a», выдает следующую ошибку:

 ...     matches = match_tds.find_all('a')
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "C:UsersGlyptAppDataLocalProgramsPythonPython36-32libsite-packagesbs4element.py", line 1884, in __getattr__
    "ResultSet object has no attribute '%s'. You're probably treating a list of items like a single item. Did you call find_all() when you meant to call find()?" % key
AttributeError: ResultSet object has no attribute 'find_all'. You're probably treating a list of items like a single item. Did you call find_all() when you meant to call find()?
>>>
  

Ответ №1:

Вы можете сократить это до гораздо более быстрого метода выбора, используя один класс. Все ссылки имеют одно и то же имя класса, поэтому вы можете передать его select в понимание списка, чтобы предоставить вам все ссылки.

 import requests
from bs4 import BeautifulSoup
r = requests.get('https://www.betexplorer.com/soccer/england/premier-league/fixtures/')
soup = BeautifulSoup(r.content, 'lxml')
matches = [item['href'] for item in soup.select('.in-match')]
  

Шансы

 import requests
from bs4 import BeautifulSoup
r = requests.get('https://www.betexplorer.com/soccer/england/premier-league/fixtures/')
soup = BeautifulSoup(r.content, 'lxml')
odds = [item['data-odd'] for item in soup.select('.table-main__odds [data-odd]')]
print(odds)
  

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

1. Я пробую этот подход сейчас, но на самом деле я пытаюсь извлечь текст ссылки, а не атрибут href. Я попытался изменить ваш пример, чтобы извлечь текст коэффициентов, но при его печати я не получаю никаких выходных данных, т.е. [] <td class=»table-main__odds» data-oid=»33jkrxv464x0x7k7pm»>» onclick=»my_selections_click(‘1×2’, ‘soccer’, ‘action=3amp;amp;matchid=tKLzuv9Mamp;amp;outcomeid=33jkrxv464x0x7k7pmamp;amp;otheroutcomes=33jkrxv498x0x0,33jkrxv464x0x7k7pn’); » title=»Добавить в избранное»>2.08 </a> </td>

2. Извините, у этих ссылок нет класса ‘.in-match’ или, скорее, вообще никакого класса. Однако я попытался разобрать атрибут «title», который, похоже, всегда является «Добавить к моим выборкам». Однако при попытке распечатать список совпадений после этого я не получаю никаких результатов.

3. Прошу прощения за ужасное форматирование, но для примера элемента a, который я привел выше, я хочу извлечь текст «2.08» из элемента a.

4. Вы бы изменили на item.text для item в

5. совпадения = [item.text для элемента в soup.select(‘.in-match’)] шансы = [item.text для элемента в soup.select (‘Добавить в мои выборки’)] Первый работает так, как вы описали, однако второй не работает, когда я пытаюсь настроить таргетинг на атрибут title элементов A. Все еще выводится []

Ответ №2:

Вы должны использовать встроенную функциональность для поиска вложенных структур. Вы можете указать .css класс с '.class_name' помощью и найти вложенные структуры с помощью «первый селектор»> «второй селектор» (или даже больше селекторов). Вместе это будет выглядеть так:

 import requests
from bs4 import BeautifulSoup

s = requests.session()
s.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36'
res = s.get('https://www.betexplorer.com/soccer/england/premier-league/fixtures/')
soup = BeautifulSoup(res.text, 'html.parser')

matches = soup.select('.table-main  tr  td  a')
for match in matches:
    print(match.getText())
  

В строке matches = soup.select('.table-main tr td a') будут выбраны все a элементы, которые находятся внутри td элемента, которые находятся внутри tr элемента, которые находятся внутри class=table-main элемента. Кроме того, вы можете использовать matches = soup.select('td > a') ( > оператор), чтобы указать, что a элемент находится непосредственно внутри td элемента. Я полагаю, что это потенциально может значительно упростить ваш код!

Примечание, я не смог протестировать это на своем компьютере, поскольку сертификат SSL не может быть подтвержден и поднят requests.exceptions.SSLError

Ответ №3:

Чтобы получить текст, попробуйте:

 for td in soup.findAll('td', attrs = {'class': 'h-text-left'}):
    print(td.findAll('a')[0].text)
  

Ответ №4:

match_tds это список, а не отдельный элемент — вы получаете его с tr.find_all(...) помощью — поэтому вам нужно использовать for цикл для запуска другого find_all()

 for tr in soup.find_all('tr'):
    match_tds = tr.find_all('td', attrs = {'class': 'h-text-left'})
    for item in match_tds:
        matches = item.find_all('a')
        for a in matches:
            print(a['href'])
  

Если вы используете find() для получения первого элемента, вы можете использовать с другим find() или find_all()

 soup.find(...).find(...).find_all(...)
  

но вы не можете использовать find() или find_all() после find_all()

 # ERROR
soup.find_all(...).find_all(...) 

# ERROR
soup.find_all(...).find(...) 
  

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

1. Спасибо, получил именно то, что искал.