Авито профиль заблокирован

Ни когда не думала что коснется меня такая ситуация, но 17.09.25, мои профили на Авито были заблокированы ссылаясь на то что я не прошла проверку документов, я конечно была удивлена таким решением со стороны Авито , так как у меня пройдены проверки , ни на одном профиле , у меня их несколько на разных симках телефона и профили привязаны . Я конечно…

Из PDF в Excel, когда не все так просто…

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

Разные шаблоны документов затрудняют процесс извлечения данных и…

9

Парсер vc.ru

Сегодня я вам покажу парсер статьи на vc.ru.

Буду использовать beautifulsoup4,requests.

2

Избавляемся от продуктов априори – использование ассоциативных правил для поиска комбинаций

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

5

22 сниппета на Python для повседневных задач

22 сниппета на Python для повседневных задач

В этой статье хотелось бы поделиться 22 фрагментами кода на Python, которые помогут вам в решении повседневных задач.

6

Играем в эпидемиологов

Для того чтобы создать простейшую модель по распространению инфекции, нам потребуется немного математики и небольшой скрипт на python. Модель будет упрощенная, т.е. мы пропустим ряд параметров, влияющих на распространение болезни. Поэтому нельзя сказать, что реальная инфекция распространяется именно так, как на графиках ниже. Хотя некоторые тенденц…

2

Сложности при выгрузке 500 гб из базы данных и пример их решения

Хочу поделиться с вами забавным случаем, который произошел со мной. Была поставлена задача — в кратчайшие сроки выгрузить 500 ГБ информации из базы данных (БД). Но на тот момент места на жестком диске катастрофически не хватало, и не было возможности оперативно очистить или добавить новый. К счастью, в наличии был защищенный файловый информационный…

Выгрузка данных из несвязанных таблиц в отдельные файлы с помощью python

В предыдущей статье мы рассмотрели, как с помощью Python можно создать и запустить SQL запрос с множеством условий для выгрузки информации из баз данных (БД) Oracle в один файл частями.

В данной статье мы рас…

2

Как извлечь изображения из PDF c помощью Python, сохраняя их качество?

Есть несколько способов извлечь изображения из файла PDF. Самый простой способ – просто сделать снимок экрана с изображением, присутствующим на любой странице PDF-файла, и обрезать изображение в соответствии с вашими требованиями. Этот способ выглядит очень простым, но что, если в файл PDF содержит 100 или 1000 изображений, и вы хотите, чтобы все о…

5

Выгружаем из базы данных с помощью Python

Задача на выгрузку данных из одной таблицы, с одним условием для фильтра решается посредством создания простого SQL-запроса. Но она легко становится трудоемкой в исполнении, если в фильтрации применить множество условий. Давайте представим, что необходимо выгрузить данные из одной таблицы по фильтру, где первые две цифры ИНН начинаются на «66». SQL…

select * from tabl where inn like '66%'
3

Как применить Process Mining при отсутствии логов? ​

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

Анализировать процесс мы решили с исполь…

2

Python: как создать простейшего голосового помощника?

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

В данной статье представлена программа, которая может стать…

8
):\n df = pd.read_excel(filename)\n if df.empty: # если пустой файл то записываем в пустые\n empty.append(filename)\n continue\n df.dropna(how='all', inplace=True)\n df.reset_index(drop=True, inplace=True)\n cols = list(df.columns) #выделяем колонки из таблицы в файле\n cols = clean(cols)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Далее удаляем ненужные поля (для примера допустим, что в шаблоне dd_eirs_2 присутствует столбец 'Акт сдачи-приемки')

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"if 'Акт сдачи-приемки' in df.columns[0]:\n df.drop(df.columns[0], axis=1, inplace=True)\n df.columns = df.loc[0]\n df = df[1:]\n cols = list(df.columns)\n cols = clean(cols)\n if cols == dd_eirs_2: #смотрим подходят ли колонки каждому шаблону\n for i in df.index:\n if 'ИТОГО (' in str(df.loc[i, 'ИТ-услуга ТП']):\n df = df[:i]\n break\n df.columns = cols\n df['filename'] = os.path.basename(filename)\n df_eirs = pd.concat([df_eirs, df]) #если установили соответствие с шаблоном то записываем в общий файл\n find.append(filename)\n elif cols == dd_eirs_1: # если шаблону не подошло то сравниваем с другими\n for i in df.index:\n if 'ИТОГО (' in str(df.loc[i, 'ИТ-услуга ТП']):\n df = df[:i]\n break\n df.columns = cols\n df['filename'] = os.path.basename(filename)\n df_eirs = pd.concat([df_eirs, df])\n find.append(filename)\n else … #<обработка других шаблонов>","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В результате получили сводную таблицу для проведения дальнейшего анализа:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"886df1d7-f645-5124-a5f3-089328b166e7","width":725,"height":175,"size":17789,"type":"png","color":"e2e3df","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAQUGCf/EAB8QAAIBBAMBAQAAAAAAAAAAAAECAwAEBRESMVEhcf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAMAwEAAhEDEQA/AND8nPkbPMPYw3960PFW3vZHwHvkPfKkqIMXBLBHIL+8YOgYEy6J2PymgLrGY2e7NxNj7aSXiBzaJS3XpG6CZQokcSRxqFVVAVQNAADoVB//2Q=="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Второй способ - поиск паттернов в шаблонах.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

О применении данного способа расскажем на примере обработки 2-х заказ-нарядов на ремонт автомобиля, оформленных по разным шаблонам. Задача: узнать у каких машин, когда были совершены ремонтные работы, а также детальную информацию по работам (детали, их количество, вид работ, стоимость и т.д.).

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"a828bf9d-2d5b-56fe-b5b2-3ed77b52ae3c","width":1045,"height":641,"size":306781,"type":"png","color":"d3d0d0","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABQMJ/8QAIhAAAgEDAwUBAAAAAAAAAAAAAQIDAAQRBhIxBSEjJFFh/8QAFwEBAQEBAAAAAAAAAAAAAAAAAAEDBP/EABoRAAIDAQEAAAAAAAAAAAAAAAABAhESIUH/2gAMAwEAAhEDEQA/ANErXrnpvfxaVgjMY3B45WBBP4V5rO68OrKk60WR9RMisYLjJAJ81LZMwBEd57pxO7SBoxncc55+1RFtPgTJdXSuyrcygAkABz2oSz//2Q=="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

При обработке данных вторым способом мы использовали другой алгоритм действий:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def get_info_from_pdf(row):\n try:\n f = fitz.open(row['FilePath'])\n text = ''\n for page in f:\n text += page.get_text()\n text = text.lower()\n text = re.sub(r'\\n', ' ', text)\n#выделяем текст из файла, в тексте ищем интересующую нас информацию с помощью регулярных выражений\n zakaz = re.findall(r'(заказ-наряд)|(наряд-заказ)|(заказ-наряда)|(наряд-заказа)|(наряд-заказу)|(заказ-наряду)', text)\n if len(zakaz)==0:\n if len(re.findall(r'пробег\\D*\\d+', text))!= 0:\n row['Пробег'] = re.findall(r'пробег\\D*\\d+', text)[0]\n if len(re.findall(r'sd\\d+', text)) != 0: \n row['Номер SD'] = re.findall(r'sd\\d+', text)[0]\n return row\n except:\n return row","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

- с помощью Python в документах были найдены и извлечены все таблицы;

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def extract_table(o):\n table = tables[o].df\n table.columns = [ str(x).lower().translate(str.maketrans('','',string.punctuation)) for x in table.columns] # выделяем названия колонок в таблице\n df = pd.DataFrame()\n for i in table.columns: #итерируемся по каждой колонке\n for j in range(len(table)):\n data = str(table[i][j]).lower().translate(str.maketrans('','',string.punctuation))\n data = [i] + data.split()\n for d in data:\n\t\t#смотрим встречается ли в данных колонки слова маркеры нужных нам данных\n if d in ['код']:\n df[\"Код\"] = table[i] # если нужный маркер найден, то данные из колонки записываются в соответствующую колонку общего файла\n if d in ['наименованиие', 'работ', 'работы', 'услуги', 'услуг', 'услуга']:\n df[\"Работы\"] = table[i]\n if d in ['время']:\n df[\"Время\"] = table[i]\n if d in ['нч']:\n df[\"Н/ч\"] = table[i]\n if d in ['колво','кол']:\n df[\"Кол-во\"] = table[i]\n if d in ['цена']:\n df[\"Цена\"] = table[i]\n if d in ['сумма']:\n df[\"Сумма\"] = table[i].drop([j])\n return df.dropna(how='any')","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В результате была получена следующая таблица для проведения дальнейшего анализа:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"3ac57b62-0654-5e1b-be3d-2b287db3cc70","width":868,"height":157,"size":49970,"type":"png","color":"d7d7d4","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAgUHCf/EACIQAAEDBAICAwAAAAAAAAAAAAECAxEABAUSBiETMVGRwf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A0ZxuYzPIH/E9fPY/xIK5t9Rt3EK3CgfnqPygSXnI+TJu30t5K5SkOKCQnWAJ9CRUVQYEzHdVAFtskktp+qD/2Q=="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Оба способа имеют свои плюсы и минусы.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для первого способа характерна бОльшая точность выходных данных. Однако, он требует проведения ручного анализа.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для второго способа использование регулярных выражений позволяет снизить трудозатраты. Поиск подходящего выражения или ключевого слова занимает не так много времени, является более универсальным и не требует доработки при добавлении новых вариантов документов. Однако, при применении данного инструмента, могут встречаться некоторые артефакты выходных данных (например, номер машины: ъ000уъ00, дата договора 20.07.2024 и т.д.), которые могут усложнить дальнейший анализ.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

