Парсер Python (Скрапер)

Примеры

В этом уроке мы создадим скрапер – программу, собирающую данные со страниц сайта. А конкретнее, напишем код, который будет анализировать наш сайт и подсчитывать сколько раз пользователи просмотрели уроки.

Архитектура

Первое, с чего я рекомендую начать, это наметить самые крупные части будущей программы. Наивысшие абстракции, так сказать.

Нам потребуется:

  1. Сделать запрос и получить страницу из сети Интернет
  2. Проанализировать страницу и получить из неё нужные данные (количество просмотров каждого урока)
  3. Привести данные к нужному формату
  4. Просуммировать данные

Теперь наметим наши намерения в коде:

def clean():
    """
    Эта функция приводит данные к нужному формату
    """
    pass

def read():
    """
    Эта функция получает данные со страницы
    """
    pass


def get_page():
    """
    Эта функция получает данные из Интернет
    """
    pass


def main():
    """
    Это управляющая функция, которая вызывает
    остальные, суммирует результат их работы
    и выводит в консоль
    """
    pass


if __name__ == '__main__':
    main()


Несколько замечаний по приведённому коду:

  1. Запомните правило: не важно, в каком порядке указывать абстракции, но этот порядок должен быть, и он должен быть одинаковым во всё проекте. Здесь функции расположены снизу вверх в порядке их вызова.
  2. Существует подход, при котором каждый блок кода должен выполнять только одно действие. Такие функции называются чистыми и прекрасно, когда получается написать программу в этом стиле. Но этот подход, как и любой подход, не является безоговорочным законом и, если ему следовать неукоснительно, легко получить код, в котором будет больше инфраструктуры, чем логики. В нашем коде данный принцип нарушается в функции main() – она делает много всего сразу.
  3. Обратите внимание на конструкцию if __name__ == ‘__main__’. Это стандартный подход в Python для исполнения кода внутри скрипта. Дело в том, что, если импортировать любой модуль, то его код сразу же выполнится (что, чаще всего, нежелательно), а так мы защищаемся от подобного сценария. Подробнее об этом расскажу в другой раз.

Следующим шагом в написании программы может быть добавление параметров функций, возвращаемых значений, объявление констант. Следует хорошо продумать, как части кода будут обмениваться данными (что каждая из них принимает и возвращает, в какой последовательности).

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

Управляющий цикл

Начнём с функции main():

def main():
    i = 0
    result = 0
    url = 'https://pythoninfo.ru/page/{}'
    while True:
        page = get_page(url, i)
        if page.status_code != 200:
            break
        i += 1
        page_sum = sum(clean(read(page)))       
        print(f'Страница №{i}', ':', page_sum)
        result += page_sum
    print(result)


Здесь мы:

  1. Объявляем и инициализируем переменные и константы. i – просто счётчик для цикла; result – переменная, в которой мы будем накапливать итоговое значение; url – собственно строка url, в которую будем подставлять номер запрашиваемой страницы.
  2. Создаём главный цикл программы, в котором и произойдёт вся магия))
  3. В первой строке тела цикла получаем страницу сайта в переменную page
  4. Проверяем, удалось ли получить страницу. Если нет, завершаем перебор страниц
  5. Передаём в переменную page_sum сумму преобразованных данных о просмотрах каждого урока на странице
  6. Накапливаем суммы просмотров всех уроков страниц в переменную result
  7. После выхода из цикла выводим результат в терминал

Получаем страницы

Следующая часть кода – получение страницы из сети Интернет. Для этого используем библиотеку requests, о которой мы уже рассказывали в одном из прошлых уроков.

Не забудьте её установить:


И импортировать:


А дальше всё лаконично:


def get_page(url, i):
    page = requests.get(url.format(i + 1))
    return page


Думаю, здесь объяснять нечего.

Парсинг

Теперь, пожалуй, самое сложное – получить данные со страницы. К слову, эта задача становится всё сложнее с развитием front-end технологий. Стандартом в мире Питона является применение библиотеки BeautifulSoup4. Эта библиотека предназначена для парсинга (получения данных) из файлов в формате HTML и XML. Её так же надо установить:


И импортировать:


