#python #web-scraping #beautifulsoup
#python #очистка веб-страниц #beautifulsoup
Вопрос:
для моего курса компьютерных наук я пытаюсь написать скрипт веб-скребка на python, который находит все игры в playstation Store, которые продаются с использованием python и beautiful soup. Прямо сейчас я просто пытаюсь заставить программу перечислить все игры на первой странице, их цены и процент продаж (если он есть). Однако для всех игр, которые продаются, терминал возвращает ошибку атрибута: объект ‘nontype’ не имеет атрибута ‘get_text’. Вот мой код:
from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup
my_url = 'https://store.playstation.com/en-ca/category/85448d87-aa7b-4318-9997-7d25f4d275a4/1'
uClient = uReq(my_url)
page_html = uClient.read()
uClient.close()
page_soup = soup(page_html, "html.parser")
containers = page_soup.find_all("section",{"class":"ems-sdk-product-tile__details"})
for container in containers:
title = container.span.get_text()
salePercentContainer = container.find("span",{"class":"psw-body-2 discount-badge discount-badge--
undefined"})
salePercent = salePercentContainer.get_text()
if salePercent is None:
salePercent = 'none'
priceContainer = container.strike
price = priceContainer#.text
if price is None:
Rprice = container.find_all("span",{"class":"price"})
price = Rprice[0].text
print("title: " title)
print("sale percent: " str(salePercent))
print("price: " str(price))
Комментарии:
1. Без дополнительных подробностей все, что я могу вам точно сказать, это то, что один из объектов, с которыми вы вызываете
.get_text()
, не то, что вы думаете. Фактически это null (или NoneType в python). Я бы рекомендовал посмотретьcontainer
salePercentContainer
, и т.д. Один из них ни к чему не приводит. Скорее всего, это salePercentContainer, и вы, вероятно, захотите проверить, равно ли оно null, прежде чем пытаться сделатьget_text
2.
nontype
означаетNone
, и это означает, что не удалось найти элемент на странице — поэтому вы пытаетесь сделатьNone.get_text()
3. вероятно, страница использует JavaScript для добавления элементов, но
BeatifulSoup
/requests
не может запустить JavaScript. Возможно, вам понадобится Selenium для управления реальным веб-браузером, который может запускать JavaScript. Кстати: отключите JavaScript в веб-браузере и снова загрузите страницу, чтобы посмотреть, что Beautifulsoup может получить с сервера.4. если pages работает без JavaScript, вам следует проверить, что вы получаете в
page_html
— ie. используйтеprint()
или сохраните в файле и откройте в веб-браузере. Возможно, сервер распознал, что вы используете скрипт, и отправил HTML с предупреждением о ботах или с капчей.5. @DoloMike выясняет, что значение salePercentContainer равно null, спасибо.
Ответ №1:
Введите a try:except
, чтобы он не сработал, для elements
которого нет того, что вы ищете.
from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup
my_url = 'https://store.playstation.com/en-ca/category/85448d87-aa7b-4318-9997-7d25f4d275a4/1'
uClient = uReq(my_url)
page_html = uClient.read()
uClient.close()
page_soup = soup(page_html, "html.parser")
containers = page_soup.find_all("section",{"class":"ems-sdk-product-tile__details"})
for container in containers:
try:
title = container.span.get_text()
print(title)
salePercentContainer = container.find("span",{"class":"psw-body-2 discount-badge discount-badge--undefined"})
salePercent = salePercentContainer.get_text()
print(salePercent)
if salePercent is None:
salePercent = 'none'
except Exception as e:
pass
priceContainer = container.strike
print(priceContainer)
price = priceContainer # .text
if price is None:
Rprice = container.find_all("span", {"class": "price"})
price = Rprice[0].text
print(price)
print("title: " title)
print("sale percent: " str(salePercent))
print("price: " str(price))
Вывод:-
Just Cause 4
Rocket Arena
Vigor
Rocket League®
Fortnite
Days Gone
-50%
God of War
Genshin Impact
Mortal Kombat X
Rogue Company
Crash Bandicoot™ N. Sane Trilogy
eFootball PES 2021 LITE
Apex Legends™
Fallout 4
-70%
Stranded Deep
MONSTER HUNTER: WORLD™
RESIDENT EVIL 7 biohazard
-50%
The Last Guardian
Bloodborne™
Horizon Zero Dawn: Complete Edition
Persona 5
Battlefield™ 1
NHL® 21
-52%
Wreckfest
-30%
The Last Of Us™ Remastered
Until Dawn
inFAMOUS Second Son
Detroit: Become Human
Red Dead Online
SHAREfactory™
Brawlhalla
Hyper Scape
Rec Room
Bless Unleashed
RACING BROS
F1 2020
-50%
NBA 2K21
-50%
Spellbreak
SMITE
Grand Theft Auto V
Injustice™ 2
-75%
UFC® 4
-50%
SPIDER-MAN: FAR FROM HOME VIRTUAL REALITY EXPERIENCE
Dead Island Definitive Edition
MX vs ATV All Out
Hello Neighbor
NARUTO TO BORUTO: SHINOBI STRIKER
Tomb Raider: Definitive Edition
None
$26.99
title: Tomb Raider: Definitive Edition
sale percent: -50%
price: $26.99
Ответ №2:
Данные находятся в формате json в исходном коде html. Вы также можете извлечь это и проанализировать.
Просто отфильтруйте фрейм данных, чтобы показать то, что вы хотите.
import requests
import pandas as pd
import json
from bs4 import BeautifulSoup
rows = []
for page in range(1,11):
url = 'https://store.playstation.com/en-ca/category/85448d87-aa7b-4318-9997-7d25f4d275a4/%s' %page
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
jsonStr = soup.find_all('script',{'type':'application/json'})[2].text
jsonData = json.loads(jsonStr)
state = jsonData['props']['apolloState']
print ('Page: %s' %page)
for k, v in state.items():
if 'Product:' in k and '.price' in k:
skuId = k.split('.price')[0][1:]
title = jsonData['props']['apolloState'][skuId]['name']
v.update({'title':title})
rows.append(v)
df = pd.DataFrame(rows)
Вывод:
print (df)
basePrice discountedPrice ... __typename title
0 $0.00 Included ... SkuPrice Just Cause 4
1 $6.99 $6.99 ... SkuPrice Rocket Arena
2 Free Free ... SkuPrice Vigor
3 Free Free ... SkuPrice Rocket League®
4 Free Free ... SkuPrice Fortnite
.. ... ... ... ... ...
472 $39.99 $9.99 ... SkuPrice MX vs. ATV Supercross Encore
473 $29.99 $29.99 ... SkuPrice ASTRO BOT Rescue Mission
474 $33.49 $33.49 ... SkuPrice Descenders
475 $54.99 $21.99 ... SkuPrice Fallout 76
476 $24.99 $24.99 ... SkuPrice LEGO® Jurassic World™
[477 rows x 10 columns]
Показать со скидкой:
discounted_df = df[~df['discountText'].isnull()]
Вывод:
print(discounted_df.head(5).to_string())
basePrice discountedPrice discountText isFree isExclusive serviceBranding upsellServiceBranding upsellText __typename title
5 $49.99 $24.99 -50% False False {'type': 'json', 'json': []} {'type': 'json', 'json': ['PS_NOW']} Included SkuPrice Days Gone
13 $39.99 $11.99 -70% False False {'type': 'json', 'json': []} None None SkuPrice Fallout 4
16 $26.99 $13.49 -50% False False {'type': 'json', 'json': []} None None SkuPrice RESIDENT EVIL 7 biohazard
22 $79.99 $38.39 -52% False False {'type': 'json', 'json': []} None None SkuPrice NHL® 21
23 $39.99 $27.99 -30% False False {'type': 'json', 'json': []} {'type': 'json', 'json': ['PS_PLUS']} Save 5% more SkuPrice Wreckfest