При анализе 100 пакетов документов, содержащих разнородные таблицы, я получил следующую статистику:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"c5f71589-f1e4-5193-8932-acbfc957d450","width":983,"height":215,"size":16403,"type":"png","color":"0e0e0e","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/wAALCAAKAAoBAREA/8QAFgABAQEAAAAAAAAAAAAAAAAABQQJ/8QAJRAAAgEDAwIHAAAAAAAAAAAAAQIDAAQRBQYhEzEUIjIzQVFh/9oACAEBAAA/ANJLTburxW8oudw3rSzAxIXn9vv5sA4J7fNRHb25VJUazqjAcZ8SnP766VtFVIFVFCjqMcAY5yaLmtLUzOTbREliSSg+6//Z"}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Как видно из сравнительной таблицы, ручная обработка даёт высокую точность обработки данных, но требует большего количества времени, чем поиск паттернов. Очевидно, чем более однородные таблицы приводятся к единому виду, тем выше точность данных и скорость обработки.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":1106,"hits":10451,"reads":null,"online":0},"dateFavorite":0,"hitsCount":10451,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id447345/862553-iz-pdf-v-excel-kogda-ne-vse-tak-prosto","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":9}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":687695,"customUri":null,"subsiteId":1510590,"title":"Парсер vc.ru","date":1683428428,"dateModified":1683428428,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Сегодня я вам покажу парсер статьи на vc.ru.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Буду использовать beautifulsoup4,requests.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"pip install beautifulsoup4\npip install requests","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Теперь берем url статьи , я взял url своей статьи.Пишем легкий код:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"from bs4 import BeautifulSoup\nimport requests\n\nurl = 'https://vc.ru/u/1510590-python-idea/686866-oop-v-python'#здесь может быть ваш url\nresponse = requests.get(url)#создание запроса к ссылке\nprint(response.status_code)#если в консоли вышло число 200 идем дальше","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Добавляем bs4

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"soup = BeautifulSoup(response.text, 'html.parser')\ntitle = soup.find('h1', class_='content-title')\ncodes = soup.find_all('figure')\ntexts = soup.find_all('div', class_='l-island-a')","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И последнее вывод данных парсера:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"print(title.get_text(strip=True))\nfor text in texts:\n print(t.get_text(strip=True))\nfor code in codes:\n print(c.get_text(strip=True))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Весь код:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"from bs4 import BeautifulSoup\nimport requests\n\nurl = 'https://vc.ru/u/1510590-python-idea/686866-oop-v-python'\nresponse = requests.get(url)\nprint(response.status_code)\n\nsoup = BeautifulSoup(response.text, 'html.parser')\ntitle = soup.find('h1', class_='content-title')\ncodes = soup.find_all('figure')\ntexts = soup.find_all('div', class_='l-island-a')\nprint(title.get_text(strip=True))\nfor text in texts:\n print(t.get_text(strip=True))\nfor code in codes:\n print(c.get_text(strip=True))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Ну и все конец.

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":5,"favorites":5,"reposts":0,"views":739,"hits":632,"reads":null,"online":0},"dateFavorite":0,"hitsCount":632,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1510590/687695-parser-vcru","author":{"id":1510590,"name":"Python Idea","nickname":null,"description":"Раскажу вам о всех чертогах постижения IT и покажу что программистом могут стать все, если захотят и подпишутся на меня,конечно.","uri":"","avatar":{"type":"image","data":{"uuid":"2f87cc08-8d35-5587-947b-9b51331b9a0b","width":2560,"height":1440,"size":1257538,"type":"png","color":"1c1c1c","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"d61baea8-5da2-5fb5-8aa8-b95dde4fbbb3","width":1920,"height":1080,"size":913739,"type":"png","color":"14120f","hash":"","external_service":[]}},"cover_y":26},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3943935,"userId":1510590,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3943935"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1510590,"name":"Python Idea","nickname":null,"description":"Раскажу вам о всех чертогах постижения IT и покажу что программистом могут стать все, если захотят и подпишутся на меня,конечно.","uri":"","avatar":{"type":"image","data":{"uuid":"2f87cc08-8d35-5587-947b-9b51331b9a0b","width":2560,"height":1440,"size":1257538,"type":"png","color":"1c1c1c","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"d61baea8-5da2-5fb5-8aa8-b95dde4fbbb3","width":1920,"height":1080,"size":913739,"type":"png","color":"14120f","hash":"","external_service":[]}},"cover_y":26},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3943935,"userId":1510590,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3943935"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":658954,"customUri":null,"subsiteId":447345,"title":"Избавляемся от продуктов априори – использование ассоциативных правил для поиска комбинаций","date":1680873087,"dateModified":1680873087,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Я очень люблю готовить, поэтому постоянно закупаюсь ингредиентами для различных блюд. Однако, в последний раз я закупил их слишком много, теперь срок их годности подходит к концу. Но не выкидывать же продукты в мусорную корзину? Конечно, я бы мог использовать их все разом, но вряд ли блюдо в таком случае получится вкусным. Или же приготовить множество вариаций совершенно разной еды, но я так много не съем и на готовку у меня времени в обрез. Потому было принято решение приготовить минимальное количество блюд, использовав все доступные ингредиенты.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Что рассматриваем?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Долгое время я копил записи рецептов с форумов. Был собран датафрейм: 8102 строк и 60 столбцов, где строки — это блюда и их рецепты, а столбцы – ингредиенты. Строки могут повторяться – это связано с тем, что рецепт какого-либо блюда часто упоминался среди пользователей, значит рецепт проверенный.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

С помощью библиотеки pandas посмотрю на собранный датафрейм.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import pandas as pd\ndf = pd.read_excel('рецепты.xlsx', sheet_name='Sheet1', engine='openpyxl')df = df.set_index(['Блюдо'])\ndf","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"6966f0b8-bdb7-54ee-b0fe-393a66aa1a5c","width":850,"height":396,"size":22804,"type":"png","color":"f3f3f2","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Датафрейм представляет собой бинарную матрицу, где 1 – наличие продукта в рецепте и 0 – его отсутствие.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

На второй странице у меня находятся классификации продуктов, где ОП – обязательный продукт, ВП – вспомогательный продукт и ПП – приправы. Запишу их в отдельные датафреймы на будущее, для возможности выбора между ними. Сначала найду самые популярные, проверенные рецепты.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Как это сделать?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

На самом деле можно посчитать «в лоб» и сравнить значения, но это слишком долго. Поэтому применю существующий инструмент, который позволяет выполнять поиск наиболее частых комбинаций, используя данные такого же формата – поиск ассоциативных правил или association rule mining.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Поиск ассоциативных правил — это метод выявления отношений между переменными, использующийся в data mining. Существует множество методов анализа. Алгоритм Apriori, который я использовал, является наиболее популярным, простым и понятным инструментом.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Существует множество метрик алгоритма Apriori. Но так как мне интересна только частота, была использована только поддержка. Поддержка (Support) – соотношение количества полученных транзакций к общему количеству транзакций. Иными словами – как часто полученный набор товаров встречается во всем датасете.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

О работе алгоритма, кратко:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

1. Вычисляется поддержка для каждого элемента. Сразу исключаются те элементы, у которых поддержка меньше заданного минимума.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

3. С ростом размера набора метрика поддержки либо уменьшается, либо остается неизменной. Иными словами, набор будет популярным только тогда, когда его элементы (или подмножества) также будут популярны. Этот принцип позволяет сократить пространство поиска, не тратя время на перебор абсолютно всех возможных комбинаций. Например, если поддержка для комбинации МОЛОКО+СЕЛЕДКА ниже заданного минимума, из дальнейшего перебора исключаются абсолютно все рецепты, которые содержат эту комбинацию.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вызову библиотеку с реализованным алгоритмом и применю его для всего датасета.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Установка библиотек\nfrom mlxtend.frequent_patterns import apriori\nfrom mlxtend.frequent_patterns import association_rules\n#Вызов алгоритма Априори для всего датасета\nfr = apriori(df, min_support=0.0075, use_colnames=True)","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b9e5a3ed-b82d-505f-9d99-0f44f66d2ff6","width":844,"height":477,"size":94409,"type":"png","color":"f2f2f1","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Но от продуктов избавляться надо, поэтому попробую доработать алгоритм:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

1. Алгоритм Apriori достаточно требовательный и его отработка может занять много времени. Поэтому есть смысл искать вариации продуктов по группам, для каждого блюда отдельно.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

2. Разделю основные продукты, вспомогательные и приправы.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Записываем в датафрейм лист с классификацией продуктов\ndff = pd.read_excel('рецепты.xlsx', sheet_name='po', engine='openpyxl')\nop = []\nvp = []\npp = []\n#iterrows для лучшей читаемости, таблица небольшая. Соотносим значение столбца type в выделенные одноименные массивы\nfor i, row in dff.iterrows():\n if row['type'] == 'ОП':\n op.append(row['po'])\n elif row['type'] == 'ВП':\n vp.append(row['po'])\n else:\n sp.append(row['po'])\n#Превращаем собранные массивы в датасеты, для удобного вызова\ndf_op = df[op]\ndf_sp = df[vp]\ndf_pp = df[pp]","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Указываю группировку (возможно будут другие атрибуты, например, страна происхождения блюда) и выбор используемых продуктов.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#В х попадает атрибут по которому будет группировка, в у – подготовленные выше датасеты\nx = input(\"Группировать: \")\ny = input(\"Вид продуктов:\")\nif y=='ОП':\n gr = df_op.groupby(level=[x])\nelif y=='ВП':\n gr = df_sp.groupby(level=[x])\nelif y=='ПП':\n gr = df_pp.groupby(level=[x])\nelse:\n gr = df.groupby(level=[x])\n#Предварительный вывод полученной группировки\ngr.head()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#В appended_data запишем результаты работы алгоритма\nappended_data = []\nfor name, group in gr:\n#В каждой группе выполняем алгоритм априори. Рассматриваем частоту внутри каждой группы обособленно от всего датасета. Параметры min_support (минимальная поддержка)и max_len (максимальная длинна комбинации)задаются пользователем, сохраняются в переменные\n fr = apriori(group, min_support=float(z), use_colnames=True, max_len=int(a))\n#Добавляем столбец в котором будет длинна комбинации\n fr['length'] = fr['itemsets'].apply(lambda x: len(x))\n#Делаем сортировку поддержки по убыванию. От самых частых до самых редких комбинаций \n idx = fr.groupby(['length'])['support'].transform(max) == fr['support']\n#leng отвечает за минимальную длину, задается пользователем. Внутри apriori нет возможности задать минимальную длину комбинации, поэтому просто фильтруем \n fr = fr[idx][(fr[idx]['length'] >= int(leng))]\n#Добавляем столбец в котором будет указана группа, к которой принадлежит комбинация\n fr['name'] = name \n fr['itemsets'] = fr['itemsets'].apply(lambda x: ', '.join(list(x))).astype(\"unicode\")\n#Собираем результат\nappended_data.append(fr)\nappended_data = pd.concat(appended_data)\nappended_data = appended_data.reset_index()\nappended_data.head()","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"3ba8cc8c-f4a5-541e-8885-e4584b47cf48","width":806,"height":295,"size":76863,"type":"png","color":"f3f2f0","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"test = pd.DataFrame()\ntest['index'] = ''\ntest['itemsets'] = ''\ni = 0\n#Пересобираем исходный датасет. Так как нужно проверить комбинации как подмассивы, делаем из строки массив, куда заносим названия столбцов со значением 1\nfor row in df.itertuples():\n test.loc[i, 'index'] = ', '.join((str(x) for x in getattr(row, 'Index')))\n x = []\n for name, value in row._asdict().items():\n if value == 1:\n x.append(name)\n test.loc[i, 'itemsets'] = ', '.join(x)\n i += 1","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Затем важно преобразовать полученные наборы выше, так как Itemsets содержит в себе frozenset.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"i = 0\nfor row in appended_data.itertuples():\n a = sorted(getattr(row, 'itemsets').split(', '))\n appended_data.loc[i, 'itemsets'] = ', '.join(a)\n i += 1","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Соединяю список рецептов и процент попадания элементов в другие рецепты.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"i = 0\nappended_data['where'] = ''\nappended_data['percent'] = ''\nfor row in appended_data.itertuples():\n#Получаем значения из itemsets \n a = getattr(row, 'itemsets')\n y = []\n count = 0\n for roww in test.itertuples():\n#Если у нас полное совпадение по продуктам, записываем рецепт\n if all(x in (getattr(roww, 'itemsets')) for x in a):\n y.append((getattr(roww, 'index')))\n count +=1\n appended_data.loc[i, 'where'] = ' | '.join(y)\n#Процент вхождения ингредиентов во всех рецептах \n appended_data.loc[i, 'percent'] = count/df.shape[0]*100\n i += 1","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"f0aaed7e-a5dd-5a85-9f7f-1a5de86f821c","width":1089,"height":522,"size":261654,"type":"png","color":"f2f2f0","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Я получил отличную таблицу с отобранными рецептами в количестве 90 штук и мои продукты спасены.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Итог

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Алгоритм Apriori достаточно удобный инструмент, несмотря на то что он в действительности далеко не новый. Существует много других алгоритмов, в том числе и модификации Apriori для полного анализа продуктовой корзины. Но для типовых задач комбинаторики этот инструмент подходит отлично. Пример с продуктами лишь более просто и наглядно демонстрирует возможности применения этого инструмента. В действительности задачи на поиски комбинаций с полным покрытием встречаются и в повседневной работе любого IT специалиста. Конечно, можно было бы разработать тяжеловесный скрипт с использованием технологий искусственного интеллекта, но займемся этим в будущем. Ведь важно не забивать гвозди микроскопом, и уметь использовать подобные «допотопные» инструменты. Это поможет сэкономить ваше время и ресурсы.

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":762,"hits":451,"reads":null,"online":0},"dateFavorite":0,"hitsCount":451,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id447345/658954-izbavlyaemsya-ot-produktov-apriori-ispolzovanie-associativnyh-pravil-dlya-poiska-kombinacii","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":5}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":479576,"customUri":null,"subsiteId":552457,"title":"22 сниппета на Python для повседневных задач","date":1660122560,"dateModified":1660122560,"blocks":[{"type":"media","cover":true,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"e82d7523-65b3-5cf8-9cdc-cb83ce518d8a","width":1400,"height":787,"size":23721,"type":"png","color":"8b54fb","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В этой статье хотелось бы поделиться 22 фрагментами кода на Python, которые помогут вам в решении повседневных задач.

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"1. Прием нескольких входных значений, разделенных пробелами"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот фрагмент кода позволяет принимать сразу несколько значений вводимых данных, которые разделены пробелами. Он пригодится при решении задач на соревнованиях по программированию.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"## Прием двух целых чисел в качестве входных данных\na,b = map(int,input().split())\nprint(\"a:\",a)\nprint(\"b:\",b)\n\n## Прием списка в качестве входных данных\narr = list(map(int,input().split()))\nprint(\"Input List:\",arr)","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"2. Одновременный доступ к индексу и значению"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Одновременный доступ к индексу и значению в цикле позволяет получить встроенная функция enumerate():

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"arr = [2,4,6,3,8,10]\n\nfor index,value in enumerate(arr):\n print(f\"At Index {index} The Value Is -> {value}\")\n\n'''Output\nAt Index 0 The Value Is -> 2\nAt Index 1 The Value Is -> 4\nAt Index 2 The Value Is -> 6\nAt Index 3 The Value Is -> 3\nAt Index 4 The Value Is -> 8\nAt Index 5 The Value Is -> 10\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"3. Проверка использования памяти"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот фрагмент используется для проверки использования памяти объекта:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import sys\na = 20\nprint(sys.getsizeof(a)) #28","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"4. Проверка существования файла"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# Метод перебора\nimport os.path\nfrom os import path\n\ndef check_for_file():\n print(\"File exists: \",path.exists(\"data.txt\"))\n\nif __name__==\"__main__\":\n check_for_file()\n \n'''\nFile exists: False\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"5. Вывод уникального идентификатора переменной"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Уникальный идентификатор переменной находится с помощью метода id(). Для этого нужно просто передать в метод имя переменной:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"a = 20\nname = 'Karl'\ndeci = 5.5\n\nprint(id(a)) # 2557830785936\nprint(id(name)) # 2557830785956\nprint(id(deci)) # 2557830785996","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"6. Вычисление времени выполнения в оболочке"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Иногда важно знать время выполнения в оболочке или в блоке кода для получения лучшего алгоритма с минимальным количеством затраченного им времени:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# МЕТОД 1\nimport datetime\nstart = datetime.datetime.now()\n\"\"\"\nCODE\n\"\"\"\nprint(datetime.datetime.now()-start)\n\n# МЕТОД 2\nimport time\nstart_time = time.time()\nmain()\nprint(f\"Total Time To Execute The Code is {(time.time() - start_time)}\" )\n\n# МЕТОД 3\nimport timeit\ncode = '''\n## Фрагмент кода, чье время выполнения подлежит измерению\n[2,6,3,6,7,1,5,72,1].sort()\n'''\nprint(timeit.timeit(stmy = code,number = 1000))","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"7. Цепочка вызовов функций"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В Python есть возможность вызывать несколько функций в одной строке:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def add(a,b):\n return a+b\n\ndef sub(a,b):\n return a-b\n\na,b = 9,6\nprint((sub if a > b else add)(a, b))","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"8. Перестановка значений"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Быстрый способ поменять местами две переменные без использования дополнительной:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"a,b = 5,7\n\n# Метод 1\nb,a = a,b\n\n# Метод 2\ndef swap(a,b):\n return b,a\nswap(a,b)","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"9. Калькулятор без if-else"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот фрагмент кода показывает, как просто написать калькулятор без использования каких-либо условных операторов if-else:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import operator\n\naction = {\n \"+\" : operator.add,\n \"-\" : operator.sub,\n \"/\" : operator.truediv,\n \"*\" : operator.mul,\n \"**\" : pow\n}\n\nprint(action['*'](5, 5)) # 25","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"10. Обработка ошибок"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В Python, как и в языках Java и C++, имеется способ обработки исключений с помощью блоков try, except и finally:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# Пример 1\ntry: \n a = int(input(\"Enter a:\")) \n b = int(input(\"Enter b:\")) \n c = a/b \n print(c)\nexcept: \n print(\"Can't divide with zero\") \n \n# Пример 2\ntry: \n #если файл не существует, будет выброшено исключение. \n fileptr = open(\"file.txt\",\"r\") \nexcept IOError: \n print(\"File not found\") \nelse: \n print(\"The file opened successfully\") \n fileptr.close()\n\n# Пример 3\ntry:\n fptr = open(\"data.txt\",'r')\n try:\n fptr.write(\"Hello World!\")\n finally:\n fptr.close()\n print(\"File Closed\")\nexcept:\n print(\"Error\")","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"11. Проверка наличия анаграммы"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Анаграмма — это слово, которое образуется путем перестановки букв другого слова, причем каждая буква используется только один раз.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def check_anagram(first_word, second_word):\n return sorted(first_word) == sorted(second_word)\n \nprint(check_anagram(\"silent\", \"listen\")) # True\nprint(check_anagram(\"ginger\", \"danger\")) # False","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"12. Проверка наличия подстрок в строке списка"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"addresses = [\n \"12/45 Elm street\",\n '34/56 Clark street',\n '56,77 maple street',\n '17/45 Elm street'\n\n]\nstreet = 'Elm street'\nfor i in addresses:\n if street in i:\n print(i)\n\n'''output\n12/45 Elm street\n17/45 Elm street\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"13. Форматирование строки"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Самые важные части кода — это входные данные, логика и выходные данные. Все три части требуют некоторого форматирования при написании кода для получения лучших и более удобных для восприятия человеком выходных данных. В Python имеется целый ряд методов форматирования строки.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"name = \"Abhay\"\nage = 21\n\n## МЕТОД 1: конкатенация\nprint(\"My name is \"+name+\", and I am \"+str(age)+ \" years old.\")\n\n## МЕТОД 2: форматированные строки (F-строки в Python 3+)\nprint(f\"My name is {name}, and I am {age} years old\")\n\n## МЕТОД 3: join\nprint(''.join([\"My name is \", name, \", and I am \", str(age), \" years old\"]))\n\n## МЕТОД 4: оператор модуля\nprint(\"My name is %s, and I am %d years old.\" % (name, age))\n\n## МЕТОД 5: format (Python 2 и 3)\nprint(\"My name is {}, and I am {} years old\".format(name, age))Списки","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"14. Сортировка списка строк"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот фрагмент кода пригодится, например при упорядочении всех имен студентов в списке:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"list1 = [\"Karl\",\"Larry\",\"Ana\",\"Zack\"]\n\n# Метод 1: sort()\nlist1.sort()\n\n# Метод 2: sorted()\nsorted_list = sorted(list1)\n\n# Метод 3: метод перебора \nsize = len(list1)\nfor i in range(size): \n for j in range(size): \n if list1[i] < list1[j]: \n temp = list1[i] \n list1[i] = list1[j] \n list1[j] = temp\nprint(list1)","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"15. Генератор списков с If и Else"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

А этот фрагмент кода будет очень полезен при проведении фильтрации структуры данных на основе некоторых условий:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"## List Comprehension with if and else\n[\"Divided by 5\" if i%5=0 else i for i in range(1,20)]\n\n### FizzBuzz Implementation Threw the same\n['FizzBuzz' if i%3=0 and i%5=0 else 'Fizz' if i%3=0 else 'Buzz' if i%5=0 else i for i in range(1,20)]\n\n'''output\n[1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz', 11, 'Fizz', 13, 14, 'FizzBuzz', 16, 17, 'Fizz', 19]\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"16. Сложение элементов двух списков"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Допустим, у вас есть два списка, которые надо объединить в один, суммировав их элементы. Это пригодится, например в таком сценарии:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"maths = [59, 64, 75, 86]\nphysics = [78, 98, 56, 56]\n\n# Метод перебора\nlist1 = [\n maths[0]+physics[0],\n maths[1]+physics[1],\n maths[2]+physics[2],\n maths[3]+physics[3]\n]\n\n# Генератор списков\nlist1 = [x + y for x,y in zip(maths,physics)]\n\n# Использование методов map\nimport operator \nall_devices = list(map(operator.add, maths, physics))\n\n# Использование библиотеки Numpy\nimport numpy as np\nlist1 = np.add(maths,physics)\n\n'''Output\n[137 162 131 142]\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"17. Самые часто встречающиеся в списке"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот метод возвращает элементы, появляющиеся в списке наиболее часто:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def most_frequent(nums):\n return max(set(nums), key = nums.count)","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"18. Возведение в квадрат всех чисел в заданном диапазоне"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В этом фрагменте для нахождения квадрата каждого целого числа в заданном диапазоне прибегнем к помощи встроенной функции itertools:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# МЕТОД 1\nfrom itertools import repeat\n\nn = 5\nsquares = list(map(pow, range(1, n+1), repeat(2)))\nprint(squares)\n\n# МЕТОД 2\nn = 6\nsquares = [i**2 for i in range(1,n+1)]\nprint(squares)\n\n\"\"\"Output\n [1, 4, 9, 16, 25]\n\"\"\"","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"19. Поиск дублей"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Эти фрагменты кода позволяют проверить, есть ли в списке повторяющиеся значения:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def has_duplicates(lst):\n return len(lst) ≠ len(set(lst))\n\nx = [1,2,2,4,3,5]\ny = [1,2,3,4,5]\nhas_duplicates(x) # True\nhas_duplicates(y) # False","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"20. Преобразование двух списков в словарь"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Здесь используются следующие методы:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"list1 = ['karl','lary','keera']\nlist2 = [28934,28935,28936]\n\n# Метод 1: zip()\ndictt0 = dict(zip(list1,list2))\n\n# Метод 2: генераторы словарей\ndictt1 = {key:value for key,value in zip(list1,list2)}\n\n# Метод 3: использование цикла For (не рекомендуется)\ntuples = zip(list1, list2) \ndictt2 = {} \nfor key, value in tuples: \n if key in dictt2: \n pass\n else: \n dictt2[key] = value\nprint(dictt0, dictt1, dictt2, sep = \"\\n\")","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"21. Сортировка списка словарей"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Бывают задачи, в которых требуется упорядочить список словарей, чтобы получить с помощью ключа отсортированный в определенном порядке список:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"dict1 = [\n {\"Name\":\"Karl\",\n \"Age\":25},\n {\"Name\":\"Lary\",\n \"Age\":39},\n {\"Name\":\"Nina\",\n \"Age\":35}\n]\n\n## Использование sort()\ndict1.sort(key=lambda item: item.get(\"Age\"))\n\n# Сортировка списка с помощью itemgetter\nfrom operator import itemgetter\nf = itemgetter('Name')\ndict1.sort(key=f)\n\n# Функция sorted для итерируемых элементов\ndict1 = sorted(dict1, key=lambda item: item.get(\"Age\"))\n\n'''Output\n[{'Age': 25, 'Name': 'Karl'},\n {'Age': 35, 'Name': 'Nina'},\n {'Age': 39, 'Name': 'Lary'}]\n'''","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"22. Объединение двух словарей"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

А этот фрагмент кода пригодится при работе с базами данных и файлами JSON, когда нужно объединить данные из разных файлов или таблиц в общий файл. Объединение двух словарей таит в себе ряд опасностей, например возможность появления повторяющихся ключей. К счастью, у нас есть решения и для этого:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"basic_information = {\"name\":['karl','Lary'],\"mobile\":[\"0134567894\",\"0123456789\"]}\nacademic_information = {\"grade\":[\"A\",\"B\"]}\ndetails = dict() ## Объединяет словари\n\n## Метод генераторов словарей\ndetails = {key: value for data in (basic_information, academic_information) for key,value in data.items()}\nprint(details)\n\n## Распаковывание словарей\ndetails = {**basic_information ,**academic_information}\nprint(details)\n\n## Метод копирования и обновления\ndetails = basic_information.copy()\ndetails.update(academic_information)\nprint(details)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В конце статьи хочу сказать, что еще больше полезной и нужной информации вы найдете в моём Telegram-канале. Подпишитесь, мне будет очень приятно.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Оригинал на английском: Abhay Parashar: 22 Python Code Snippets for Everyday Problems

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":1,"favorites":21,"reposts":0,"views":339,"hits":7247,"reads":null,"online":0},"dateFavorite":0,"hitsCount":7247,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/dev/479576-22-snippeta-na-python-dlya-povsednevnyh-zadach","author":{"id":552457,"name":"Daniil Lebedev","nickname":null,"description":"Back-end разработчик Веду два телеграм-канала: 1) Личный - https://t.me/mavericket 2) О программировании (статьи/книги) - https://t.me/metsolution","uri":"","avatar":{"type":"image","data":{"uuid":"de24f047-2803-52bf-b6f9-e6640a6d4861","width":4726,"height":3151,"size":420619,"type":"jpg","color":"a2a1a1","hash":"1c17373333230313","external_service":[]}},"cover":null,"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4877027,"userId":552457,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4877027"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1252940,"userId":552457,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1252940"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":26232,"userId":552457,"count":0,"shareImage":"https://api.vc.ru/achievements/share/26232"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":235819,"name":"Разработка","description":"Сообщество разработчиков: публикации о личном опыте, выдающиеся приёмы при решении рутинных задач, полезные материалы для профессионального роста.","uri":"/dev","avatar":{"type":"image","data":{"uuid":"fef5b5fb-e488-5b7f-8445-e3a26a910b44","width":1200,"height":1200,"size":7757,"type":"png","color":"343434","hash":"04042b2b1c1000","external_service":[]}},"cover":{"type":"image","data":{"uuid":"2a214cc5-35cc-58ca-bc07-fc1c892d2101","width":960,"height":280,"size":177,"type":"png","color":"343434","hash":"","external_service":[]}},"lastModificationDate":1642411346,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"dev","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[{"id":1,"count":6}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":327485,"customUri":null,"subsiteId":447345,"title":"Играем в эпидемиологов","date":1638945540,"dateModified":1638945540,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Будем наблюдать за группой мышей, в которой доля привитых (вакциной против некого патогена) равна vасс_ratio. Допустим нам известно, что патоген распространяется по воздуху не дальше, чем на расстояние distination. И на расстоянии менее или равным distination заражает некоторый процент среди привитых мышек и некоторый процент среди непривитых. Особь продолжает выделять патоген еще некоторое время после заражения – ill_durat, далее она становится безопасной для окружающих.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Импортирую необходимые библиотеки:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import numpy as np\nimport random\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport matplotlib.animation","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Задаю начальные условия:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"population = 100# Количество точек\nvacc_ratio = 0.9 # Доля вакцинированных\nvacc_infect_prb = 0.05 # Вероятность заражения вакцинированной точки\nnot_vacc_infect_prb = 0.4 # Вероятность заражения невакцинированной точки\ndistination = 0.08 # Расстояние для заражения\nill_durat = 12 # Длительность выделения патогена точкой\ntimer = 100 # Время наблюдений","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Одна мышка — это точка на плоскости, имеющая координаты (x, y). Привитых мышек обозначим зеленым цветом, а непривитых - синим. Мышки время от времени перемещаются (меняют свои координаты). Первую точку мы заразим искусственно, и отметим красным цветом.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Создаем Dataframe наблюдений, содержащий идентификатор точки, временную метку, цвет, координаты точки и время заболевания.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# Задаем координаты, начальное время, ID, цвет (состояние), время заражения\nX = pd.Series(np.random.rand(population))\nY= pd.Series(np.random.rand(population))\ntime = [0.0]*population\nid = pd.Series(range(0,population))\ncolor = ['green']*int(population*vacc_ratio)+['blue']*(population-int(population*vacc_ratio))\n\n# Первое заражение\nfirst_infect_id = random.randint(0,population-1)\ncolor[first_infect_id] = 'red'\ninfect_date = pd.Series([-1]*population)\ninfect_date[first_infect_id] = 0\n\n# Сохраняем в DateFrame\nobserv_df = pd.DataFrame({ 'ID': id, 'time':time, 'color': color, 'X': X, 'Y':Y,'infect_date':infect_date })","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"for i in range(1, timer):\n # Cоединяем таблицу наблюдений саму с собой, чтобы получить все пары точек (сочетание без повторений).\n cross_observ = observ_df.merge(observ_df, how='cross',suffixes=('_1', '_2'))\n cross_observ = cross_observ[cross_observ['ID_1'] < cross_observ['ID_2']]\n\n # Считаем евклидово расстояние между каждой парой точек и записываем его в столбец destinaton\n cross_observ['destinaton']=cross_observ.apply(lambda obs: np.sqrt((obs['X_1'] - obs['X_2']) ** 2 + (obs['Y_1'] - obs['Y_2']) ** 2), axis=1)\n\n # Рассчет новых состояний точек\n a = cross_observ.apply(func=change_color, axis=1)\n\n # Обновляем DataFrame observ df в связи с изменением состояний некоторых точек после применения функции change_color\n df_new_1 = pd.DataFrame()\n df_new_2 = pd.DataFrame()\n df_new = pd.DataFrame()\n df_new_1[['ID','color','infect_date']] = a[a['color_1']=='red'][['ID_1', 'color_1','infect_date_1']].drop_duplicates()\n df_new_2[['ID','color','infect_date']] = a[a['color_2'] == 'red'][['ID_2','color_2','infect_date_2']].drop_duplicates()\n df_new[['ID', 'color','infect_date']] = pd.concat([df_new_1, df_new_2],ignore_index=True).drop_duplicates()\n observ_df = observ_df.merge(df_new, how='left', on=['ID'], suffixes=('', '_new'))\n observ_df['color'] = np.where(pd.notnull(observ_df['color_new']), observ_df['color_new'], observ_df['color'])\n observ_df['infect_date'] = np.where(pd.notnull(observ_df['infect_date_new']), observ_df['infect_date_new'], observ_df['infect_date'])\n observ_df.drop(['color_new','infect_date_new'], axis=1, inplace=True)\n\n # Добавляем observ_df в общий DataFrame\n observ_all = observ_all.append(observ_df)\n\n # Обновляем время и координаты точек\n observ_df['time'] = i\n if i%5 == 0:\n observ_df['X'] = pd.Series(np.random.rand(population))\n observ_df['Y'] = pd.Series(np.random.rand(population))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Реализация функции change_color():

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Если 2 точки находятся на расстоянии не более destination\n# и только одна из точек помечена красным(заражена),\n# и с момента ее заражения прошло не более ill_durat,\n# тогда вторая точка окрашивается красным (заражается) с вероятностью\n#vacc_infect_prb, если она была привита и с вероятностью not_vacc_infect_prb, если непривита.\ndef change_color(row):\n if row['destinaton'] <= distination and row['color_1']== 'red' and (row['time_1'] - row['infect_date_1']) < ill_durat:\n if row['color_2']=='blue':\n row['color_2'] = np.random.choice(['blue', 'red'], p=[1 - not_vacc_infect_prb, not_vacc_infect_prb])\n if row['color_2']=='red':\n row['infect_date_2'] = row['time_1']\n if row['color_2'] == 'green':\n row['color_2'] = np.random.choice(['green', 'red'], p=[1 - vacc_infect_prb, vacc_infect_prb])\n if row['color_2'] == 'red':\n row['infect_date_2'] = row['time_1']\n\n if row['destinaton'] <= distination and row['color_2']== 'red' and (row['time_1'] - row['infect_date_2']) < ill_durat:\n if row['color_1']=='blue':\n row['color_1'] = np.random.choice(['blue', 'red'], p=[1 - not_vacc_infect_prb, not_vacc_infect_prb])\n if row['color_1'] == 'red':\n row['infect_date_1'] = row['time_1']\n if row['color_1'] == 'green':\n row['color_1'] = np.random.choice(['green', 'red'], p=[1 - vacc_infect_prb, vacc_infect_prb])\n if row['color_1'] == 'red':\n row['infect_date_1'] = row['time_1']\n\n return row","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Осталось создать анимированные графики и сохранить их в формате gif.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"fig, ax = plt.subplots()\nx, y = [], []\nsc = ax.scatter(x, y)\nsc.set_cmap(\"brg\")\nplt.xlim(0, 1)\nplt.ylim(0, 1)\nttl = ax.text(-0.05, 1.05, \"\", transform=ax.transAxes, fontsize=14)\n\ndef animate(i):\n ill_prc = int(observ_all[(observ_all['time']==i) & (observ_all['color']==0.5)]['ID'].count()/population *100)\n ttl.set_text('vacc_ratio = {}, time = {}, ill_prc = {}'.format(vacc_ratio, i,ill_prc))\n x = list(observ_all[observ_all['time']==i]['X'])\n y = list(observ_all[observ_all['time'] == i]['Y'])\n colors = list(observ_all[observ_all['time'] == i]['color'])\n sc.set_offsets(np.c_[x,y])\n sc.set_array(colors)\n\nani = matplotlib.animation.FuncAnimation(fig, animate,frames=timer-1, interval=1, repeat=True)\n\n# Сохраняем анимацию в виде gif файла:\nmy_writer=matplotlib.animation.PillowWriter(fps = 8)\nani.save('vacc_ratio='+str(vacc_ratio)+'.gif',writer=my_writer)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Теперь, изменяя параметры vacc_ratio, vacc_infect_prb, not_vacc_infect_prb, distination, ill_durat можно наблюдать за охватом и скоростью распространения инфекции. Изменяя параметр vacc_ratio c 30% до 90%, я построила следующие графики:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"e405b51b-979a-537e-90fc-a88d3234f6e5","width":1920,"height":480,"size":104454,"type":"jpg","color":"f0f6f1","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Те же результаты, но в табличном виде (в абсолютных и относительных значениях):

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"20010e51-8dec-5511-b70a-e478fd1e7f24","width":1069,"height":353,"size":94388,"type":"jpg","color":"111111","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":11,"hits":148,"reads":null,"online":0},"dateFavorite":0,"hitsCount":148,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id447345/327485-igraem-v-epidemiologov","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":286162,"customUri":null,"subsiteId":447345,"title":"Сложности при выгрузке 500 гб из базы данных и пример их решения","date":1630067353,"dateModified":1630067353,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Хочу поделиться с вами забавным случаем, который произошел со мной. Была поставлена задача — в кратчайшие сроки выгрузить 500 ГБ информации из базы данных (БД). Но на тот момент места на жестком диске катастрофически не хватало, и не было возможности оперативно очистить или добавить новый. К счастью, в наличии был защищенный файловый информационный ресурс (ФИР). Казалось бы, вот и решение — сохранять данные сразу в файл на ФИР. Но доступной скорости передачи информации на сетевой ресурс оказалось недостаточно, чтобы все выгрузить в полном объеме. У нас так: если SQL-запрос не отработал за сутки, он выпадает в ошибку из-за обрыва сессии. В итоге комичность в том, что на жесткий диск можно выгрузить быстро, но мало, а на ФИР много, но долго.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

При учете всего вышеописанного, требовался инструмент, который мог выгружать из базы на жесткий диск определенный объем данных и перекладывать на ФИР без участия человека. Под такое описание подходил Python с библиотеками «cx_Oracle» и «shutil». Решение разделено на два скрипта:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Первый «Выгрузка данных»

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Скрипт настроен на порционную выгрузку в отдельные файлы. Фильтрация в SQL-запросе подобрана с учетом оставшегося места на жестком диске. Чтобы исключить переполнение, учтена скорость скрипта на перенос файлов.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

За основу взят пример кода из предыдущей статьи. Ссылки на разные сайты:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

ссылка 1

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

ссылка 2

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Без изменений:

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Используемые библиотеки","Проверка версии","Дескриптор соединения с БД","Указание логин/пароль","Функция, в которой создается экземпляр класса connect","Подключение к серверу"],"type":"OL"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Внесенные изменения:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Функция, в которой создается курсор, выполняется запрос в БД и сохраняются данные в файл:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def dfFromOracle(connection, sql, file):\n us=0\n outDF=pd.DataFrame() \n success = 'False'\n with connection.cursor() as cursor1:\n cursor1.execute(sql) #Отправка SQL-запроса в базу данных\n trn=10\n while success == 'False' and trn>0:\n try:\n #Сохраняем колонки\n outheader=[desc[0] for desc in cursor1.description]\n header = ';'.join(_header)+'\\n'\n myfile=open(file, 'w',encoding='UTF-8')\n myfile.writelines(header)\n myfile.close()\n #Цикл для выгрузки определенного количества строк\n While try:\n frame = cursor1.fetchmany(1000000) # Возвращаем определенное количество строк из результата запроса\n if not frame: #Проверяем наличие данных в массиве \n break\n outDF = pd.DataFrame(frame) #Формируем ДФ из массива\n outDF.to_csv(file, sep=';',encoding='UTF-8',mode='a',header=None,index=False) #Сохраняем сформированный ДФ\n success = 'True'\n us = 1\n frame = '' #Очищаем массив\n except:\n trn=trn-1\n print('Error')\n time.sleep(60)\n return us","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Создаем массивы:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"collection_id = ('31184','25597','***','68823') #Уникальное значение присвоенное массиву в БД\nyear = ('2020','2019') #Год\nmonth = ('12','11','10','09','08','07','06','05','04','03','02','01') #Месяц\nnpp = 0 #Номер по порядку выгруженных файлов для корректной сортировки\nnew_file = '_op_.csv' #Неизменная часть имени файла","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Цикл для формирования уникальных имен файлов, создания SQL-запросов и вызова функций:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"print(str(time.ctime()) + ' : Старт') #Время старта\nfor row_id in collection_id:\n for row_y in year:\n for row_m in month:\n npp += 1 #Счетчик выгруженных файлов\n #Собираем имя файла\n file = str(npp).rjust(5,'0') +'_'+ row_y +'_'+ row_m +'_'+ row_id + new_file\n #Собираем SQL-запрос\n sql = (\"\"\"select * from tabl where\n to_char(c_date, 'YYYY') = '\"\"\" + row_y + \"\"\"'\n and to_char(c_date, 'MM') = '\"\"\" + row_m1 + \"\"\"'\n and coll_id = '\"\"\" + row_tb + \"\"\"'\"\"\")\n #Вызов функций\n with getConn(odsLogin, odsConnectStr) as con1:\n us = dfFromOracle(con1, sql, file)\nprint(str(time.ctime()) + ' : Финиш') #Время финиша\nchek = open('Выгрузка_завершена_V.txt', 'a',encoding='UTF-8') #Создаем файл\nchek.close()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Второй «Перемещение выгруженных файлов»

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Скрипт обеспечит постоянный контроль количества сохраняемых файлов. При превышении установленного лимита определит ранее выгруженные, скопирует в указанную папку и удалит из старой директории. Завершит свою работу после того как скрипт на выгрузку создаст файл «Выгрузка_завершена_V.txt».

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Используем библиотеки:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import shutil\nimport os\nimport time","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Создаем массивы:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"path_file = r'\\Users\\ *** \\ '[:-1] #Путь к выгруженным файлам на жестком диске\nsave_file = r'\\\\ *** \\ '[:-1] #Путь на ФИР к папке переноса\nchek = '' #Для проверки завершения цикла","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Цикл мониторинга выгруженных файлов:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"print(str(time.ctime()) + ' : Старт') #Время старта\n#Цикл поиска файлов\nwhile chek == '':\n count = 1\n #Собираем в папке имена выгруженных файлов, которые необходимо перенести\n list_file = []\n for file in os.listdir(path_file):\n if file.endswith(('op_.csv')): #Файл оканчивается на «op_.csv»\n list_file.append(file)\n list_file.sort() \n #Если файлов больше двух, переходим к циклу переноса двух файлов\n if len(list_file) > 2:\n for row_file in list_file:\n if count < 3:\n #Копируем файл, затем его удаляем из старой директории\n filename = row_file #Имя файла\n filedir = path_file #Его текущая директория\n move_to = save_file #Куда надо перенести\n shutil.move(os.path.join(filedir, filename), os.path.join(move_to, filename))\n count += 1\n list_file = [] \n #Проверяем завершилась выгрузка из БД, переносим оставшиеся файлы и завершаем работу скрипта\n chek_file = []\n for file in os.listdir(path_file):\n if file.endswith(('_V.txt')): #Файл оканчивается на «_V.txt»\n chek_file.append(file)\n if len(chek_file) > 0:\n chek = 'V'\n time.sleep(15)\n for row_file in list_file:\n #Копирует файл, затем его удаляет из старой директории\n filename = row_file \n filedir = path_file \n move_to = save_file \n shutil.move(os.path.join(filedir, filename), os.path.join(move_to, filename))\n count += 1\nprint(str(time.ctime()) + ' : Финиш') #Время финиша","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В итоге, за два неполных дня автоматически выгружено и перенесено более 500 ГБ, а это 168 файлов в среднем по 3 ГБ (без учета одного обрыва сессии). Применен метод fetchmany() с возможностью установки приемлемого расхода памяти для комфортной работы с другими задачам без зависания системы и ошибок «memory error».

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":2,"favorites":3,"reposts":0,"views":11,"hits":446,"reads":null,"online":0},"dateFavorite":0,"hitsCount":446,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/dev/286162-slozhnosti-pri-vygruzke-500-gb-iz-bazy-dannyh-i-primer-ih-resheniya","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":235819,"name":"Разработка","description":"Сообщество разработчиков: публикации о личном опыте, выдающиеся приёмы при решении рутинных задач, полезные материалы для профессионального роста.","uri":"/dev","avatar":{"type":"image","data":{"uuid":"fef5b5fb-e488-5b7f-8445-e3a26a910b44","width":1200,"height":1200,"size":7757,"type":"png","color":"343434","hash":"04042b2b1c1000","external_service":[]}},"cover":{"type":"image","data":{"uuid":"2a214cc5-35cc-58ca-bc07-fc1c892d2101","width":960,"height":280,"size":177,"type":"png","color":"343434","hash":"","external_service":[]}},"lastModificationDate":1642411346,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"dev","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":274129,"customUri":null,"subsiteId":447345,"title":"Выгрузка данных из несвязанных таблиц в отдельные файлы с помощью python","date":1627375636,"dateModified":1627375636,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

В предыдущей статье мы рассмотрели, как с помощью Python можно создать и запустить SQL запрос с множеством условий для выгрузки информации из баз данных (БД) Oracle в один файл частями.

"}},{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Не секрет, что прежде чем писать SQL запросы к таблицам БД для формирования выгрузок, необходимо проанализировать, какая информация в них содержится. Особенно это актуально в случае, если описание к БД отсутствует (что в нашей работе встречается нередко). Для этого, например, можно выгрузить небольшое количество записей из нужных таблиц в отдельные файлы excel. При этом зачастую таблиц, которые необходимо изучить, довольно много. Чтобы не выгружать информацию из каждой вручную, можно воспользоваться Python. Ниже мы рассмотрим, как это сделать на примере БД Oracle.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для начала создадим txt файл с названием «dt_in.txt», в котором укажем таблицы для выгрузки данных в следующем виде:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Название_базы_данных_1.Название_таблицы_1;

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Название_базы_данных_1.Название_таблицы_2;

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Название_базы_данных_2.Название_таблицы_1;

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Название_базы_данных_3.Название_таблицы_1

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Обратите внимание, в файле не должно быть пробелов и пустых строк, в том числе после названия последней таблицы в списке. Указать можно таблицы разных БД.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Далее запускаем скрипт на Python (импорт библиотек, подключение к Oracle, описание функций для взаимодействия с Oracle и выполнения запроса можно найти в указанной выше статье).

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Имя файла со строками вида base_name.table_name;\nfn = 'dt_in.txt'\n\n#Чтение входного файла с именами баз данных и таблиц\nf = open(fn,'r',encoding='UTF-8') \n\n#Ключ для записи заголовков, если h=0, то происходит первый проход по циклу - записываем в файл заголовки, \n#если h=1 - не первый проход по циклу, заголовки записаны\nh=0\nl = 0 \n#Счетчик строк для запуска основного SQL-запроса\nfor row in f:\n \n dt = row.split(\";\")\n \n #Имя файла в который выгружаем\n new_file = str(dt[0].replace('\\n',''))\n l += 1\n \n #SQL-запрос (количество выгружаемых строк можно менять)\n sql = \"\"\"SELECT * FROM \"\"\" + str(dt[0].replace('\\n','')) + \"\"\" where rownum<400\"\"\"\n \n #Проверка на наличие записей в таблице. Если записей в таблице нет, файл с ее именем не создается, \n #дальнейшая ее выгрузка не производится\n count= \"\"\"SELECT count(*) FROM \"\"\" + str(dt[0].replace('\\n','')) + \"\"\" where rownum<3\"\"\"\n with getOdsConn(odsLogin,odsConnectStr) as con1:\n _header,result,us = dfFromOracle(con1, count)\n if result.values[0,0]==0:\n print(\"Таблица \" + new_file + \" пустая\")\n continue\n \n #Выполнение основного SQL-запроса\n with getOdsConn(odsLogin,odsConnectStr) as con1:\n _header,result,us = dfFromOracle(con1, sql)\n \n #Запись заголовков в выходной файл\n if h==0:\n header = ';'.join(_header)+'\\n'\n myfile=open(new_file + '.csv', 'w',encoding='UTF-8')\n myfile.writelines(header)\n myfile.close()\n h=1\n \n #Запись выгруженных строк таблиц в файлы \n result.to_csv(new_file + '.csv', sep=';',encoding='UTF-8',mode='a',header=None)\n print('Выгружено ' + str(len(result)) + ' из ' + str(new_file) )\n \nf.close()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В результате, данные из таблиц автоматически выгружаются в отдельные файлы excel, названия которых содержат наименование БД и таблицы. Если таблица пустая – файл не формируется. В Python выводится сообщение о том, сколько записей выгружено из каждой таблицы, или, что она пустая.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Таким образом, с помощью Python можно:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

— уменьшить трудозатраты на выгрузку информации для анализа из несвязанных таблиц БД в отдельные файлы;

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

— выгружать данные для анализа сразу из нескольких БД.

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":1,"reposts":0,"views":12,"hits":2223,"reads":null,"online":0},"dateFavorite":0,"hitsCount":2223,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id447345/274129-vygruzka-dannyh-iz-nesvyazannyh-tablic-v-otdelnye-faily-s-pomoshyu-python","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":257312,"customUri":null,"subsiteId":447345,"title":"Как извлечь изображения из PDF c помощью Python, сохраняя их качество?","date":1623348506,"dateModified":1623348506,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Есть несколько способов извлечь изображения из файла PDF. Самый простой способ – просто сделать снимок экрана с изображением, присутствующим на любой странице PDF-файла, и обрезать изображение в соответствии с вашими требованиями. Этот способ выглядит очень простым, но что, если в файл PDF содержит 100 или 1000 изображений, и вы хотите, чтобы все они были в отдельной папке. Тогда этот подход будет утомительным и трудоемким и нам нужно автоматизировать этот процесс, а язык программирования Python сделает это за вас.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Если вы думаете, что можете использовать любой онлайн-инструмент, то вы ставите под угрозу безопасность своих данных. Почему вам нужно полагаться на любую онлайн-платформу, если вы можете выполнить ту же задачу, запустив простой скрипт Python на своем компьютере?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Установка необходимых библиотек

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В этой статье мы будем использовать библиотеку Python PyMuPDF (также известную как «fitz»), которая представляет собой легкую программу просмотра PDF и XPS. Эта библиотека может получить доступ к файлам в форматах: PDF, XPS, комиксов и художественных книг. И она известна своей высочайшей производительностью и высоким качеством рендеринга.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вы можете установить этот пакет с помощью следующей команды в терминале (пользователи Linux) или командной строке (пользователи Windows).

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"$pip3 install PyMuPDF","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Написание скрипта Python для извлечения всех изображений в файл pdf

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Мы напишем скрипт для очистки изображений из файла PDF. Итак, приступим, для этого нам понадобится интерпретатор Python и idle.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#импорт необходимых библиотек\nimport fitz\n#открытия файла\nfile_path = input(\"Введите путь к PDF файлу\")\npdf_file = fitz.open(file_path)\n# Чтение места, где сохранить файл\nlocation = input(\"Enter the location to save: \")\n#поиск количества страниц в pdf\nnumber_of_pages = len(pdf_file)\n#итерация по каждой странице в pdf\nfor current_page_index in range(number_of_pages):\n # итерация по каждому изображению на каждой странице PDF\n for img_index,img in enumerate(pdf_file.getPageImageList(current_page_index)):\n xref = img[0]\n image = fitz.Pixmap(pdf_file, xref)\n # если это чёрно-белое или цветное изображение\n if image.n < 5: \n image.writePNG(\"{}/image{}-{}.png\".format(location,current_page_index, img_index))\n #если это CMYK: конвертируем в RGB\n else: \n new_image = fitz.Pixmap(fitz.csRGB, image)\n new_image.writePNG(\"{}/image{}-{}.png\".foramt(location,current_page_index, img_index))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Примечание:

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Пользователю необходимо ввести путь к файлу pdf: укажите абсолютный путь, по которому находится файл pdf. Пользователь должен указать, где изображения должны быть сохранены.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

После того, как мы запустим скрипт, используя образец PDF, который содержит изображения внутри текста мы можем получить все изображения с этого документа.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Давайте запустим этот скрипт, используя образец PDF представленный ниже:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"a98f3926-5be6-5e55-81ae-3c5fd14d7328","width":198,"height":257,"size":29972,"type":"png","color":"d1cdbe","hash":"","external_service":[]}}}]}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"72e56b70-fc40-5d6b-861b-bec7b6026803","width":194,"height":255,"size":98802,"type":"png","color":"c6c3bd","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Образец PDF с изображениями

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Когда мы запускаем скрипт Python для этого PDF-файла, мы получим все 6 изображений из PDF-файла в пользовательскую папку.

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"065fab5c-a3c7-57f3-b658-fae4a3041b7c","width":360,"height":55,"size":5204,"type":"png","color":"3b3b3b","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"768449b5-660e-520b-b7d3-880912e7538b","width":198,"height":191,"size":7708,"type":"png","color":"3c3c3c","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Такой метод извлечения изображений является эффективным, если нам необходимо обработать большой объем данных формата PDF.

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":3,"favorites":25,"reposts":0,"views":14,"hits":3927,"reads":null,"online":0},"dateFavorite":0,"hitsCount":3927,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/dev/257312-kak-izvlech-izobrazheniya-iz-pdf-c-pomoshyu-python-sohranyaya-ih-kachestvo","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":235819,"name":"Разработка","description":"Сообщество разработчиков: публикации о личном опыте, выдающиеся приёмы при решении рутинных задач, полезные материалы для профессионального роста.","uri":"/dev","avatar":{"type":"image","data":{"uuid":"fef5b5fb-e488-5b7f-8445-e3a26a910b44","width":1200,"height":1200,"size":7757,"type":"png","color":"343434","hash":"04042b2b1c1000","external_service":[]}},"cover":{"type":"image","data":{"uuid":"2a214cc5-35cc-58ca-bc07-fc1c892d2101","width":960,"height":280,"size":177,"type":"png","color":"343434","hash":"","external_service":[]}},"lastModificationDate":1642411346,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"dev","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[{"id":1,"count":5}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":219528,"customUri":null,"subsiteId":447345,"title":"Выгружаем из базы данных с помощью Python","date":1615801506,"dateModified":1615801506,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Задача на выгрузку данных из одной таблицы, с одним условием для фильтра решается посредством создания простого SQL-запроса. Но она легко становится трудоемкой в исполнении, если в фильтрации применить множество условий. Давайте представим, что необходимо выгрузить данные из одной таблицы по фильтру, где первые две цифры ИНН начинаются на «66». SQL-запрос будет выглядеть так:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"select * from tabl where inn like '66%'","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вроде ничего сложного, но, если изменить условие фильтрации на поиск по 2 млн уникальных ИНН, SQL-запрос будет выглядеть так:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"select * from tabl where inn = '66******01'\n or inn = '66******02'\n or inn = '66******03'\n or inn = '66******04'\n or inn = '66******05'\n or inn = '66******06'\n or inn = '66******07'\n ...\n or inn = '66******nn'","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Исходя из своего опыта, такой запрос запустить не получится из-за ограничения СУБД на количество условий в 10 тыс. значений (Ограничения СУБД могут быть разные). В нашем случае потребуется создать 200 запросов и запустить их по отдельности. В итоге мы получим 200 файлов с данными, которые необходимо собрать в один. Сложно представить сколько потребуется на это времени и сил, но с помощью Python и библиотеки «cx_Oracle» задача решается легко.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Используем библиотеки:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import cx_Oracle\nimport pandas as pd\nimport time","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Проверяем версию (должна быть > 3.0):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"cx_Oracle.__version__","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Прописываем дескриптор соединения Oracle:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"ConnectStr = \"\"\"(DESCRIPTION=(ADDRESS=(PROTOCOL=...)(Host=...)\n (Port= ... ))(CONNECT_DATA=(SERVICE_NAME= ... )))\"\"\"","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Прописываем логин и пароль (при доменной аутентификации оставляем '/'):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Login = 'IVANOV/PASSWORD'","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Функция, в которой создается экземпляр класса connect, он обеспечит взаимодействие с сервером Oracle:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def getConn(Login, ConnectStr):\n conn=None\n nn=0\n while conn==None:\n try:\n conn=cx_Oracle.connect(Login + '@' + ConnectStr)\n except cx_Oracle.DatabaseError as e:\n ers,=e.args\n nn=nn+1\n print (nn,end='\\r')\n if ers.code!=2391:\n print ('Ошибка Oracle ', ers.code)\n break\n time.sleep(5) \n return conn","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Функция, в которой создается курсор и выполняется запрос:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def dfFromOracle(connection, sql):\n us=0\n outDF=pd.DataFrame() \n success = 'False'\n with connection.cursor() as cursor1:\n cursor1.execute(sql)\n trn=10 \n while success == 'False' and trn>0:\n try:\n outheader=[desc[0] for desc in cursor1.description]\n #При вызове \"cursor1.fetchall()\" возвращается список записей, каждая из которых \n #является кортежем (неизменяемым списком) полей разного типа\n outDF=pd.DataFrame(cursor1.fetchall())\n success = 'True'\n print('Результат получен из базы')\n us = 1\n except:\n trn=trn-1\n print('Error')\n time.sleep(60)\n return outheader, outDF, us","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Подключаемся к серверу:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"getConn(Login, ConnectStr)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Файл, в котором одна колонка со всеми значениями ИНН:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"f = open('inn_in_.txt','r',encoding='UTF-8')","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Присваиваем имя файла, в который выгрузится результат:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"new_file = 'new_file.csv'","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