Библиотека является очень популярной, во многом, из-за удобства использования и хорошей документации, в том числе, на русском языке. Но что именно парсить? Давайте рассмотрим страницу со списком уроков. Под каждым из них указано количество просмотров. Щёлкаем по этим цифрам правой клавишей мыши и выбираем «Просмотреть код» (у меня Гугл Хром, в других браузерах название пункта выпадающего меню может отличаться). После этого в браузере откроется окно инструментов разработчика, а в нём код страницы, в том месте, где расположен код выбранного элемента. Тут, конечно, неплохо бы хоть немного уметь читать HTML. Мы видим, что нужные нам цифры – это значения тегов span с классом ‘post-card__views’. Забираем!

def read(page):
    page = page.text
    soup = BeautifulSoup(page, "html.parser")
    all_data = soup.findAll('span', class_='post-card__views')
    watch_list = [data.text for data in all_data]
    return watch_list

Подготовка данных

Теперь давайте посмотрим на списки, которые возвращает эта функция:


['69', '160', '200', '134', '657', '226', '973', '252', '156', '226', '69', '160', '200', '134']

['405', '11к.', '491', '1.5к.', '528', '1.4к.', '4.5к.', '392', '24.6к.', '2.1к.', '35', '88', '161', '106']

['3.3к.', '2.9к.', '1.3к.', '2.2к.', '4.8к.', '2к.', '1.2к.', '1.3к.', '3.9к.', '1.2к.', '30', '71', '149', '97']

['1.9к.', '620', '2.5к.', '414', '5.6к.', '2.3к.', '2.8к.', '490', '104', '1.3к.', '30', '71', '149', '97']


Обратите внимание, что есть два типа значений: те, которые можно просто преобразовать в число и те, которые надо очистить от буквы «к» и привести к тысячам. Это просто:


def clean(crud_list):
    cleaned_list = []
    for item in crud_list:
        if 'к' in item:
            item = item.replace('к.', '')
            if '.' in item:
                item = item.replace('.', '') + '00'
            else:
                item += '000'
        cleaned_list.append(int(item))
    return cleaned_list


Проверим содержимое cleaned_list:


[69, 160, 200, 134, 657, 226, 973, 252, 156, 226, 69, 160, 200, 134]
[405, 11000, 491, 1500, 528, 1400, 4500, 392, 24600, 2100, 35, 88, 161, 106]
[3300, 2900, 1300, 2200, 4800, 2000, 1200, 1300, 3900, 1200, 30, 71, 149, 97]
[1900, 620, 2500, 414, 5600, 2300, 2800, 490, 104, 1300, 30, 71, 149, 97]


Да, так гораздо лучше.

Переходим к следующему этапу… Стоп. Мы уже всё сделали?

Полный код:


import requests
from bs4 import BeautifulSoup


def clean(crud_list):
    cleaned_list = []
    for item in crud_list:
        if 'к' in item:
            item = item.replace('к.', '')
            if '.' in item:
                item = item.replace('.', '') + '00'
            else:
                item += '000'
        cleaned_list.append(int(item))
    return cleaned_list


def read(page):
    page = page.text
    soup = BeautifulSoup(page, "html.parser")
    all_data = soup.findAll('span', class_='post-card__views')
    watch_list = [data.text for data in all_data]
    return watch_list


def get_page(url, i):
    page = requests.get(url.format(i + 1))
    return page


def main():
    i = 0
    result = 0
    url = 'https://pythoninfo.ru/page/{}'
    while True:
        page = get_page(url, i)
        if page.status_code != 200:
            break
        i += 1
        page_sum = sum(clean(read(page)))
        print(f'Страница №{i}', ':', page_sum)
        result += page_sum
    print(result)


if __name__ == '__main__':
    main()

# Вывод:

Страница №1 : 3616

Страница №2 : 47306

Страница №3 : 24447

Страница №4 : 18375

93744


Да, всё верно, 93744 просмотров в общем на все материалы сайта на 11.01.2022

Код ревью

— Мы уже всё сделали?

— Нет!

Каждый раз (когда позволяет время) анализируйте свой код. Где могут быть проблемы? Что можно улучшить? Если Вы этого не сделаете, то Вы прочитаете его ещё не раз. Но уже не по своей воле ))

