Как мне правильно использовать функцию выбора в BeautifulSoup4?

#python #python-3.x #web-scraping #beautifulsoup

#python #python-3.x #очистка веб-страниц #beautifulsoup

Вопрос:

Я пишу свой первый скрипт, используя BS4 в качестве введения к веб-очистке, и у меня возникли проблемы. Я продолжаю автоматизировать скучные вещи с помощью руководства по Python, в котором он использует soup.select('insert class here') для выбора классов. Когда я запускаю приведенный ниже код, он сообщает мне, что soup не является правильной командой AttributeError: 'Response' object has no attribute 'select'

 import webbrowser
import selenium
import bs4
import requests

table = []

url = 'http://espn.com/mlb/team/stats/_/name/wsh'
r = requests.get(url)
page = bs4.BeautifulSoup(r.text)
table = soup.select("Table2__th")
print(str(table))
  

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

1. должно быть page.select() не soup.select() , поскольку вы назвали свой объект Soup page

2. Хорошо, я это исправил. Теперь, когда я печатаю, он просто выдает мне пустой список.

Ответ №1:

Я предполагаю, что вам действительно нужны данные в таблице? Это содержимое отображается с использованием javascript, поэтому одни запросы не помогут, если вы нацелены на саму таблицу.

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

 import bs4
import requests
import re
import json
import pandas as pd

url = 'http://espn.com/mlb/team/stats/_/name/wsh'
r = requests.get(url)
page = bs4.BeautifulSoup(r.text, 'lxml')
r = re.compile(r'playerStats":(.*),"teamLeaders"' , re.DOTALL)
data = page.find('script', text=r).text
script = r.findall(data)[0]    
players_info = json.loads(script)
player_batting_stats = players_info[0]
expanded_player_batting_stats = players_info[1]

table1 = []
table2 = []
headers = ['Name', 'GP', 'AB', 'R', 'H', '2B', '3B', 'HR', 'RBI', 'TB', 'BB', 'K', 'SB', 'BA', 'OBP', 'SLG', 'OPS', 'WAR']

for player in player_batting_stats:
    name = player['athlete']['name']
    row = [stat['value'] for stat in player['statGroups']['stats']]
    row.insert(0, name)
    table1.append(row)

df1 = pd.DataFrame(table1, columns = headers)
print(df1.head())

# repeat for table2 using expanded_player_batting_stats
  

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

1. Огромное спасибо! Знаете ли вы какие-либо хорошие ресурсы для изучения того, как работать с JS в Python при извлечении данных?

2. Я в восторге от этой техники, предназначенной для извлечения содержимого из тегов скрипта.

3. @SIM Это так удобно. К счастью, в python есть лучшая поддержка регулярных выражений, это точно.

4. джон — на самом деле вам не нужно много знать js (если есть) для этого конкретного подхода. Более полезно знать некоторые базовые регулярные выражения для определения шаблона и json для обработки объекта json (после синтаксического анализа из js с помощью библиотеки json). В более общем плане, знание некоторых js очень удобно для написания собственной функциональности при работе с веб-сайтами и необходимости чего-либо, недоступного в библиотеках python. Мне приходилось делать это на других языках, но пока не на python, поскольку для python доступно так много функциональных возможностей.

Ответ №2:

Здесь две проблемы:

  • вы определили свой объект soup как page , но затем пытаетесь ссылаться на него как soup . Вы должны делать page.select('...')
  • вы пытаетесь выбрать определенный класс в CSS, поэтому ваш выбор должен быть ".Table2__th" (в CSS имени класса предшествует точка). Смотрите https://facelessuser.github.io/soupsieve / для получения дополнительной информации о селекторах CSS.

Вот рабочая версия вашего кода:

 import bs4
import requests

table = []
url = 'http://espn.com/mlb/team/stats/_/name/wsh'
r = requests.get(url)
page = bs4.BeautifulSoup(r.text)
table = page.select(".Table2__th")
print(str(table))