r='' - для собора строк в «with», которые подставим в SQL-запрос,
h = 0 - проверки наличия заголовков,
l = 0 — счетчик строк для запуска SQL-запроса,
ll = 0 — счетчик строк для проверки на остаток,
cnt = 10000 — количество строк для запуска SQL-запроса.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Цикл для создания и запуска SQL-запросов:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"for row in f:\n l += 1\n if r == '': #Формируем \"where\" - список ИНН для фильтрации\n r = r + 'inn = \\'' + row.replace('\\n','') + '\\''\n else:\n r = r + 'or inn = \\'' + row.replace('\\n','') + '\\''\n if l % cnt == 0: #Проверка на 10 000 строк\n ll += l\n sql = ( 'select * from tabl where' + r ) #Сформированный SQL-запрос","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Отправляем SQL-запрос и обрабатываем полученную выгрузку:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"with getConn(Login,ConnectStr) as con1:\n if h==0:\n _header,result,us = dfFromOracle(con1, sql)\n header = ';'.join(_header)+'\\n'\n myfile=open(new_file, 'w',encoding='UTF-8')\n myfile.writelines(header)\n myfile.close()\n h=1\n result.to_csv(new_file, sep=';',encoding='UTF-8',mode='a',header=None,index=False)\n r=''\n print('Выгружено: '+str(l) )\n if l != ll: #Если последний список менее 10 000 строк\n sql = ( 'select * from tabl where' + r ) #Сформированный SQL-запрос\n with getConn(Login,ConnectStr) as con1:\n _header,result,us = dfFromOracle(con1, sql)\n if h==0:\n header = ';'.join(_header)+'\\n'\n myfile=open(new_file, 'w',encoding='UTF-8')\n myfile.writelines(header)\n myfile.close()\n h=1\n result.to_csv(new_file, sep=';',encoding='UTF-8',mode='a',header=None,index=False)\n print('Выгружено: '+str(l) )\n print('Выгрузка завершена')\n f.close()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Таким образом, с помощью Python мы автоматизировали процесс создания, запуска SQL-запросов и сохранения результатов в один файл, избежали больших трудозатрат, при этом:

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["исключили ошибки, возможные при перечислении списка условий для фильтрации, что снизило вероятность потери данных;","настроили процесс создания, запуска SQL-запросов и сохранения результатов в один файл порционно, что дает возможность в случае обрыва сессии не повторять заново запросы с уже отработавшими условиями, а продолжить с условий, на которых обрыв произошел.
"],"type":"UL"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":5,"favorites":28,"reposts":0,"views":8,"hits":17185,"reads":null,"online":0},"dateFavorite":0,"hitsCount":17185,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/dev/219528-vygruzhaem-iz-bazy-dannyh-s-pomoshyu-python","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":235819,"name":"Разработка","description":"Сообщество разработчиков: публикации о личном опыте, выдающиеся приёмы при решении рутинных задач, полезные материалы для профессионального роста.","uri":"/dev","avatar":{"type":"image","data":{"uuid":"fef5b5fb-e488-5b7f-8445-e3a26a910b44","width":1200,"height":1200,"size":7757,"type":"png","color":"343434","hash":"04042b2b1c1000","external_service":[]}},"cover":{"type":"image","data":{"uuid":"2a214cc5-35cc-58ca-bc07-fc1c892d2101","width":960,"height":280,"size":177,"type":"png","color":"343434","hash":"","external_service":[]}},"lastModificationDate":1642411346,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"dev","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[{"id":1,"count":3}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":205200,"customUri":null,"subsiteId":447345,"title":"Как применить Process Mining при отсутствии логов? ​","date":1612537645,"dateModified":1612537645,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Анализировать процесс мы решили с использованием методологии Process Mining. Все действия с данными проводились с помощью Python и библиотеки Pm4py.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Когда мы попытались собрать информацию о фактической работе процесса, то столкнулись с тем, что в автоматизированных системах недостаточно данных.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Какими данными мы обладали на начальном этапе?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

1. Информация из системы электронного документооборота.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В ней отражались название документа и фактические даты ввода/валидации/завершения документа. Пример таблицы приведен ниже.

"}},{"type":"media","cover":true,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"7802250c-fffd-556b-a312-7f602654a096","width":732,"height":239,"size":46442,"type":"png","color":"fbfbfa","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

2. Информация из аналога Service Desk.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Мы точно знали, что сотрудник, после проведения документа через систему электронного документооборота, должен завести заявку в Service Desk для отправки в управление рисков.

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"a381f7d0-884d-59d2-ba20-efc7ad462ea1","width":645,"height":147,"size":30671,"type":"png","color":"edf4f3","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

3. Информация из отчетов по заявкам в Service Desk.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Направлялись аудиторам по почте.

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"4f4fbe3e-1d31-5fbd-a542-44d135e74052","width":761,"height":545,"size":17703,"type":"png","color":"fbfbfb","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import re\ndef searchSD(string):\n parser = re.search(r'\\d{2}\\S\\d{5}',string)\n try:\n return parser[0] \n except TypeError: \n return 'Not Found' \n ServiceDesk_copy = ServiceDesk.copy()\nasDrug_copy[‘Номер заявки в Service Desk'] = asDrug_copy.apply(lambda x: searchSD(x['DESCRIPTION']), axis=1)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для выделения этапов заведения и завершения документа мы разделили столбцы по датам и присвоили новое значение, затем соединили их в единый dataframe.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

С помощью PM4PY построили граф этих четырех этапов нашего процесса. Что мы увидели интересного?

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b4664845-e105-5a34-a6b4-aace8069513c","width":288,"height":421,"size":21008,"type":"png","color":"fafafb","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

С точки зрения анализа для нас было интересно: среднее время между открытием и завершением заявки – 5 минут. Это очень сильно выделялось в разрезе остальных этапов, поэтому мы зафиксировали данный факт, как гипотезу. Далее мы объясним, почему так произошло.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

На следующем этапе анализа мы столкнулись с другой проблемой. Информации из автоматизированных систем больше не было. Мы уточнили у коллег, как дальше строится процесс. В итоге выяснилось, что сотрудники управления рисков, исполняя заявку из Service Desk, выгружают себе документы и закрывают заявку. Весь дальнейший процесс общения с аудитом происходит посредством переписки в корпоративной почте. После рассмотрения представленных аудитом материалов сотрудники управления рисками присылают по почте отчет в формате xlsx на создателя заявки в Service Desk. Именно это и есть ответ на нашу гипотезу про завершение заявки в течении 5 минут.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для понимания следующих этапов процесса мы попросили наших коллег предоставить нам переписку с управлением рисков. Примеры писем приведены ниже.

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"fa7a63da-e4dd-5764-ba7e-a8a0fa4933d4","width":982,"height":405,"size":23504,"type":"png","color":"050505","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Изучив текст писем, мы подумали – что если, используя тексты писем и подходы NLP, преобразовать их в этапы процесса?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для обработки переписки мы использовали библиотеку ExtractMSG. Библиотека позволила нам получить из файлов Outlook типа. msg всю необходимую информацию.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"for files in os.listdir('./'):\n print([files])\n fileExtension = files.split('.')[-1]\n if(fileExtension == 'msg'):\n #обработка исключения, если письмо неправильно сохранено и не имеет атрибутов\n try:\n msg = extract_msg.Message(files)\n except AttributeError:\n msg = 0\n try:\n messageTo = processEmail(msg.to)\n except AttributeError:\n msg = 0\n#если письмо сохранено правильно, то обрабатываем данные в нем\nif msg!=0:\n sd=msg.subject[msg.subject.find('SD'):]\n if sd==' ' or len(sd)<=1:\n sd=msg.subject[msg.subject.find('EVE-'):]\n if len(sd)<=1:\n sd=msg.subject\n messageSubject = msg.subject\n messageDate = msg.date\n messageTo = processEmail(msg.to)\n messageBody = msg.body\n time_zona=msg.date[msg.date.find('+') : ]\n #msg_to=msg.sender.split('<',1)\n #sender=msg.sender.split('<',1)\n msd_body=msg.body.split('From:',1)\n #сохранить вложения)\n #for x in msg.attachments:\n # x.save()\n if msd_body[0]!=' \\r\\n\\r\\n \\r\\n\\r\\n':\nif msd_body[0]!='____________________________\\r\\n\\r\\nCообщение зашифровано\\r\\n\\r\\n____________________________\\r\\n\\r\\n':\n mas_mail.append([str(sd),str(msg.date),str(msg.subject),str(msd_body[0]),check_mail(msg.sender),check_mail(msg.to)])\nif(check_child(msg.body,time_zona)!='0'):\nmas_mail.append(check_child(msg.body,time_zona))\ni+=1\nmas_time.append(str(msg.date))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Далее, перепроверив данные, оказалось, что в 50% случаев сотрудники отвечали на входящее сообщение, и у нас в текст письма попадала вся переписка. На этом этапе мы написали небольшой парсер, который несмотря на наличие текста, делит письмо на отдельные элементы.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

После того, как все письма были разделены по отдельности, мы выделили из них идентификатор заявки в Service Desk. Он понадобился нам для дальнейшего объединения данных в единый Лог.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для обучающей выборки мы разметили письма на 6 классов и обучили классификатор. Сами классы представлены ниже. Точность работы алгоритма по метрике F1 – 0.92. Для нашей задачи — это хороший результат!

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"4611ed0c-7834-57a3-af2f-c44d94b307bd","width":200,"height":157,"size":6069,"type":"png","color":"eef8f9","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

На финальном этапе мы связали переписку с отчетом по приему документов (по номеру заявки из Service Desk) и вставили эти данные в лог с перепиской. Получение от управления рисков отчета по заявке является завершением исследуемого процесса.

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b1d0f8a1-d321-5eb3-b5dc-90466b7dae42","width":977,"height":166,"size":44451,"type":"png","color":"f3fbfb","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"4a91e516-1219-5638-9a31-1e0e04d3ba2f","width":1024,"height":740,"size":48833,"type":"png","color":"2de2e2","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Итак, использование методов NLP и Process mining помогло нам воссоздать полную картину процесса. По результатам анализа модели процесса «AS IS» были установлены «узкие места» и отклонения, принято решение об оптимизации процесса.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":3,"favorites":3,"reposts":0,"views":5,"hits":946,"reads":null,"online":0},"dateFavorite":0,"hitsCount":946,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/ai/205200-kak-primenit-process-mining-pri-otsutstvii-logov","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":332941,"name":"AI","description":"Нейросети, искуственный интеллект, машинное обучение","uri":"/ai","avatar":{"type":"image","data":{"uuid":"47d7652c-7ff3-5ad3-b72c-3d0aa7d14f06","width":1200,"height":1200,"size":311374,"type":"png","color":"8dd2f1","hash":"2070ecd4e4745850","external_service":[]}},"cover":{"type":"image","data":{"uuid":"d830f642-8293-f95c-8c0a-cf31c79fd3aa","width":1920,"height":384,"size":110830,"type":"gif","color":"3b3846","hash":"","external_service":[],"duration":0}},"lastModificationDate":1602860409,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"ai","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":158797,"customUri":null,"subsiteId":447345,"title":"Python: как создать простейшего голосового помощника?","date":1600244984,"dateModified":1600244984,"blocks":[{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для начала объявим необходимые нам библиотеки:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Необходимые библиотеки\nimport speech_recognition as sr\nimport os\nimport sys\nimport webbrowser\nimport pyttsx3 as p\nfrom datetime import datetime\nimport time\nimport datetime\nimport random","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Создаем лог\nchat_log = [['SESSION_ID', 'DATE', 'AUTHOR', 'TEXT', 'AUDIO_NUM']]\n#Узнаем номер сессии\ni = 1\nexit = 0\nwhile exit == 0:\n session_id = str(i)\n if session_id not in os.listdir():\n os.mkdir(session_id)\n exit = 1\n else:\n i = i + 1\n#Первое сообщение пишет bot\nauthor = 'Bot'\ntext = 'Привет! Чем я могу вам помочь?'","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В лог файл мы записываем время сообщения, автора (бот или пользователь) и собственно сам сказанный текст.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Добавляем данные к логу с помощью этой процедуры\ndef log_me(author, text, audio): \n now = datetime.datetime.now()\n i = 1\n exit = 0\n while exit == 0:\n audio_num = str(i)+'.wav'\n if audio_num not in os.listdir(session_id):\n exit = 1\n else:\n i = i + 1\n os.chdir(session_id)\n with open(audio_num , \"wb\") as file:\n file.write(audio.get_wav_data())\n chat_log.append([now.strftime(\"%Y-%m-%d %H:%M:%S\"), author, text, audio_num])","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Выводим первое сообщение за авторством бота: Привет! Чем я могу вам помочь?

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# Выводим первое сообщение на экран и записываем в лог \nprint(\"Bot: \"+ text)\nlog_me(author, text, audio)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