Как сделать код лучше?

  1. Вынес бы все константы в начало скрипта
  2. Изменил бы проверку статуса ответа. Дело в том, что запрос может быть удачным, но иметь статус не равный 200
  3. Изменил бы основной цикл таким образом, чтобы не делать url.format(i + 1) внутри функции get_page()
  4. Убрал бы page = page.text из функции read(), а передавал бы в эту функцию сразу текст
  5. Логику в функции clean() можно сделать более продвинутой. Задумайтесь, что будет, если урок наберёт миллион просмотров? Правильно, всё сломается.
  6. Количество страниц со временем будет расти и это будет негативно сказываться на быстродействии. Стоит сделать программу асинхронной.

Что действительно стоит улучшить?

Мы живём в реальном мире, где нет ничего идеального. И ВАШ КОД НИКОГДА НЕ БУДЕТ ИДЕАЛЬНЫМ. С этим стоит смириться. Всегда есть что улучшить и надо правильно расставлять приоритеты – исправить/ускорить/доделать всё не получится. Ещё одна правда состоит в том, что причина Вашего выбора часто будет лежать за границами конкретного кода. К примеру, из шести перечисленных пунктов я выберу последний, так как асинхронный скрапер полезно будет использовать в других задачах, не связанных с этим конкретным кодом.


from bs4 import BeautifulSoup
import asyncio
import aiohttp

def clean(crud_list):
    cleaned_list = []
    for item in crud_list:
        if 'к' in item:
            item = item.replace('к.', '')
            if '.' in item:
                item = item.replace('.', '') + '00'
            else:
                item += '000'
        cleaned_list.append(int(item))
    return cleaned_list


async def read(page):
    page = await page.text()
    soup = BeautifulSoup(page, "html.parser")
    all_data = soup.findAll('span', class_='post-card__views')
    watch_list = [data.text for data in all_data]
    return clean(watch_list)


async def get_page(session, url, i):
    page = await session.get(url.format(i + 1))
    return page


async def main():
    i = 0
    result = 0
    url = 'https://pythoninfo.ru/page/{}'
    async with aiohttp.ClientSession() as session:
        while True:
            page = await get_page(session, url, i)
            if page.status != 200:
                break
            i += 1
            page_sum = sum(await read(page))
            print(f'Страница №{i}', ':', page_sum)
            result += page_sum
    print(result)

asyncio.get_event_loop().run_until_complete(main())


При выборе приоритетов развития кода важно здраво оценивать:

  1. Его задачи. К примеру, если вашим порталом пользуются через АПИ и почти не используют веб интерфейс, то и тратить время на развитие фронт-энда нет смысла
  2. Перспективы развития. К примеру, если ваш проект является в стадии раннего развития на молодом рынке – пусть он будет изредка выдавать ошибки, но привлечёт много внимания своим функционалом. Пилим фичи!
  3. Стоимость ошибок. Если Ваше приложение работает с деньгами или персональными данными, Вы изо дня в день будете оттачивать мастерство в ловле мельчайших багов
  4. Стадии жизни применяемых и окружающих технологий. Пример из моей практики. В одном проекте используется Object Pascal для десктопа и Python 3 для веб-версии. Так сложилось исторически. Всем очевидны перспективы обоих языков и обоих частей проекта. Конечно, десктоп будет со временем переписан и избавлен от Паскаля – эту часть не развивают, а лишь поддерживают в работоспособном состоянии.
