Данные, введенные вами, представляют собой HTML и встроенный JSON, причем JSON разбивается вдребезги.
Я бы максимально использовал парсеры, чтобы приблизиться к этому.Для HTML мы можем использовать lxml
, что позволяет легко получить доступ к текстовому содержанию интересующего элемента (<script type="application/ld+json">
) с помощью XPath.
Когда у нас есть элемент text, мы можем использовать json.loads()
.Это работает для первого такого элемента на странице примера, но не работает для второго элемента с ошибкой "Expecting ',' delimiter"
.
Раздел, который нарушает работу:
"claimReviewed": "İDDİA: Diyanet İşleri Başkanlığı "Çocuklara Zekâ Geliştirici Oyuncaklar Vermeyin" şeklinde bir açıklama yaptı.
",
, который будет корректен как:
"claimReviewed": "İDDİA: Diyanet İşleri Başkanlığı \"Çocuklara Zekâ Geliştirici Oyuncaklar Vermeyin\" şeklinde bir açıklama yaptı.\n\n ",
Итак, есть две вещи, которые нужно исправить:
- пропущенные обратные слеши перед двойными кавычками
- буквальные символы новой строки должны бытьзаменяется на
\n
После этого JSON должен выполнить синтаксический анализ.
Мы можем использовать информацию об исключении из анализатора JSON, чтобы выполнить соответствующие исправления в JSON, неоднократно пытаясь проанализироватьпока он не преуспеет или не столкнется с ошибкой, которую он еще не знает, как исправить.
# json_utils.py
import json
class JsonRepairError(Exception):
def __init__(self, e, text):
message = "Don't know how to fix '%s', position %s (-->%s<--)" % (e.msg, e.pos, text[e.pos-5:e.pos+5])
super().__init__(message)
self.text = text
def json_repair(text):
while True:
try:
return json.loads(text)
except json.decoder.JSONDecodeError as e:
if e.msg == "Expecting ',' delimiter":
if text[e.pos-1] == '"':
text = text[:e.pos-1] + '\\' + text[e.pos-1:]
continue
elif text[e.pos-2] == '"':
text = text[:e.pos-2] + '\\' + text[e.pos-2:]
continue
elif e.msg == "Invalid control character at":
if text[e.pos] == '\n':
text = text[:e.pos] + '\\n' + text[e.pos+1:]
continue
raise JsonRepairError(e, text) from None
, которую мы можем использовать следующим образом:
import requests
from html import unescape
from lxml import html
from json_utils import json_repair
response = requests.get("https://gist.githubusercontent.com/isspek/6b687e69bbfbb1f5519de5c13e92e4da/raw")
tree = html.fromstring(response.content)
elem = tree.findall('.//script[@type="application/ld+json"]')[-1]
text = unescape(elem.text) # this gets rid of the stray in the data
data = json_repair(text)
print(data["claimReviewed"])
Это печатаетправильный вывод:
İDDİA: Diyanet İşleri Başkanlığı "Çocuklara Zekâ Geliştirici Oyuncaklar Vermeyin"
şeklinde bir açıklama yaptı.
Преимущество состоит в том, что этот подход легко адаптировать к любому ранее необработанному типу ошибки - просто добавьте пару проверок if
/ elif
и произведите исправление соответствующего значения.Это гораздо сложнее сделать с помощью регулярных выражений.Кроме того, он намного более устойчив к форматированию изменений в HTML и в целом проще в обслуживании.