А с помощью такой процедуры в Jupyter Notebook мы можем озвучить через устройство воспроизведения, настроенное по умолчанию, сказанные слова:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Произношение words\ndef talk(words):\n engine.say(words)\n engine.runAndWait()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Как озвучивать текст мы рассмотрели выше, но как же мы свой голос сможем превратить в текст? Тут нам поможет распознавание речи от Google и некоторые манипуляции с микрофоном.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Настройка микрофона \ndef command():\n rec = sr.Recognizer()\n with sr.Microphone() as source:\n #Бот ожидает нашего голоса\n print('Bot: ...')\n #Небольшая задержка в записи\n rec.pause_threshold = 1\n #Удаление фонового шума с записи\n rec.adjust_for_ambient_noise(source, duration=1)\n audio = rec.listen(source)\n try:\n #Распознание теста с помощью сервиса GOOGLE\n text = rec.recognize_google(audio, language=\"ru-RU\").lower()\n #Вывод сказанного текста на экран\n print('Вы: ' + text[0].upper() + text[1:])\n log_me('User', text, audio)\n #Если не распознался тест из аудио\n except sr.UnknownValueError:\n text = 'Не понимаю. Повторите.'\n print('Bot: ' + text)\n talk(text)\n #Начинаем заново слушать\n text = command()\n log_me('Bot', text, , Null)\n return text","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Что может сделать наш помощник кроме того, чтобы нас слушать? Все ограничено нашей фантазией! Рассмотрим несколько интересный примеров.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Начнем с простого, пусть при команде открыть сайт – он откроет сайт (не ожидали?).

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Тут расписаны действия, которые будут выполнятся при наличии некоторых словосочетаний\ndef makeSomething(text):\n if 'открой сайт' in text:\n print('Bot: Открываю сайт NewTechAudit.')\n talk('Открываю сайт NewTechAudit.')\n log_me('Bot','Открываю сайт NewTechAudit.', Null)\n webbrowser.open('https://newtechaudit.ru/')","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Иногда полезно послушать свои слова, да чужими устами. Пусть бот еще умеет и повторять за нами:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Повторение фразы пользователя\n elif 'произнеси' in text or 'скажи' in text or 'повтори' in text:\n print('Bot: ' + text[10].upper() + text[11:])\n talk(text[10:])\n log_me('Bot', text[10].upper() + text[11:] , Null)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Пусть еще и собеседником будет, но начнем мы пока только со знакомства:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Ответ на вопрос\n elif 'своё имя' in text or 'как тебя зовут' in text or 'назови себя' in text:\n print('Bot: Меня зовут Bot.')\n talk('Меня зовут Bot')\n log_me('Bot', 'Меня зовут Bot', Null)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Мы также можем попросить голосового помощника назвать случайное число в выбранных нами пределах в формате: Назови случайное число от (1ое число) до (2ое число).

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Определение случайного числа\n elif 'случайное число' in text:\n ot=text.find('от')\n do=text.find('до')\n f_num=int(text[ot+3:do-1])\n l_num=int(text[do+3:])\n r=str(random.randint(f_num, l_num))\n print('Bot: ' + r)\n talk(r)\n log_me('Bot', r, Null)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того, чтобы завершить программу, достаточно только попрощаться с ботом:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Завершение программы\n elif 'пока' in text or 'до свидания' in text:\n print('Bot: До свидания!')\n talk('До свидания')\n log_me('Bot', 'Конец сессии', Null)\n os.chdir(session_id)\n log_file = open( session_id + \".txt\", \"w\")\n for row in chat_log:\n np.savetxt(log_file, row)\n log_file.close()\n sys.exit()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

А чтобы все это работало беспрерывно, мы создаем бесконечный цикл.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"#Бесконечный цикл для работы\nwhile True:\n makeSomething(command())","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Проведем тестовый диалог:

"}},{"type":"media","cover":true,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"c9fc2ef0-57a0-29d6-7c71-15fdfe14e684","width":394,"height":280,"size":19405,"type":"png","color":"042c58","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В созданной папке-сессии хранятся все файлы-аудиозаписи нашего голоса и текстовый лог-файл:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"76eb6d1c-833f-a297-cb9c-b8de50a8ae21","width":536,"height":195,"size":86761,"type":"png","color":"272728","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В текстовый лог-файл записывается:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"575572c1-9ed8-0b14-6e34-043ad768d710","width":654,"height":369,"size":171481,"type":"png","color":"e8eeeb","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В данной статье мы рассмотрели простейшего голосового бота и основной полезный функционал для дальнейшей работы бота с нейронной сетью. Для анализа качества оказанной помощи и дальнейшей работы по улучшению мы сможем проверять лог файл.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Этот бот может стать основой для вашего собственного Джарвиса!

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

#selectel_инструкция

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":32,"reposts":1,"views":2,"hits":21831,"reads":null,"online":0},"dateFavorite":0,"hitsCount":21831,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/dev/158797-python-kak-sozdat-prosteishego-golosovogo-pomoshnika","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764967264,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":235819,"name":"Разработка","description":"Сообщество разработчиков: публикации о личном опыте, выдающиеся приёмы при решении рутинных задач, полезные материалы для профессионального роста.","uri":"/dev","avatar":{"type":"image","data":{"uuid":"fef5b5fb-e488-5b7f-8445-e3a26a910b44","width":1200,"height":1200,"size":7757,"type":"png","color":"343434","hash":"04042b2b1c1000","external_service":[]}},"cover":{"type":"image","data":{"uuid":"2a214cc5-35cc-58ca-bc07-fc1c892d2101","width":960,"height":280,"size":177,"type":"png","color":"343434","hash":"","external_service":[]}},"lastModificationDate":1642411346,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":false,"isDisabledAd":false,"nickname":"dev","isUnsubscribable":true,"badge":null,"badgeId":null,"isDonationsEnabled":false,"isOnline":false,"isPlus":false,"isUnverifiedBlogForCompanyWithoutPro":false,"isVerified":false,"isRemovedByUserRequest":false,"isFrozen":false,"isPro":false,"type":2,"subtype":"community"},"reactions":{"counters":[{"id":1,"count":8}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}}],"ogTitle":null,"ogDescription":null,"isAnonymized":true}};