Оцените статью
О Python на русском языке
Добавить комментарий

  1. Дмитрий

    Подскажите как новичку,как распарсить уже готовый скрипт?
    На простом примере я ввожу эти данные:
    import paramiko
    import time
    import json

    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(«***********», port=***, username=»name», password=»password»)

    f = open(«c:\ps\Data.txt», mode=»w»)

    stdin, stdout, stderr = ssh.exec_command(«show interface ISP»)
    opt = stdout.readlines()
    opt = «».join(opt)
    print(opt)
    f.write(opt)

    stdin, stdout, stderr = ssh.exec_command(«show policy»)
    opt = stdout.readlines()
    opt = «».join(opt)
    #print(opt)
    #f.write(opt)

    stdin, stdout, stderr = ssh.exec_command(«show ip arp»)
    opt1 = stdout.readlines()
    opt1 = «».join(opt1)
    print(opt1)
    #f.write(opt1)

    stdin, stdout, stderr = ssh.exec_command(«show ip route»)
    opt2 = stdout.readlines()
    opt2 = «».join(opt2)
    print(opt2)
    #f.write(opt2)

    stdin, stdout, stderr = ssh.exec_command(«show ip name-server»)
    opt3 = stdout.readlines()
    opt3 = «».join(opt3)
    print(opt3)
    f.write(opt1 + opt2 + opt3)

    f.close()
    #while True:
    #print(«This prints once a minute.»)
    #time.sleep(1)

    Ответить
    1. Иван Душенко автор

      Здравствуйте. Не совсем понятно, что именно Вам требуется.

      Давайте разберёмся с понятиями.
      Скрапер — код, который получат код. К примеру, загружает страницу сайта.
      Парсер — код, который подготавливает данные. Сюда входит, синтаксический разбор, очистка данных, их преобразование в необходимый формат, приведение типов и тому подобное. К примеру, получение значений определённых тегов на сайте.

      Теперь, о Вашей задаче.

      Если Вам нужно получить очищенные данные из того, что возвращает скрипт, то нужно анализировать структуру этих данных. К примеру, если по SSH вернётся JSON, то Вам надо посмотреть, какие поля он содержит и что во что вложено.

      Если Вы воспринимаете сам код скрипта как данные (хотите получить что-то из самого текста скрипта), то стоит его открыть как текстовый файл и искать необходимое при помощи регулярных выражений и различных строковых методов.

      Если Вам нужно просто получать какие-то данные, генерируемые скриптом (значение переменных, результаты работы функций и вызовов методов), то Вам надо импортировать этот скрипт в свою программу или наладить межпроцессное взаимодействие при помощи очередей/сообщений/пайпов или как-то ещё.

      Конкретизируйте задачу — постараюсь помочь.

      Ответить
  2. Дмитрий

    С этого скрипта мне нужно получить данные с работы роутера.То есть принцип такой,работа самого скрипта считать информацию с устройства и вывести мне все данные.Ниже опять же командами описано то что я хочу получить на выходе работы этого скрипта.С парсингом я не то что на Вы,а просто не понимаю смысла его работы,так что если можете помочь,то попробуйте объяснить как для «Хлебушка»,и если можете сделать как пример парсинга на этом скрипте чтобы у меня хотя бы был образец на который стоит ориентироваться.

    Ответить
  3. Дмитрий

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

    Ответить
    1. Иван Душенко автор

      Этот скрипт открывает SSH туннель и присоединяется к роутеру:

      ssh = paramiko.SSHClient()
      ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
      ssh.connect(«***********», port=***, username=»name», password=»password»)

      Затем открывает текстовый файл:

      f = open(«c:\ps\Data.txt», mode=»w»)

      После этого отправляет команды роутеру:

      stdin, stdout, stderr = ssh.exec_command(«show interface ISP»)
      stdin, stdout, stderr = ssh.exec_command(«show policy»)
      stdin, stdout, stderr = ssh.exec_command(«show ip arp»)
      stdin, stdout, stderr = ssh.exec_command(«show ip route»)
      stdin, stdout, stderr = ssh.exec_command(«show ip name-server»)

      Преобразует ответ на команду в строку:

      opt = stdout.readlines()
      opt = ‘ ‘.join(opt)

      И записывает эти ответы в открытый ранее текстовый файл:

      f.write(opt)

      В конце закрывает файл:

      f.close()

      Таким образом, «данные с работы скрипта» находятся в файле по адресу «c:\ps\Data.txt».

      Узнать больше о работе с файлами можно в уроке «Работа с файлами«.

      Если надо эти данные преобразовать, то работайте с ними как с текстом. Подробнее в уроке о регулярных выражениях. Это и будет Вашим парсером.

      Если нужна помощь в этом, предоставьте содержимое файла «c:\ps\Data.txt» и то, к какому виду надо привести данные.

      Официальная документация библиотеки paramiko (отвечает за работу с SSH) здесь.
      Если нужна помощь в написании команд роутеру, то это совсем не по теме нашего сайта, но в сети есть много информации. Конкретно Вам нужен язык Bash. Начать можно с официального учебника.

      Ответить
      1. Дмитрий

        Я наверное не так выразился,прошу прощения.Мне нужно распарсить уже готовые данные,тоесть .txt файл

        Ответить
        1. Иван Душенко автор

          В таком случае предоставьте содержимое .txt файла и то, к какому виду надо привести данные; как их планируете использовать. Подозреваю, что Вы хотите, чтобы Ваша программа получила эти данные из файла и произвела с ними какие-то манипуляции. Какие?

          Ответить
          1. Дмитрий

            Мне нужно чтобы информация была без лишних строк и единым списком.Например: [K
            id: GigabitEthernet1
            index: 1
            interface-name: ISP
            type: GigabitEthernet
            description: Broadband connection

            traits: Peer

            traits: Mac

            traits: Ip

            traits: Ip6

            traits: Supplicant

            traits: Ethernet

            traits: GigabitEthernet

            link: up
            connected: yes
            state: up
            mtu: 1500
            tx-queue-length: 2000
            address: ***************
            mask: ****************
            uptime: 764695
            global: yes
            defaultgw: yes
            priority: 700
            security-level: public
            mac: *****************
            auth-type: none

            port, name = 0:
            id: GigabitEthernet1/0
            index: 0
            interface-name: 0
            label: 0
            type: Port

            traits: EthernetPort

            traits: GigabitEthernetPort

            link: up
            speed: 1000
            duplex: full
            auto-negotiation: on
            flow-control: on
            eee: off
            cable-diagnostics: no

            [K[K================================================================================
            Name IP MAC Interface
            ================================================================================
            DESKTOP-O7H02RC ************* ******************** Home
            ****************** ***************** ISP
            [K[K================================================================================
            Destination Gateway Interface F Metric
            ================================================================================
            0.0.0.0/0 ************ ISP U 0
            *************** 0.0.0.0 Guest U 0
            *************** 0.0.0.0 ISP U 0
            *************** 0.0.0.0 Home U 0
            [K[K

            И вот эту вакханалью,надо приести в нужный вид,тоесть чтобы была просто информационная таблица.

  4. Дмитрий

    Это часть данных которые в .txt полученных со скрипта

    Ответить
    1. Иван Душенко автор

      Приведу не самый быстрый вариант, но такое решение проще для понимания.

      (Сайт автоматически удаляет пробелы в начале строки, поэтому в коде все отступы слева я заменю на знак ‘~’).

      Сначала создаю папку, в которую помещаю файл.txt. Потом создаю в этой же папке скрипт Python.

      В скрипте создаю переменную patterns типа список, в котором перечисляю кортежи, содержащие каждый по две строки: первая — что надо найти, вторая — на что заменить.

      patterns = [
      ~~~~('[K\n', ''),
      ~~~~('[K', ''),
      ~~~~('\n\n\n', '\n'),
      ~~~~('\n\n', '\n'),
      ~~~~('=', ''),
      ~~~~('*', ''),
      ~~~~('\n ', '\n'),
      ]

      Так, к примеру, каждый знак «равно» будет заменён на пустую строку или, проще говоря, удалён. С помощью этих кортежей можно настроить все преобразования.

      Затем открываю файл.txt и читаю его содержимое. После этого применяю к тексту замену перечисленных ранее паттернов:

      with open('example.txt', 'r') as f:
      ~~~~text = f.read()
      ~~~~while True:
      ~~~~~~~~do = False
      ~~~~~~~~for pattern in patterns:
      ~~~~~~~~~~~~if pattern[0] in text:
      ~~~~~~~~~~~~~~~~text = text.replace(*pattern)
      ~~~~~~~~~~~~~~~~do = True
      ~~~~~~~~if not do:
      ~~~~~~~~~~~~break
      ~~~~print(text)

      # Вывод:

      id: GigabitEthernet1
      index: 1
      interface-name: ISP
      type: GigabitEthernet
      description: Broadband connection
      traits: Peer
      traits: Mac
      traits: Ip
      traits: Ip6
      traits: Supplicant
      traits: Ethernet
      traits: GigabitEthernet
      link: up
      connected: yes
      state: up
      mtu: 1500
      tx-queue-length: 2000
      address:
      mask:
      uptime: 764695
      global: yes
      defaultgw: yes
      priority: 700
      security-level: public
      mac:
      auth-type: none
      port, name 0:
      id: GigabitEthernet1/0
      index: 0
      interface-name: 0
      label: 0
      type: Port
      traits: EthernetPort
      traits: GigabitEthernetPort
      link: up
      speed: 1000
      duplex: full
      auto-negotiation: on
      flow-control: on
      eee: off
      cable-diagnostics: no
      Name IP MAC Interface
      DESKTOP-O7H02RC Home
      ISP
      Destination Gateway Interface F Metric
      0.0.0.0/0 ISP U 0
      0.0.0.0 Guest U 0
      0.0.0.0 ISP U 0
      0.0.0.0 Home U 0

      Вы можете использовать мой код, просто меняя паттерны в списке patterns.
      Так же настоятельно рекомендую изучить уроки:

      https://pythoninfo.ru/osnovy/re
      https://pythoninfo.ru/osnovy/str-python
      https://pythoninfo.ru/osnovy/rabota-s-faylami

      Ответить
      1. Дмитрий

        Простите,но я не совсем понимаю как это в полном виде должно выглядеть.Не сам итог а написание.

        Ответить
        1. Дмитрий

          Я просто правда не понимаю как 1е со 2ым связывается чтобы была полноценная картина для удачного итога.

          Ответить
          1. Дмитрий

            И уж прошу прощения за такую цж назойливость и наглость,но вопрос,как из этих данных сделать табличную форму?

        2. Иван Душенко автор

          Просто пишите первую часть, потом вторую (каждый знак «~» замените на пробел), вот так:

          patterns = [
          ~~~~(‘[K\n’, »),
          ~~~~(‘[K’, »),
          ~~~~(‘\n\n\n’, ‘\n’),
          ~~~~(‘\n\n’, ‘\n’),
          ~~~~(‘=’, »),
          ~~~~(‘*’, »),
          ~~~~(‘\n ‘, ‘\n’),
          ]

          with open(‘example.txt’, ‘r’) as f:
          ~~~~text = f.read()
          ~~~~while True:
          ~~~~~~~~do = False
          ~~~~~~~~for pattern in patterns:
          ~~~~~~~~~~~~if pattern[0] in text:
          ~~~~~~~~~~~~~~~~text = text.replace(*pattern)
          ~~~~~~~~~~~~~~~~do = True
          ~~~~~~~~if not do:
          ~~~~~~~~~~~~break
          ~~~~print(text)
          input()

          Создаёте файл example.txt, в него помещаете результаты работы скрипта, опрашивающего роутер. Помещаете этот файл и файл с кодом, приведённым выше в одну папку. Запускаете код и видите вывод в консоли как в предыдущем комментарии. Нажимаете «Enter» и консоль закрывается.

          Для записи данных в таблицу Эксель проще всего воспользоваться библиотекой openpyxl. Это большая отдельная тема. Думаю, если Вы ознакомитесь с примерами из документации, сделать это будет не сложно.
          https://openpyxl.readthedocs.io/en/stable/

          Ответить
          1. Дмитрий

            Может не правильно выразился,просто данные нужны не списком а табличной формы,и желательно в Json.Короче чем дальше тем хуже

  5. Иван Душенко автор

    import json
    patterns = [
    ~~~~(‘[K\n’, »),
    ~~~~(‘[K’, »),
    ~~~~(‘\n\n\n’, ‘\n’),
    ~~~~(‘\n\n’, ‘\n’),
    ~~~~(‘=’, »),
    ~~~~(‘*’, »),
    ~~~~(‘:’, »),
    ~~~~(‘\n ‘, ‘\n’),
    ]

    with open(‘example.txt’, ‘r’) as f:
    ~~~~text = f.read()
    ~~~~while True:
    ~~~~~~~~do = False
    ~~~~~~~~for pattern in patterns:
    ~~~~~~~~~~~~if pattern[0] in text:
    ~~~~~~~~~~~~~~~~text = text.replace(*pattern)
    ~~~~~~~~~~~~~~~~do = True
    ~~~~~~~~if not do:
    ~~~~~~~~~~~~break
    ~~~~text_to_list = [i.split(‘ ‘) for i in text.split(‘\n’)]

    with open(‘data.json’, ‘w’) as f:
    ~~~~json.dump(text_to_list, f)

    Итоговый json будет расположен в файле data.json в той же папке.

    Если так тоже не подходит, приведите более подробное описание необходимого результата и предоставьте пример.

    Ответить
Adblock
detector