#python #web-scraping
Вопрос:
У меня возникли проблемы с добавлением очищенных данных в списки. В списках отображаются только один URL-адрес, очищенный от данных. Мне нужны все данные в этих двух списках. Пожалуйста, помогите.
Вот образец моей работы:
import requests
from bs4 import BeautifulSoup
import pandas as pd
urls = ['https://www.brooklyncollegeathletics.com/sports/mens-volleyball/roster/2019',
'https://athletics.baruch.cuny.edu/sports/mens-volleyball/roster',
'https://yorkathletics.com/sports/mens-volleyball/roster']
for url in urls:
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
height_volley = soup.findAll('span', class_ = "sidearm-roster-player-height")
first_name_data = soup.findAll('span', class_ = "sidearm-roster-player-first-name")
last_name_data = soup.findAll('span', class_ = "sidearm-roster-player-last-name")
print(height_volley)
print(first_name_data)
print(last_name_data)
first_name = []
last_name = []
for text in first_name_data:
name = text.get_text()
first_name.append(name)
for text in last_name_data:
name = text.get_text()
last_name.append(name)
print(first_name)
print(last_name)
df = pd.DataFrame()
df['First Name'] = first_name
df['Last Name'] = last_name
df
Ответ №1:
Вы можете использовать функцию для очистки каждой ссылки:
import requests
from bs4 import BeautifulSoup as soup
def get_roster(url):
d = soup(requests.get(url).text, 'html.parser')
return [{'name':i.select_one('.sidearm-roster-player-name h3 a').text,
'number':i.select_one('span.sidearm-roster-player-jersey-number').get_text(strip=True),
'position':getattr(i.select_one('.sidearm-roster-player-position-long-short'), 'get_text', lambda **_:None)(strip=True),
'height':i.select_one('.sidearm-roster-player-height').get_text(strip=True),
'year':i.select_one('.sidearm-roster-player-academic-year').get_text(strip=True),
'hometown':i.select_one('.sidearm-roster-player-hometown').get_text(strip=True),
'highschool':getattr(i.select_one('.sidearm-roster-player-highschool'), 'text', None)}
for i in d.select('ul.sidearm-roster-players li.sidearm-roster-player')]
urls = ['https://www.brooklyncollegeathletics.com/sports/mens-volleyball/roster/2019',
'https://athletics.baruch.cuny.edu/sports/mens-volleyball/roster',
'https://yorkathletics.com/sports/mens-volleyball/roster']
result = list(map(get_roster, urls))
Выход:
[[{'name': 'Snigdho Hasan', 'number': '2', 'position': 'Libero', 'height': '5'5"', 'year': 'Jr.', 'hometown': 'Brooklyn, NY', 'highschool': 'Midwood'}, {'name': 'Michael Valentin', 'number': '3', 'position': 'Setter', 'height': '5'9"', 'year': 'Jr.', 'hometown': 'Brooklyn, NY', 'highschool': 'LaGuardia'}, {'name': 'Andres Vargas', 'number': '4', 'position': 'Outside Hitter', 'height': '6'0"', 'year': 'Fr.', 'hometown': 'Bogota, Colombia', 'highschool': 'Lawrence Senior'}, {'name': 'Jasper Diangco', 'number': '7', 'position': 'Outside Hitter', 'height': '5'10"', 'year': 'So.', 'hometown': 'Brooklyn, NY', 'highschool': 'James Madison'}, {'name': 'Sayuj Zachariah', 'number': '9', 'position': 'Setter', 'height': '6'2"', 'year': 'Sr.', 'hometown': 'Floral Park, NY', 'highschool': 'St. Francis Prep'}, {'name': 'Omar Rezika', 'number': '13', 'position': 'Middle Hitter', 'height': '6'2"', 'year': 'Fr.', 'hometown': 'Brooklyn, NY', 'highschool': 'Rachel Carson'}, {'name': 'Gabriel Pjatak', 'number': '14', 'position': 'Middle Blocker', 'height': '6'3"', 'year': 'Jr.', 'hometown': 'Maly Lipnik, Slovakia', 'highschool': 'James Madison'}, {'name': 'Ryan Chabel', 'number': '16', 'position': 'Outside Hitter', 'height': '5'11"', 'year': 'Sr.', 'hometown': 'Brooklyn, NY', 'highschool': None}, {'name': 'Utku Tanritanir', 'number': '17', 'position': 'Defensive Specialist', 'height': '6'0"', 'year': 'So.', 'hometown': 'Brooklyn, NY', 'highschool': 'Abraham Lincoln'}], [{'name': 'QiQin Zeng', 'number': '1', 'position': 'Outside Hitter', 'height': '5'9"', 'year': 'Fr.', 'hometown': 'Guangdong, China', 'highschool': 'Emma Lazarus (NY)'}, {'name': 'Andrew Tsororos', 'number': '2', 'position': 'Right Side', 'height': '6'4"', 'year': 'Jr.', 'hometown': 'Hauppauge, N.Y.', 'highschool': 'Hauppauge'}, {'name': 'Steven Lopez', 'number': '3', 'position': 'Outside Hitter', 'height': '6'0"', 'year': 'Sr.', 'hometown': 'Port Washington, N.Y.', 'highschool': 'Paul D. Schreiber'}, {'name': 'Sonam Dorjee', 'number': '4', 'position': 'Outside Hitter', 'height': '5'10"', 'year': 'Fr.', 'hometown': 'Woodside, N.Y.', 'highschool': 'Academy of American Studies'}, {'name': 'Edward Grinberg', 'number': '5', 'position': 'Setter', 'height': '6'2"', 'year': 'Jr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'M.S.I.T'}, {'name': 'David Sirchenko', 'number': '6', 'position': 'Middle Hitter', 'height': '6'3"', 'year': 'Fr.', 'hometown': 'Staten Island, N.Y.', 'highschool': 'Staten Island Tech'}, {'name': 'Stallone Shankar', 'number': '7', 'position': 'Setter', 'height': '6'0"', 'year': 'So.', 'hometown': 'Jackson Heights, N.Y.', 'highschool': 'Aviation'}, {'name': 'Rabsang Andrugtsang', 'number': '9', 'position': 'Libero/Defensive Specialist', 'height': '5'10"', 'year': 'Fr.', 'hometown': 'Rego Park, N.Y.', 'highschool': 'Forest Hills'}, {'name': 'Hanbin Lee', 'number': '10', 'position': 'Libero', 'height': '5'9"', 'year': 'Sr.', 'hometown': 'Briarwood, N.Y.', 'highschool': 'Forest Hills'}, {'name': 'Artem Zinkin', 'number': '11', 'position': 'Right Side', 'height': '6'5"', 'year': 'Sr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'Midwood'}, {'name': 'Michael Higgins', 'number': '12', 'position': 'Middle Hitter', 'height': '6'4"', 'year': 'Fr.', 'hometown': 'Valley Stream, N.Y.', 'highschool': 'Valley Stream Central'}, {'name': 'Carlos Rodriguez', 'number': '13', 'position': 'Outside Hitter/Right Side', 'height': '6'4"', 'year': 'Fr.', 'hometown': 'Bronx, N.Y.', 'highschool': 'Walton Campus'}, {'name': 'Leon Petrovitsky', 'number': '14', 'position': 'Right Side', 'height': '6'5"', 'year': 'Fr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'James Madison'}, {'name': 'Defeng Han', 'number': '15', 'position': 'Outside Hitter/Right Side', 'height': '6'1"', 'year': 'Fr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'Fort Hamilton'}, {'name': 'Evan Takos', 'number': '16', 'position': 'Middle Hitter', 'height': '6'3"', 'year': 'Jr.', 'hometown': 'Bayside, N.Y.', 'highschool': 'Saint Francis Prep'}, {'name': 'Meni Musheyev', 'number': '17', 'position': 'Middle Hitter', 'height': '6'3"', 'year': 'Jr.', 'hometown': 'Forest Hills, N.Y.', 'highschool': 'Metro Campus'}, {'name': 'Justin Iloulian', 'number': '18', 'position': 'Outside Hitter/Right Side', 'height': '6'2"', 'year': 'Fr.', 'hometown': 'Plainview, N.Y.', 'highschool': 'Plainview-Old Bethpage JFK'}], [{'name': 'John Vitor', 'number': '2', 'position': 'DS/L', 'height': '5'6"', 'year': 'Fr.', 'hometown': 'Manila, Philippines', 'highschool': 'Martin Van Buren'}, {'name': 'Juan Rodriguez', 'number': '3', 'position': 'L/S', 'height': '5'6"', 'year': 'Jr.', 'hometown': 'Santiago, Dominican Republic', 'highschool': 'Academy for Language and Technology'}, {'name': 'Shawn Nemoto', 'number': '4', 'position': 'L', 'height': '5'7"', 'year': 'Jr.', 'hometown': 'Floral Park, N.Y.', 'highschool': 'Thomas Edison CTE'}, {'name': 'Erick Ortega', 'number': '5', 'position': 'OH', 'height': '5'8"', 'year': 'Fr.', 'hometown': 'Queens, N.Y.', 'highschool': 'Benjamin Franklin HS for Finance amp; Information Technology'}, {'name': 'Akil Vaughn', 'number': '6', 'position': 'OH', 'height': '6'5"', 'year': 'Jr.', 'hometown': 'Georgetown, Guyana', 'highschool': 'St. Roses'}, {'name': 'Justin Saji', 'number': '7', 'position': 'OH', 'height': '5'10"', 'year': 'Fr.', 'hometown': 'Kerala, India', 'highschool': 'Sewanhaka'}, {'name': 'Jayden Deokinanan', 'number': '8', 'position': None, 'height': '5'4"', 'year': 'Fr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'John Adams'}, {'name': 'Mambe Koureissi', 'number': '9', 'position': 'MB', 'height': '6'4"', 'year': 'Sr.', 'hometown': 'Harlem, N.Y.', 'highschool': 'Environmental Studies'}, {'name': 'Anthony Nazario, Jr.', 'number': '10', 'position': 'OH', 'height': '5'9"', 'year': 'Sr.', 'hometown': 'Bronx, N.Y.', 'highschool': 'DeWitt Clinton'}, {'name': 'Olawale Kila', 'number': '12', 'position': 'OH', 'height': '6'4"', 'year': 'Fr.', 'hometown': 'Nigeria', 'highschool': 'Broome Street Academy Charter'}, {'name': 'David Heyliger, Jr.', 'number': '13', 'position': 'OH', 'height': '6'1"', 'year': 'Fr.', 'hometown': 'St. Thomas, U.S. Virgin Islands', 'highschool': 'Charlotte Amalie'}, {'name': 'Guillermo Hernandez', 'number': '14', 'position': 'MH', 'height': '6'4"', 'year': 'So.', 'hometown': 'New York, N.Y.', 'highschool': 'Gregorio Luperon'}, {'name': 'Fatmir Glavatovic', 'number': '15', 'position': 'OH', 'height': '6'0"', 'year': 'Sr.', 'hometown': 'Brooklyn, N.Y.', 'highschool': 'Secondary School for Law'}, {'name': ' Ti’Juan Boothe', 'number': '16', 'position': 'OH', 'height': '5'8"', 'year': 'Fr.', 'hometown': 'Kingston, Jamaica', 'highschool': 'Excelsior Prep'}]]
Комментарии:
1. Это очень хороший способ передать ошибку атрибута или что-то подобное. У меня есть один вопрос по этому поводу: как бы вы проанализировали href, когда используете
getattr()
этоsoup.select_one("a").get("href")
? Я пытался,getattr(soup.select_one("a"),"href",None)
но потерпел неудачу. Спасибо.2. @MITHU, так
select_one
как будет либо возвращенNone
, либоbs4
объект, я бы использовал выражение присваивания со встроенным условным:None if (a:=soup.select_one("a")) is None else a.get('href')
. Вы бы использовалиgetattr
, еслиsoup
бы само могло бытьNone
:getattr(soup, 'select_one', lambda _:{})().get('href')
Ответ №2:
Убедитесь, что вы отлаживаете свой код шаг за шагом, чтобы решить такого рода проблемы.
Объявленный вами фрейм данных сбрасывается на каждой итерации.
import requests
from bs4 import BeautifulSoup
import pandas as pd
urls = ['https://www.brooklyncollegeathletics.com/sports/mens-volleyball/roster/2019',
'https://athletics.baruch.cuny.edu/sports/mens-volleyball/roster',
'https://yorkathletics.com/sports/mens-volleyball/roster']
df_list = list()
for url in urls:
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
height_volley = soup.findAll('span', class_ = "sidearm-roster-player-height")
first_name_data = soup.findAll('span', class_ = "sidearm-roster-player-first-name")
last_name_data = soup.findAll('span', class_ = "sidearm-roster-player-last-name")
first_name = []
last_name = []
for text in first_name_data:
name = text.get_text()
first_name.append(name)
for text in last_name_data:
name = text.get_text()
last_name.append(name)
df_temp = pd.DataFrame()
df_temp['First Name'] = first_name
df_temp['Last Name'] = last_name
df_list.append(df_temp)
df_result = pd.concat(df_list, axis=0, ignore_index=True)
print(df_result)
ОТРЕДАКТИРУЙТЕ (на основе ответа Ajax1234), чтобы ответить на более поздний вопрос.
import requests
from bs4 import BeautifulSoup as soup
import pandas as pd
urls = [
'https://www.brooklyncollegeathletics.com/sports/mens-volleyball/roster/2019',
'https://athletics.baruch.cuny.edu/sports/mens-volleyball/roster',
'https://yorkathletics.com/sports/mens-volleyball/roster'
]
def get_roster(url):
d = soup(requests.get(url).text, 'html.parser')
return pd.DataFrame([
{
'name': i.select_one('.sidearm-roster-player-name h3 a').get_text(strip=True),
'height': i.select_one('.sidearm-roster-player-height').get_text(strip=True),
}
for i in d.select('ul.sidearm-roster-players li.sidearm-roster-player')
])
df_result = pd.concat(map(get_roster, urls), ignore_index=True)
df_result[['first_name','last_name']] = df_result['name'].str.split(" ", 1, expand=True)
del df_result['name']
print(df_result)
Комментарии:
1. Привет, Исмаил, Спасибо за помощь. У меня есть еще один вопрос о соскобленной высоте, когда я пытаюсь добавить ее в df, это дает мне ошибку значения: длина значений (18) не соответствует длине индекса (11), какие-либо предложения? Вот что я сделал для высоты: для текста в height_volley: футы, дюймы = text.get_text()[:-1].разделить(«‘») высоту.добавить(int(футов) * 12 int(дюймов))
2. С удовольствием. Пожалуйста, в следующий раз задайте новый вопрос или добавьте его в тот же вопрос выше. Я отредактировал свой ответ, теперь он содержит больше кода для вашего вопроса. Я надеюсь, что это поможет. Вы можете обратиться к @Ajax1234 за более подробной информацией, если вам нужно проанализировать больше данных, таких как позиция игрока и т.д.
Ответ №3:
Потому что вы сбрасываете свой фрейм данных на каждой итерации :
Примечание : Рекомендуется добавлять заголовки в ваш запрос.
import requests
from bs4 import BeautifulSoup
import pandas as pd
urls = ['https://www.brooklyncollegeathletics.com/sports/mens-volleyball/roster/2019',
'https://athletics.baruch.cuny.edu/sports/mens-volleyball/roster',
'https://yorkathletics.com/sports/mens-volleyball/roster']
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"}
df = pd.DataFrame()
first_name = []
last_name = []
for url in urls:
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.content, "html.parser")
height_volley = soup.findAll('span', class_ = "sidearm-roster-player-height")
first_name_data = soup.findAll('span', class_ = "sidearm-roster-player-first-name")
last_name_data = soup.findAll('span', class_ = "sidearm-roster-player-last-name")
print(height_volley)
print(first_name_data)
print(last_name_data)
for text in first_name_data:
name = text.get_text()
first_name.append(name)
for text in last_name_data:
name = text.get_text()
last_name.append(name)
print(first_name)
print(last_name)
df['First Name'] = first_name
df['Last Name'] = last_name
df