Не могу соскрести рейтинги

Моя проблема в том, что я не могу использовать bs4 для очистки субрейтингов в его обзорах. Ниже пример:

До сих пор я обнаружил, где находятся эти звезды, но их коды одинаковы независимо от цвета (т. Е. Зеленый или серый) ... Мне нужно определить цвет, чтобы определить рейтинги, а не просто соскребать звезды. . Ниже мой код:

url='https://www.glassdoor.com/Reviews/Walmart-Reviews-E715_P2.htm?filter.iso3Language=eng'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
com = soup.find(class_ = "ratingNumber mr-xsm")
com1 = soup.find(class_ = "gdReview")
com1_1 = com1.find(class_ = "content")
com дает вам рейтинг в виде числа - зачем вам после этого цвета звезд?
Driftr95 01.12.2022 08:02

@Jaevapple- теперь я понимаю лучше (вы должны указать в своем вопросе, что вам также нужны подрейтинги, поскольку сразу видимые рейтинги также имеют числовое значение, поэтому цвета звездочек не важны.)

Driftr95 01.12.2022 12:14
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для получения разбивки звездного рейтинга (который, кажется, не имеет числового отображения или мета-значения), я не думаю, что есть какой-то очень простой и прямой короткий метод, поскольку он выполняется с помощью css в теге стиля, связанном с класс элемента-контейнера.

Вы можете использовать что-то вроде soup.select('style:-soup-contains(".css-1nuumx7")') [часть css-1nuumx7 специфична для упомянутого выше рейтинга], но :-soup-contains требует синтаксического анализатора html5lib и может быть немного медленным, поэтому лучше выяснить атрибут data-emotion-css тег style вместо этого:

def getDECstars(starCont, mSoup, outOf=5,  isv=False):
    classList = starCont.get('class', [])
    if type(classList) != list: classList = [classList]
    classList = [str(c) for c in classList if str(c).startswith('css-')] 
    if not classList: 
        if isv: print('Stars container has no "css-" class')
        return None
    
    demc = classList[0].replace('css-', '', 1)
    demc_sel = f'style[data-emotion-css = "{demc}"]'
    cssStyle = mSoup.select_one(demc_sel)
    if not cssStyle:
        if isv: print(f'Nothing found with selector {demc_sel}')
        return None
    
    cssStyle = cssStyle.get_text()
    errMsg = ''
    if '90deg,#0caa41 ' not in cssStyle: errMsg += 'No #0caa41'
    if '%' not in cssStyle.split('90deg,#0caa41 ', 1)[-1][:20]: 
        errMsg += ' No %'
    if not errMsg:
        rPerc = cssStyle.split('90deg,#0caa41 ', 1)[-1]
        rPerc = rPerc.split('%')[0]
        try:  
            rPerc = float(rPerc)
            if 0 <= rPerc <= 100:
                if type(outOf) == int and outOf > 0: rPerc = (rPerc/100)*outOf
                return float(f'{float(rPerc):.3}')
            errMsg = f'{demc_sel} --> "{rPerc}" is out of range'
        except: errMsg = f'{demc_sel} --> cannot convert to float "{rPerc}"' 
    if isv: print(f'{demc_sel} --> unexpected format {errMsg}')
    return None

ИЛИ, если вам все равно, почему отсутствует рейтинг:

def getDECstars(starCont, mSoup, outOf=5, isv=False):
    try:
        demc = [c for c in starCont.get('class', []) if c[:4]=='css-'][0].replace('css-', '', 1)
        demc_sel = f'style[data-emotion-css = "{demc}"]'
        rPerc = float(mSoup.select_one(demc_sel).get_text().split('90deg,#0caa41 ', 1)[1].split('%')[0])
        return float(f'{(rPerc/100)*outOf if type(outOf) == int and outOf > 0 else rPerc:.3}')
    except: return None

Вот пример того, как вы можете его использовать:

pcCon = 'div.px-std:has(h2 > a.reviewLink) + div.px-std'
pcDiv = f'{pcCon} div.v2__EIReviewDetailsV2__fullWidth'
refDict = {
    'rating_num': 'span.ratingNumber',
    'emp_status': 'div:has(> div > span.ratingNumber) + span',
    'header': 'h2 > a.reviewLink',
    'subheader': 'h2:has(> a.reviewLink) + span',
    'pros': f'{pcDiv}:first-of-type > p.pb',
    'cons': f'{pcDiv}:nth-of-type(2) > p.pb'
}

subRatSel = 'div:has(> .ratingNumber) ~ aside ul > li:has(div ~ div)'
empRevs = []
for r in soup.select('li[id^ = "empReview_"]'):
    rDet = {'reviewId': r.get('id')}
    for sr in r.select(subRatSel):
        k = sr.select_one('div:first-of-type').get_text(' ').strip()
        sval = getDECstars(sr.select_one('div:nth-of-type(2)'), soup)
        rDet[f'[rating] {k}'] = sval
    
    for k, sel in refDict.items():
        sval = r.select_one(sel)
        if sval: sval = sval.get_text(' ').strip()
        rDet[k] = sval
    
    empRevs.append(rDet)

