πΌ ΠΠ΅ΡΠ΅Π½ΠΎΡΠΈΠΌ ΡΡΠ΅ΠΊΠΈ ΠΈΠ· Π―Π½Π΄Π΅ΠΊΡ.ΠΡΠ·ΡΠΊΠΈ Π² Spotify Ρ ΠΏΠΎΠΌΠΎΡΡΡ Python
Используя Python и Selenium, получим все плейлисты и альбомы из Яндекс.Музыки, а с помощью библиотеки spotipy перенесем фонотеку в Спотифай.
Возможности скрипта: Единственное условие: фонотека Яндекс.Музыки должна быть открытой. *** Первую часть пути мы пройдем окольным путем: чтобы чему-то научиться откажемся от использования неофициальной библиотеки У Яндекс.Музыки (ЯМ) нет API. Совсем. Поэтому данные нам придется брать со страниц ЯМ. Перейдем на свою страницу ЯМ в раздел альбомы и начнем поиск информации, открыв инструменты разработчика ( В теле страницы с альбомами есть JSON-массив C массивом JSON Яндекс.Музыки Мы воспользуемся первым ключом, чтобы перенести все альбомы. Через функцию Получив ID, перейдем на страницу альбома В браузере перейдем в инструменты разработчика и выберем инструмент Селектор ( И просто кликнем по названию альбома на странице: Тег Тег Библиотека Selenium умеет управлять браузером и часто используется для автоматизации тестирований. С помощью Установим Установим драйвер Chrome: Chromedriver установится в Алгоритм получения данных об альбоме: Создадим в корневой директории папку Импортируем Selenium и напишем функцию get_music_data.py Здесь: Дальше идет небольшой велосипед. Дело в том, что Пауза после загрузки, чтобы страница загрузилась до конца и листание в конец страницы не помогли, другие подходы – тоже. Оказалось, если сохранить страницу на локальной машине, то можно вытащить всю информацию из массива Алгоритм следующий: Напишем функцию get_music_data.py Здесь: Теперь напишем функцию get_music_data.py Здесь: И, наконец, функция get_music_data.py Здесь: Получим название альбома и имя исполнителя. Создадим функцию get_music_data.py В этом блоке кода мы: get_music_data.py Здесь происходит следующее: В итоге словарь выглядит так: За перенос треков отвечает библиотека spotipy. Установим ее:
Для начала нужно создать приложение в Спотифай, получить И кликнем на Создание приложения в Spotify заполним поля и нажмем После создания приложения кликнем по нему и найдем Получение Client ID и Client Secret в приложении Spotify Зайдем в настройки приложения: и впишем в поле Редактирование поля Redirect URIs в приложении Spotify Сохраним и закроем. Дальше нам понадобится второй файл Запишем в transfer.py Здесь: Напишем функцию transfer.py Здесь: Перенесем альбомы через функцию transfer.py Здесь: Плейлисты делятся на два вида: Алгоритм: Вернемся к файлу get_music_data.py Также как с альбомами: переходим на страницу всех плейлистов, извлекаем данные из массива Напишем функцию get_music_data.py В этой части кода мы получаем ID всех наших плейлистов и записываем в словарь Теперь получим названия трека и имя исполнителя: get_music_data.py Здесь: В итоге словарь Теперь перенесем плейлисты в Спотифай. Откроем файл transfer.py Напишем функцию transfer.py transfer.py Лайкнутые плейлисты создали другие пользователи, но мы можем их перенести также как свои плейлсты. Откроем файл get_music_data.py Здесь мы переходим на страницу своих плейлистов, извлекаем содержимое из массива Теперь напишем функцию get_music_data.py В этом блоке кода мы: get_music_data.py Здесь: Лайкнутые плейлисты переносим через функцию В конце файла transfer.py GitHub Весь код с выводом на экран процесса переноса доступен в репозитории yandex-music-to-spotify на GitHub. Плейлисты переносятся долго, поэтому попробуйте сначала перенести альбомы, чтобы посмотреть, как работает скрипт. Альбомы переносятся так: *** Мы написали скрипт для переноса всей фонотеки из Яндекс.Музыки в Спотифай и научились: – Осваиваем парсинг сайта: короткий туториал на Python – Инструменты дата-журналиста #2: веб-скрапинг, парсинг и визуализация данных – Веб-скрапинг по расписанию с Django и Herokuyandex-music-api
и получим данные своими руками. Во второй части пути к нам на помощь придет библиотека spotipy, которая перенесет в Спотифай все личные плейлисты, лайкнутые треки и альбомы. Поехали!Переносим альбомы
Ctrl + Shift + i
в Chrome и FireFox).Mu
, который в двух ключах содержит все, что нам нужно знать об альбоме: его ID, название и имя исполнителя:Mu
будем работать постоянно, так как в нем содержится самое ценное – ID альбомов, плейлистов и треков, по которому мы получаем всю необходимую информацию. Поэтому ID будет нашей целью на всем пути следования.albumIds
– содержит ID всех альбомов, но нам нужно будет дополнительно переходить на страницу каждого альбома и получать его название и имя исполнителя и тегов.albums
– сразу выдает название альбома и имя исполнителя, но только первые 150 пар (это ограничение распространяется на плейлисты тоже).Как вывести json в более читаемом виде, как на картинке выше?
pprint
:
from pprint import pprint pprint(json)
Как получить название альбома и имя исполнителя?
https://music.yandex.ru/album/ID_альбома
. Из тегов узнаем название альбома и имя исполнителя. Допустим, в нашей фонотеке есть альбом «Greatest Hits In Japan» – Queen. Откроем страницу альбома.Ctrl + Shift + C
):<h1>
с названием альбома найдется автоматически:<a>
с именем исполнителя:Автоматизация получения данных, Selenium
Selenium
мы автоматизируем действия браузера: открытие страниц плейлистов, альбомов и треков.Selenium
:
pip install selenium
sudo apt-get install chromium-chromedriver
/usr/lib/chromium-browser/chromedriver.
Mu
.proglib
, а в ней файл get_music_data.py
.run_driver()
, запускающую браузер:
from selenium import webdriver from selenium.webdriver.chrome.options import Options import os import re # функция, запускающая браузер def run_driver(): # включает возможность управления опциями браузера chrome_options = Options() # аргумент '--headless' запускает браузер без всплывающего окна chrome_options.add_argument('--headless') # аргумент '--log-level=3' скрывает логирование chrome_options.add_argument('--log-level=3') # запуск браузера driver = webdriver.Chrome('chromedriver', options=chrome_options) return driver
'chromedriver'
– путь к драйверу. Либо можем записать полный путь: '/usr/lib/chromium-browser/chromedriver'
.Selenium
при извлечении массива Mu
со страницы ЯМ выдает ошибку:
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: stale element not found
Mu
без каких-либо проблем. Сделаем это.Mu
.create_local_html_page()
, которая создает локальный html-файл и заполняет ее содержимым страницы из ЯМ:
# функция, создающая локальный html-файл def create_local_html_page(page_filename, page_source): with open(page_filename + '.html', 'w', encoding='UTF-8') as file: # запишем в локальный html-файл содержимое интернет-страницы ЯМ (page_source) file.write(page_source)
page_filename
– название html-файла.page_source
– код страницы ЯМ.get_local_html_page()
, которая с помощью Selenium
открывает локальную страницу:
# функция, открывающая локальный html-файл def get_local_html_page(yandex_username, page_filename, url): # запускаем браузер driver = run_driver() # браузер переходит на страницу Яндекс.Музыки driver.get(url) # создаем локальную html-страницу с содержимым из страницы Яндекс.Музыки create_local_html_page(page_filename, driver.page_source) # путь к локальной html-странице path_to_local_html_page = str('file://' + os.getcwd() + '/' + page_filename + '.html') # браузер переходит на локальную html-страницу driver.get(path_to_local_html_page) return driver
yandex_username
– имя пользователя ЯМ.page_filename
– название html-файла.url
– ссылка на ЯМ.delete_local_html_page()
удаляет локальную страницу:
# функция, удаляющая локальный html-файл def delete_local_html_page(page_path): os.remove(page_path)
page_path
– путь к html-файлу.get_albums()
:
# функция, создающая словарь с названиями альбомов и именами исполнителей def get_albums(yandex_username): # создаем ссылку на альбом url = ''.join(['https://music.yandex.ru/users/', yandex_username, '/albums/']) # создадим и перейдем на локальную страницу с альбомом из раздела «Также вам понравились эти плейлисты» driver = get_local_html_page(yandex_username, 'album_page', url) # прочтем массив с данными плейлистов json = driver.execute_script('return Mu') # создадим словарь, в который запишем название альбома и имя исполнителя albums_for_spotify = {} # получим данные альбомов all_albums_ids = json['pageData']['albumIds']
Mu
.albums_for_spotify
, в который запишем название альбома и имя исполнителя.
# заполним словарь albums_for_spotify for i in range(len(all_albums_ids)): try: # перейдем на страницу альбома driver.get(''.join(['https://music.yandex.ru/album/', str(all_albums_ids[i])])) # создадим для каждого альбома вложенный словарь albums_for_spotify[i] = {} # запишем во вложенный словарь название альбома albums_for_spotify[i]['album_title'] = driver.find_element_by_xpath( "//h1[@class='deco-typo']").text # запишем во вложенный словарь имя исполнителя albums_for_spotify[i]['artist_name'] = driver.find_element_by_xpath( "//span[@class='d-artists']//a[@class='d-link deco-link']").text except: pass # удалим локальный html-файл delete_local_html_page('album_page.html') # закроем браузер driver.quit() return albums_for_spotify
xpath
позволяет искать вложенные друг в друга теги и извлекать из них текст. С его помощью найдем в коде страницы тег <h1>
с классом deco-typo
. И получим текст, содержащийся в теге с помощью .text
.
{0: {'album_title': 'The Essential Aerosmith', 'artist_name': 'Aerosmith'}, 1: {'album_title': 'Hyperion', 'artist_name': 'Gesaffelstein'}, ...
driver.find_elements_by_xpath("//span[@class='d-artists']//a[@class='d-link deco-link']")[0].text
– возвращает список, где первый элемент – имя первого исполнителя, если их несколько или единственного, если он записал трек соло.Данные получили, как их перенести?
pip install spotipy
client_id
и client_secret
. Зайдем на страницу developer.spotify.com/dashboardCreate an app
:Create
:Client ID
и Client Secret
:Redirect URIs
адрес http://localhost:8888/callback/
:transfer.py
для авторизации в Spotify
через библиотеку spotify
и для переноса альбомов (а также плейлистов, но об этом позже).transfer.py
следующие строчки:
import spotipy from spotipy.oauth2 import SpotifyOAuth from spotipy import oauth2 from get_music_data import get_my_playlists, get_liked_playlists, get_albums # функция для авторизации в Spotify def autorisation(): client_id = '' client_secret = '' # разрешения нашего приложения scope = ('user-library-read, playlist-read-private, playlist-modify-private, playlist-modify-public, user-read-private, user-library-modify, user-library-read') # URL, на который переадресуется браузер пользователя после получения прав доступа при получении ключа доступа redirect_uri = 'http://localhost:8888/callback/' sp_oauth = oauth2.SpotifyOAuth(client_id, client_secret, redirect_uri, scope=scope) code = sp_oauth.get_auth_response(open_browser=True) # получаем токен token = sp_oauth.get_access_token(code, as_dict=False) sp = spotipy.Spotify(auth=token) # id пользователя Spotify username = sp.current_user()['id'] return sp, username
scope
– права приложения.redirect_uri
– ссылка, которая откроется (и сразу закроется) в браузере при получении прав доступа.username
– имя пользователя в Спотифай.get_album_id()
, которая ищет в спотифай ID альбома:
# функция, получающая id альбома def get_album_id(query, sp): # получим данные по альбому из поискового запроса в Spotify album_id = sp.search(q=query, limit=1, type='album') # получим id альбома # split() – делает список, состоящий из одной строчки для метода current_user_saved_albums_add() return album_id['albums']['items'][0]['id'].split()
sp.search(q=query, limit=1, type='album')
– поисковый запрос query выдает первый результат из раздела альбомы.split
делает из строки список, потому что метод current_user_saved_albums_add()
, добавляющий в Спотифай альбом по ID, принимает в качестве аргумента именно список.transfer_albums()
:
# функция для переноса альбомов def transfer_albums(yandex_username, albums_for_spotify): # авторизуемся в Spotify sp, username = autorisation() # albums_for_spotify – альбомы для переноса из Яндекс.Музыки # содержит название трека и имя исполнитель for i in range(len(albums_for_spotify)): try: # получим название альбома album_title = albums_for_spotify[i]['album_title'] # получим имя исполнителя artist_name = albums_for_spotify[i]['artist_name'] # сформируем поисковый запрос в Spotify query = ' '.join([artist_name, album_title]) # получим id альбома в Spotify album_id = get_album_id(query, sp) # добавим альбом в свою фонотеку sp.current_user_saved_albums_add(album_id) except: pass
sp, username = autorisation()
– авторизация в Спотифай.query = ' '.join([artist_name, album_title])
– формируем поисковый запрос из названия альбома и имени исполнителя.get_album_id(query, sp)
– ищем в Спотифай альбом по ID.sp.current_user_saved_albums_add(album_id)
– добавляем альбом в свою медиатеку.except: pass
– если альбом не найден (функция get_album_id
вернет нам IndexError
или KeyError
), то пропустить и начать сначала.Переносим плейлисты
Мои плейлисты
https://music.yandex.ru/users/yandex_username/playlists/
.Mu
.Mu
.get_music_data.py
и добавим в него функцию get_my_playlists_id
для получения ID альбомов на ЯМ:
# функция, получающая id плейлистов def get_my_playlists_id(yandex_username): # создадим ссылку на плейлист url = ''.join(['https://music.yandex.ru/users/', yandex_username, '/playlists/']) # создадим и перейдем на локальную страницу плейлиста driver = get_local_html_page(yandex_username, 'playlist_page', url) # получим массив с данными плейлистов json = driver.execute_script('return Mu') # получим id плейлистов my_playlists_id = json['pageData']['playlistIds'] return my_playlists_id
Mu
, находим ключ playlistIds
с ID всех плейлистов. get_my_playlists
для переноса своих плейлистов:
# функция, создающая словарь с названиями личных плейлистов, треков и именами исполнителей def get_my_playlists(yandex_username): # получим id плейлистов my_playlists_id = get_my_playlists_id(yandex_username) # создадим словарь, в который запишем название плейлистов, треков и имена исполнителей my_playlists_for_spotify = {} # заполним словарь my_playlists_for_spotify for i in range(len(my_playlists_id)): # создадим ссылку на плейлист my_playlist_url = ''.join(['https://music.yandex.ru/users/', yandex_username, '/playlists/', str(my_playlists_id[i])]) # создадим и перейдем на локальную страницу плейлиста driver = get_local_html_page(yandex_username, 'my_playlist', my_playlist_url) # получим массив с данными плейлистов json = driver.execute_script('return Mu') # создадим словарь, в который запишем название трека и имя исполнителя my_playlists = {} # получим из json значение id трека и id плейлиста в формате track_id:playlist_id all_track_ids = json['pageData']['playlist']['trackIds'] # получим название плейлиста playlist_name = json['pageData']['playlist']['title']
my_playlists
, их ID и названия.
# заполним словарь my_playlists for j in range(len(all_track_ids)): # получим id трека track_id = re.findall(r'd+(?=:)', all_track_ids[j])[0] # перейдем на страницу трека driver.get(''.join(['https://music.yandex.ru/track/', track_id])) # создадим вложенный словарь для каждого плейлиста my_playlists[j] = {} # запишем в словарь название трека try: my_playlists[j]['track_name'] = driver.find_elements_by_xpath("//span[@class='']//a[@class='d-link deco-link']")[0].text except: my_playlists[j]['track_name'] = [''] # запишем в словарь имя исполнителя try: my_playlists[j]['artist_name'] = driver.find_elements_by_xpath( "//span[@class='d-artists']//a[@class='d-link deco-link']")[0].text except: my_playlists[j]['track_name'] = [''] # запишем в словарь название плейлиста, его треки и имена исполнителей my_playlists_for_spotify[playlist_name] = my_playlists # удалим локальный html-файл delete_local_html_page('my_playlist.html') # закроем браузер driver.quit() return my_playlists_for_spotify
track_id = re.findall(r'd+(?=:)', all_track_ids[j])[0]
– all_track_ids
имеет вид ID_трека:ID_плейлиста
. Используем регулярное выражение, чтобы получить все числа до двоеточия – ID трека.my_playlists_for_spotify
выглядит так:
{'Мне нравится': {0: {'track_name': 'Memory (From "Cats")', 'artist_name': 'London Theatre Orchestra'}, 1: {'track_name': 'All By Myself', 'artist_name': 'Céline Dion'}, ...
transfer.py
и напишем функцию get_track_id()
для поиска ID трека в Спотифай:
# функция, получающая id трека def get_track_id(query, sp): # получаем данные по первому треку из поисковой выдачи Spotify track_id = sp.search(q=query, limit=1, type='track') # Теперь найдем id первого трека из поисковой выдачи. # метод split() сделает список из одной строчки.eя # Это нужно для метода playlist_add_items(), принимающего в качестве второго аргумента список. return track_id['tracks']['items'][0]['id'].split()
sp.search(q=query, limit=1, type='track')
– получаем первый результат поискового запроса query
.transfer_playlists
, которая будет переносить как плейлисты пользователя, так и лайкнутые:
# функция, переносящая плелисты def transfer_playlists(yandex_username, playlists_for_spotify): # авторизуемся в Spotify sp, username = autorisation() # playlists_for_spotify – плейлисты из Яндекс Музыки (название и исполнитель) # плейлисты берем из функций get_liked_playlists() и get_my_playlists() # создадим плейлисты for i in range(len(playlists_for_spotify)): # сделаем список из ключей/названий плейлистов playlist_name = list(playlists_for_spotify.keys())[i] # создадим в Spotify плейлист с именем (playlist_name) create_spotify_playlist = sp.user_playlist_create(username, playlist_name) # получим id созданного плейлиста new_spotify_playlist_id = create_spotify_playlist['id'] # number_of_tracks – количество треков в плейлисте playlist_name number_of_tracks = range(len(playlists_for_spotify[playlist_name])) new_spotify_playlist = {}
create_spotify_playlist
– создает пустой плейлист в Спотифай с названием из ЯМ.number_of_tracks
– количество треков в плейлисте.
# добавим песни в плейлист Spotify for j in number_of_tracks: try: # получим имя исполнителя artist_name = playlists_for_spotify[playlist_name][j]['artist_name'] # получим название трека track_name = playlists_for_spotify[playlist_name][j]['track_name'] # query – поисковый запрос в Spotify, состоящий из имени исполнителя (artist_name), пробела (' ') и названия трека (track_name) query = ' '.join([artist_name, track_name]) # получим id найденного трека в Spotify spotify_track_id = get_track_id(query, sp) # добавим в словарь id трека (ключ) и id плейлиста (значение) new_spotify_playlist[spotify_track_id[0]] = new_spotify_playlist_id except: pass # если плейлист пустой (треки отсутствуют в каталоге Spotify), то удалить его из Spotify if all(query == '' for query in new_spotify_playlist.values()): sp.user_playlist_unfollow(username, new_spotify_playlist_id) continue # если в каталоге есть хотя бы один трек, то добавить трек(и) в плейлист Spotify else: for new_spotify_playlist_id, track_id in new_spotify_playlist.items(): sp.playlist_add_items(track_id, new_spotify_playlist_id.split())
new_spotify_playlist
.if all(query == '' for query in new_spotify_playlist.values())
– если все значения словаря пустые, значит треки отсутствуют в каталоге Спотифай, например, подкасты или локальные исполнители.sp.user_playlist_unfollow(username, new_spotify_playlist_id)
– удаляет пустой плейлист из Спотифай.Лайкнутые плейлисты
get_music_data
и напишем функцию get_liked_playlists_data()
, которая создает словарь с ID лайкнутых плейлистов, их названиями и именами пользователей, создавших плейлист.
# функция, создающая словарь с id лайкнутых плейлистов, их названиями # и именами пользователя, создавших плейлист def get_liked_playlists_data(yandex_username): # создадим ссылку на плейлист url = ''.join(['https://music.yandex.ru/users/', yandex_username, '/playlists/']) # создадим и перейдем на локальную страницу с плейлистами из раздела «Также вам понравились эти плейлисты» driver = get_local_html_page(yandex_username, 'playlist_page', url) # получим массив с данными плейлистов json = driver.execute_script('return Mu') # создадим словарь, в который запишем имя пользователя, создавшего плейлист, id и название плейлиста liked_playlists_data = {} # получим все данные об избранных плейлистах bookmarked_playlists_data = json['pageData']['bookmarks'] # заполним словарь liked_playlists_data for i in range(len(bookmarked_playlists_data)): try: # создадим вложенный словарь для каждого плейлиста liked_playlists_data[i] = {} # запишем во вложенный словарь id плейлиста liked_playlists_data[i]['id'] = bookmarked_playlists_data[i]['kind'] # запишем во вложенный словарь имя пользователя liked_playlists_data[i]['yandex_username'] = bookmarked_playlists_data[i]['owner']['login'] # запишем во вложенный словарь название плейлиста liked_playlists_data[i]['playlist_title'] = bookmarked_playlists_data[i]['title'] except: pass # удалим локальный html-файл delete_local_html_page('playlist_page.html') return liked_playlists_data
Mu
и создаем словарь с ID плейлиста, его названием и именем пользователя создавшего плейлист.get_liked_playlists()
, которая переходит на страничку лайкнутых плейлистов и получает названия треков и исполнителей
# функция, которая переходит на страничку лайкнутых плейлистов и получает названия треков и исполнителей def get_liked_playlists(yandex_username): # получим id плейлистов, их названия и имя пользователя, создавшего плейлист liked_playlists_data = get_liked_playlists_data(yandex_username) # создадим словарь, в который запишем название плейлиста, треков и имя исполнителя liked_playlists_for_spotify = {} for key, value in liked_playlists_data.items(): # ключ – порядковый номер плейлиста в словаре liked_playlists_data, а значения – имя пользователя и id плейлиста # создадим ссылку на плейлист пользователя url = ''.join(['https://music.yandex.ru/users/', str(value['yandex_username']), '/playlists/', str(value['id'])]) # создадим и перейдем на локальную html-страницу плейлиста driver = get_local_html_page(yandex_username, str(key), url) # получим массив с данными плейлистов json = driver.execute_script('return Mu') # создадим словарь, в который запишем треки и имя исполнителей liked_playlists = {} # получим из json значение id трека и id альбома в формате track_id:album_id all_track_ids = json['pageData']['playlist']['trackIds'] # получим название плейлиста playlist_name = json['pageData']['playlist']['title']
ID_трека:ID_альбома
.
# запишем в словарь треки и имена исполнителей for i in range(len(all_track_ids)): try: # отфильтруем только id трека track_id = re.findall(r'd+(?=:)', all_track_ids[i])[0] # перейдем на страницу трека driver.get(''.join(['https://music.yandex.ru/track/', track_id])) # создадим вложенный словарь для каждого плейлиста liked_playlists[i] = {} # получим название трека из боковой панели try: liked_playlists[i]['track_name'] = driver.find_elements_by_xpath("//span[@class='']//a[@class='d-link deco-link']")[0].text except: liked_playlists[i]['track_name'] = [''] # получим имя исполнителя из боковой панели try: liked_playlists[i]['artist_name'] = driver.find_elements_by_xpath( "//span[@class='d-artists']//a[@class='d-link deco-link']")[0].text except: liked_playlists[i]['artist_name'] = [''] except: pass # запишем в словарь название плейлиста, его треки и имена исполнителей liked_playlists_for_spotify[playlist_name] = liked_playlists # удалим локальный html-файл delete_local_html_page(str(key) + '.html') # закроем браузер driver.quit() return liked_playlists_for_spotify
transfer_playlsits()
.Запускаем миграцию музыки
transfer.py
напишем функцию main()
, которая перенесет всю фонотеку в Спотифай.
def main(yandex_username): # перенос плейлиста «Мне нравится» и личных плейлистов #transfer_playlists(yandex_username, get_my_playlists(yandex_username)) # перенос лайкнутых плейлистов #transfer_playlists(yandex_username, get_liked_playlists(yandex_username)) # перенос альбомов transfer_albums(yandex_username, get_albums(yandex_username)) if __name__ == "__main__": yandex_username = 'имя пользователя Яндекс.Музыки' main(yandex_username)
Материалы по теме:
- 1 views
- 0 Comment
- ΠΡΠ΅Π΄ΡΠ΄ΡΡΠΈΠΉ - ΠΠ·ΡΡΠ΅Π½ΠΈΠ΅ Python OpenCV / Π£ΡΠΎΠΊ #6 β ΠΠΎΠ±ΠΈΡΠΎΠ²ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ ΠΈ ΠΌΠ°ΡΠΊΠΈ
- All Posts
- Π‘Π»Π΅Π΄ΡΡΡΠΈΠΉ - π§ Π’Π΅ΡΠΌΠΈΠ½Π°Π» Π΄Π»Ρ ΡΠ΅ΡΡΠΈΡΠΎΠ²ΡΠΈΠΊΠ°: ΠΊΠΎΠ½ΡΠΎΠ»ΡΠ½ΡΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Unix/Linux, ΠΊΠΎΡΠΎΡΡΠ΅ Π½ΡΠΆΠ½ΠΎ Π·Π½Π°ΡΡ Π½Π°ΠΈΠ·ΡΡΡΡ
Π‘Π²Π΅ΠΆΠΈΠ΅ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