Веб-сканирование с помощью Beautiful Soup и невозможность получить сериализованные даты и другие пары ключ-значение из приложения / json данные - PullRequest
0 голосов
/ 05 мая 2020

Я просматриваю отзывы клиентов с этого веб-сайта для проекта. https://www.trustpilot.com/review/stockx.com Я собираюсь очистить

Имя пользователя, Дата, Рейтинг, Заголовок и Содержание обзора.

Я могу получить текст для имени пользователя, заголовка , и просматривайте контент, но чтобы получить звездный рейтинг в этом теге скрипта, мне нужно получить доступ к объекту JSON для пары "ключ-значение"


    <script data-initial-state="review-info" type="application/json">
    {"socialShareUrl":"https://www.trustpilot.com/reviews/5eb04df325e5d209b8e1bcb0","businessUnitId":"59ae88de0000ff0005aa7413","businessUnitDisplayName":"StockX","consumerId":"5eb04ddecc8cbb5d89fbab50","consumerName":"Morgan H","reviewId":"5eb04df325e5d209b8e1bcb0","stars":1}
    </script>

Затем для даты мне нужно получить сериализованную дату "publishedDate".

    <div class="review-content-header__dates">
        <script data-initial-state="review-dates" type="application/json">
        {"publishedDate":"2020-05-04T18:22:35Z","updatedDate":null,"reportedDate":null}
        </script>
        <review-dates :published-date="publishedDate" :reported-date="reportedDate" :updated- 
         date="updatedDate"></review-dates>
        </div>

То, что у меня есть, работает, за исключением того, что я не могу понять, как извлечь данные объекта JSON и изолировать их, чтобы создать df для анализа.

    import requests
    import json
    from bs4 import BeautifulSoup

    r = requests.get("https://www.trustpilot.com/review/stockx.com", headers={'User-agent': 'Mozilla/5.0 
    (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0'})
    source = r.json()
    source

    trust = BeautifulSoup(source, "html.parser")
    trust

    review = trust.find_all("div", {"class":"review-card"})
    review[0].find("p", {"class":"review-content__text"}).text.replace("\n","").replace("  ","")

    for item in review:

        try:
            print(item.find("div", {"class":"consumer-information__name"}).text)
        except:
            pass

        try:
            print(item.find("a", {"class":"link link--large link--dark"}).text)
        except:
            pass

        try:
            print(item.find("p", {"class":"review-content__text"}).text.replace("\n","").replace("  
            ",""))
        except:
            pass

    date = review[0].find("div", {"class":"review-content-header__dates"})
    date

Метод. json () выдает ошибку


JSONDecodeError Traceback (последний вызов последний)


    <ipython-input-102-7165ac9f6f1c> in <module>
          1 r = requests.get("https://www.trustpilot.com/review/stockx.com", headers={'User-agent': 
          'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0'})
    ----> 2 source = r.json()
          3 source

    ~\Anaconda3\lib\site-packages\requests\models.py in json(self, **kwargs)
        895                     # used.
        896                     pass
    --> 897         return complexjson.loads(self.text, **kwargs)
        898 
        899     @property

    ~\Anaconda3\lib\json\__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, 
    parse_constant, object_pairs_hook, **kw)
        346             parse_int is None and parse_float is None and
        347             parse_constant is None and object_pairs_hook is None and not kw):
    --> 348         return _default_decoder.decode(s)
        349     if cls is None:
        350         cls = JSONDecoder

    ~\Anaconda3\lib\json\decoder.py in decode(self, s, _w)
        335 
        336         """
    --> 337         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
        338         end = _w(s, end).end()
        339         if end != len(s):

    ~\Anaconda3\lib\json\decoder.py in raw_decode(self, s, idx)
        353             obj, end = self.scan_once(s, idx)
        354         except StopIteration as err:
    --> 355             raise JSONDecodeError("Expecting value", s, err.value) from None
        356         return obj, end

    JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Я думаю Мне не хватает некоторых параметров или позиционного аргумента, но я не смог найти решение с помощью предыдущего исследования.

Ответы [ 2 ]

0 голосов
/ 05 мая 2020

Как заявил Стефан, ваш ответ не возвращается в формате json. Следовательно, вам необходимо преобразовать данные в html, используя BeautifulSoup и response.text, который возвращается. Затем вы берете все нужные теги (ниже в коде используется селектор css для захвата всех тегов скрипта, у которых есть атрибут, равный 'review-info'). Затем вы можете получить внутренний скрипт как строку, преобразовать его в json / dict и использовать так, как вы изначально хотели.

import requests
import json
from bs4 import BeautifulSoup

r = requests.get("https://www.trustpilot.com/review/stockx.com", headers={'User-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0'})
html = BeautifulSoup(r.text, 'html.parser')
review_info_array = html.select('script[data-initial-state="review-info"]')  # array of all review info scripts
info_dict = json.loads(review_info_array[0].string)  # using just one for simplicity
stars = info_dict['stars']
print(stars)
0 голосов
/ 05 мая 2020

Функция json (), когда вы ее используете, пытается проанализировать ответ на ваш запрос получения как json. Это обычно полезно, когда ответ (только) в формате json, что не так в этой ситуации.

Вам нужно будет найти этот тег скрипта, в который включен рейтинг, а затем проанализировать содержимое (или внутренний html) этого тега скрипта. Затем вы можете использовать функцию json .loads () для анализа строки json. Как в примере ниже:

import json
inner_html = '{"socialShareUrl":"https://www.trustpilot.com/reviews/5eb04df325e5d209b8e1bcb0","businessUnitId":"59ae88de0000ff0005aa7413","businessUnitDisplayName":"StockX","consumerId":"5eb04ddecc8cbb5d89fbab50","consumerName":"Morgan H","reviewId":"5eb04df325e5d209b8e1bcb0","stars":1}'
star_rating = json.loads(inner_html)["stars"]
...