Если empRevs рассматривается как таблица:

идентификатор обзора [рейтинг] Баланс между работой и личной жизнью [рейтинг] Культура и ценности [рейтинг] Разнообразие и инклюзивность [рейтинг] Карьерные возможности [рейтинг] Компенсации и льготы [рейтинг] Высшее руководство рейтинг_номер emp_status заголовок подзаголовок плюсы минусы empReview_71400593 5 4 4 4 5 3 3 отличная оплата, но немного неприятная среда 26 ноября 2022 г. - продавец / кассир в Бенсалеме, Пенсильвания. - Политика справедливой оплаты Walmart - это... -некоторые локации не будут строить эм... empReview_70963705 3 3 2 2 2 2 2 Бывший работник Обученных сотрудников Walmart бросили на съедение волкам 10 ноября 2022 г. — ввод данных Перекусить в перерыве было е... Я работал в Walmart очень... empReview_71415031 4 4 4 4 4 4 5 Текущий сотрудник, более 1 года Работа 27 ноября 2022 г. - сотрудник склада в Спрингфилде, Джорджия. Деньги там хорошие во время... Иногда может напрягать... empReview_69136451 нан нан нан нан нан нан 4 Текущий сотрудник Волмарт 16 сентября 2022 г. - продавец/кассир Я ОПЫТНЫЙ РАБОТНИК. Я ✨... На мой взгляд, я считаю, что В... empReview_71398525 4 3 4 3 4 3 4 Текущий сотрудник Сильно зависит от вашей команды 26 ноября 2022 г. - Персональный цифровой покупатель У меня вообще отличные т... Как правило, отделы... empReview_71227029 1 1 1 1 3 1 1 Бывший сотрудник, менее 1 года К менеджерам относятся как к рабам. 19 ноября 2022 г. - Менеджер центра обслуживания автомобилей (ACCM) в Коттонвуде, Аризона. Отлично, если вам нравится работать с... вы работаете только в своем... empReview_71329467 1 3 3 3 4 1 1 Текущий сотрудник, более 3 лет Нет больше значений 23 ноября 2022 г. - тренер GM в Хьюстоне, штат Техас. Оплата по сравнению с другими розничными... Walmart не плохая компания... empReview_71512609 5 5 5 5 5 5 5 Бывший работник Полуночный запас Walmart 30 ноября 2022 г. - Midnight Stocker в Тейлоре, штат Мичиган. 2 оплачиваемых перерыва по 15 минут и 1 час... Честно говоря, ничего, что я могу ... empReview_70585957 3 4 4 4 4 4 4 Бывший работник Много возможностей 28 октября 2022 г. - руководитель отдела кадров Много возможностей, если... Как и в любой работе, управление ... empReview_71519435 3 4 4 5 4 4 5 Текущий сотрудник, более 3 лет Много работы, но оно того стоит 30 ноября 2022 г. — Люди лидируют Мне нравится оживлять коллег... Иногда подавляющая любовь...

Markdown for the table above was printed with pandas:

erdf = pandas.DataFrame(empRevs).set_index('reviewId')
erdf['pros'] = [p[:30] + '...' if len(p) > 33 else p for p in erdf['pros']]
erdf['cons'] = [p[:30] + '...' if len(p) > 33 else p for p in erdf['cons']]
print(erdf.to_markdown())

Не мог бы поблагодарить вас еще больше! Это АБСОЛЮТНО тот ответ, который я ищу. Я выучу ваш код наизусть. Это выглядит очень круто для меня, и я буду гуглить, чтобы узнать это. Большое спасибо.

Jaevapple 01.12.2022 18:02

@Jaevapple Я был бы рад помочь, чем смогу, но боюсь, что не вижу никаких изменений в вашем вопросе, и я не совсем понимаю, что вы подразумеваете под «каждый отзыв имеет одинаковое общее количество обзоры"... вы можете найти количество страниц, разделив текст из soup.select_one('div[data-test = "pagination-footer-text"]') [ внизу страницы ], а ссылку на следующую страницу из href из soup.select_one('li:has(a.page.selected) + li a.page[href]') [ см. это для примера того, как очистить результаты с разбивкой на страницы ]

Driftr95 01.12.2022 19:09

@Jaevapple Я видел в вашем вопросе немного об отсутствующих подрейтингах из 4 частей. Я добавил обновленную версию getDECstars в https://pastebin.com/Q0GLwRv9 (новые/измененные строки отмечены ## edit), и это должно решить эту конкретную проблему.

Driftr95 02.12.2022 19:12

@Jaevapple, но проблема остается из-за сбоев requests ... вы можете легко использовать селен, заменив свою функцию extract(pg) на что-то вроде этого, но похоже, что некоторые из необходимых тегов style исчезают после загрузки страницы, поэтому ни один из подрейтинги могут показывать

Driftr95 02.12.2022 19:19

Другие вопросы по теме