Python Beautiful-мыло JSON - очистить одну страницу, но не другие подобные - PullRequest
2 голосов
/ 09 ноября 2019

Я пытаюсь почистить веб-сайт о питании, и следующий код работает

import requests
from bs4 import BeautifulSoup
import json
import re

page = requests.get("https://nutritiondata.self.com/facts/nut-and-seed-products/3071/1")
soup = BeautifulSoup(page.content, 'html.parser')

scripts = soup.find_all("script")
for script in scripts:
    if 'foodNutrients = ' in script.text:
        jsonStr = script.text
        jsonStr = jsonStr.split('foodNutrients =')[-1]
        jsonStr = jsonStr.rsplit('fillSpanValues')[0]
        jsonStr = jsonStr.rsplit(';',1)[0]
        jsonStr = "".join(jsonStr.split())

        valid_json = re.sub(r'([{,:])(\w+)([},:])', r'\1"\2"\3', jsonStr)
        jsonObj = json.loads(valid_json)

# These are in terms of 100 grams. I also calculated for per serving       
g_per_serv = int(jsonObj['FOODSERVING_WEIGHT_1'].split('(')[-1].split('g')[0])

for k, v in jsonObj.items():
    if k == 'NUTRIENT_0':
        conv_v = (float(v)*g_per_serv)/100

        print ('%s : %s (per 100 grams)   |   %s (per serving %s' %(k, round(float(v)), round(float(conv_v)), jsonObj['FOODSERVING_WEIGHT_1']  ))

, но когда я пытаюсь использовать его на других почти идентичных веб-страницах в том же домене, это не так. Например, если я использую

page = requests.get("https://nutritiondata.self.com/facts/vegetables-and-vegetable-products/2383/2")

, я получаю ошибку

Traceback (most recent call last):
  File "scrape_test_2.py", line 20, in <module>
    jsonObj = json.loads(valid_json)
  File "/Users/benjamattesjaroen/anaconda3/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/Users/benjamattesjaroen/anaconda3/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/Users/benjamattesjaroen/anaconda3/lib/python3.7/json/decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 1 column 5446 (char 5445)

, глядя на исходный код для обеих страниц, они кажутся идентичными в том смысле, что они оба имеют

<script type="text/javascript">
    <!--
    foodNutrients = { NUTRIENT_142: ........

которая является частью, которую нужно очистить.

Я смотрел на это весь день, кто-нибудь знает, как заставить этот скрипт работать на обеих страницах, в чем здесь проблема?

1 Ответ

2 голосов
/ 09 ноября 2019

Я бы переключился на использование hjson, которое позволяет использовать ключи без кавычек и просто извлекать всю переменную foodNutrient s и анализировать, а не манипулировать строками снова и снова.


Ваша ошибка:

В настоящее время ваш сбой происходит из-за того, что количество элементов хотя бы в одном из исходных массивов имеет разную длину, и, следовательно, ваше выражение для очистки неуместно. Мы исследуем только первое известное вхождение ...

В первом URL, прежде чем использовать регулярное выражение для очистки, у вас есть:

aifr:"[ -35, -10 ]"

после:

"aifr":"[-35,-10]"

Вво-вторых, вы начинаете с массива другой длины:

aifr:"[ 163, 46, 209, 179, 199, 117, 11, 99, 7, 5, 82 ]"

после замены регулярного выражения вместо:

"aifr":"[ 163, 46, 209, 179, 199, 117, 11, 99, 7, 5, 82 ]"

у вас есть:

"aifr":"[163,"46",209,"179",199,"117",11,"99",7,"5",82]"

то есть недопустимый json. Нет больше ключа с разделителями: пары значений.


Nutshell:

Использовать hjson проще. Или обновите регулярное выражение соответствующим образом для обработки массивов переменной длины.

import requests, re, hjson

urls = ['https://nutritiondata.self.com/facts/nut-and-seed-products/3071/1','https://nutritiondata.self.com/facts/vegetables-and-vegetable-products/2383/2']

p = re.compile(r'foodNutrients = (.*?);')

with requests.Session() as s:
    for url in urls:
        r = s.get(url)
        jsonObj = hjson.loads(p.findall(r.text)[0])
        serving_weight = jsonObj['FOODSERVING_WEIGHT_1']
        g_per_serv = int(serving_weight.split('(')[-1].split('g')[0])
        nutrient_0 = jsonObj['NUTRIENT_0']
        conv_v = float(nutrient_0)*g_per_serv/100
        print('%s : %s (per 100 grams)   |   %s (per serving %s' %(nutrient_0, round(float(nutrient_0)), round(float(conv_v)), serving_weight))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...