{"id":478248,"date":"2026-05-01T14:16:28","date_gmt":"2026-05-01T14:16:28","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=478248"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=478248","title":{"rendered":"\u041a\u0430\u043a \u044f \u043f\u0440\u0438\u0448\u0451\u043b \u043a \u0438\u0434\u0435\u0438 \u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u041f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u0435<\/h2>\n<p>\u042f \u041c\u0438\u0445\u0430\u0438\u043b\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044c \u0438 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u044d\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0412\u0442\u043e\u0440\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u00a0\u2014 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u00a0\u2014 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u00a0SEO \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0430\u044f \u0432\u00a0\u0441\u0435\u0431\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u0438\u0441\u043a, \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u00a0\u0444\u043e\u0442\u043e \u0438 \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u00a0\u043e\u0431\u044b\u0447\u043d\u043e\u043c\u0443 \u0442\u0435\u043a\u0441\u0442\u0443.<\/p>\n<\/li>\n<li>\n<p>\u041c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0441\u00a0\u0434\u0432\u0443\u043c\u044f \u0431\u043e\u0442\u0430\u043c\u0438: \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f \u0441\u00a0\u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u043e\u043c, \u0430\u00a0\u0432\u0442\u043e\u0440\u043e\u0439\u00a0\u2014 \u0441\u00a0\u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c (\u0441\u043c. IoT \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438).<\/p>\n<\/li>\n<\/ul>\n<h2>\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h2>\n<p>\u042d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a \u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b \u043d\u0430 3 \u0447\u0430\u0441\u0442\u0438: <\/p>\n<ul>\n<li>\n<p>\u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, <\/p>\n<\/li>\n<li>\n<p>\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, <\/p>\n<\/li>\n<li>\n<p>IoT \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u0434\u043b\u044f \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.<\/p>\n<\/li>\n<\/ul>\n<h3>1. \u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439<\/h3>\n<p>\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043e\u043d\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u043e\u0442\u0430 \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435, \u0438 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0434\u0440\u0443\u0433\u043e\u0433\u043e.<\/p>\n<h4>1.1 \u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a<\/h4>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2d4\/fad\/529\/2d4fad529297a02fe5a881b621caef24.png\" alt=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430\" title=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430\" width=\"1905\" height=\"912\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/2d4\/fad\/529\/2d4fad529297a02fe5a881b621caef24.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2d4\/fad\/529\/2d4fad529297a02fe5a881b621caef24.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u0411\u044d\u043a\u0435\u043d\u0434 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Django \u043d\u0430 \u044f\u0437\u044b\u043a\u0435 python \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 API \u0437\u0430\u043f\u0440\u043e\u0441\u044b (POST \u0438 GET). \u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u043b\u0435\u0436\u0438\u0442 Yandex API Search, \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0434\u043e\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 Yandex Speech Kit \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u0430 \u0432 \u0442\u0435\u043a\u0441\u0442.<\/p>\n<p>Django \u0432\u044b\u0431\u0440\u0430\u043d, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043e\u0431\u0449\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0422\u0430\u043a, \u043f\u043e\u0447\u0442\u0438 \u0432\u0435\u0441\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0438 \u0433\u043e\u043b\u043e\u0441\u0430, \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0431\u044d\u043a\u0435, \u0438 \u0447\u0435\u0440\u0435\u0437 Django \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u043d\u0434\u0435\u0440, \u0430 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 \u043e\u0442\u0432\u0435\u0442\u044b \u0438\u0437 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0433\u0438\u043f\u0435\u0440\u0441\u0441\u044b\u043b\u043a\u0430\u043c.<\/p>\n<p>\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 service.py \u044f \u043e\u043f\u0438\u0441\u0430\u043b \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c: <\/p>\n<ul>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u0412\u0441\u0435\u00bb, <\/p>\n<\/li>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438\u00bb,<\/p>\n<\/li>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u0412\u0438\u0434\u0435\u043e\u00bb,<\/p>\n<\/li>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u0444\u043e\u0442\u043e,<\/p>\n<\/li>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0430\u0443\u0434\u0438\u043e\u0444\u0430\u0439\u043b\u0430<\/p>\n<\/li>\n<li>\n<p>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"python\">import osfrom dotenv import load_dotenvimport base64import timeimport loggingimport requestsimport subprocessfrom django.http import HttpResponsefrom bs4 import BeautifulSoupimport xml.etree.ElementTree as ETload_dotenv()logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')def search(search_query, page):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435    folderid = os.getenv('FOLDERID')    api_key = os.getenv('API_KEY')    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421', search_query)    print('\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u043e\u043c\u0435\u0440: ', page)    try:        page_number = int(page)    except ValueError:        page_number = 1    results = []    url = 'https:\/\/searchapi.api.cloud.yandex.net\/v2\/web\/search'    headers = {\"Authorization\": f\"Api-Key {api_key}\"}    body = {        \"query\": {          \"searchType\": \"SEARCH_TYPE_COM\",          \"queryText\": search_query,          \"familyMode\": \"FAMILY_MODE_NONE\",          \"page\": page_number,          \"fixTypoMode\": \"FIX_TYPO_MODE_ON\"        },        \"folderId\": folderid,        \"userAgent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/141.0.0.0 Safari\/537.36\",        \"responseFormat\": \"FORMAT_XML\",    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        start_time = time.time()        encode_response = response.json()[\"rawData\"]        # \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0437 base64        decoded_bytes = base64.b64decode(encode_response)        # \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0431\u0430\u0439\u0442\u044b \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 (UTF-8)        xml_data = decoded_bytes.decode('utf-8')        # \u041f\u0430\u0440\u0441\u0438\u043c XML        root = ET.fromstring(xml_data)        # \u0418\u0449\u0435\u043c \u0432\u0441\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b        for doc in root.findall('.\/\/doc'):            url_elem = doc.find('url')            title_elem = doc.find('title')            domain = doc.find('domain')            header = ''.join([title for title in title_elem.itertext()])            if '\u0423\u043a\u0440\u0430\u0438\u043d' in header or '\u0443\u043a\u0440\u0430\u0438\u043d' in header:                continue            else:                results.append({                    'url': url_elem.text if url_elem is not None else '',                    'title': header if header is not None else '',                    'favicon_url': f'https:\/\/{domain.text}\/favicon.ico'                })        return results    else:        print(f\"Error: {response.status_code} - {response.text}\")        return HttpResponse(\"Error occurred\", status=response.status_code)def image(service_request, search_query):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u043e\u0442\u043e    folderid = os.getenv('FOLDERID')    api_key = os.getenv('API_KEY')    page = service_request.GET.get('page', 1)    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421 \u041a\u0410\u0420\u0422\u0418\u041d\u041a\u0418', search_query)    try:        page_number = int(page)    except ValueError:        page_number = 1    images = []    url = 'https:\/\/searchapi.api.cloud.yandex.net\/v2\/image\/search'    headers = {\"Authorization\": f\"Api-Key {api_key}\"}    body = {        \"query\": {            \"searchType\": \"SEARCH_TYPE_COM\",            \"queryText\": search_query,            \"familyMode\": \"FAMILY_MODE_NONE\",            \"page\": page_number,            \"fixTypoMode\": \"FIX_TYPO_MODE_ON\"        },        \"folderId\": folderid,        \"userAgent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/141.0.0.0 Safari\/537.36\",    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        encode_response = response.json()[\"rawData\"]        # \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0437 base64        decoded_bytes = base64.b64decode(encode_response)        # \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0431\u0430\u0439\u0442\u044b \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 (UTF-8)        text_xml = decoded_bytes.decode('utf-8')        soup = BeautifulSoup(text_xml, 'lxml')  # \u041f\u0430\u0440\u0441\u0438\u043c HTML-\u043a\u043e\u0434        # \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439        for item in soup.find_all('doc'):            img_url = item.find('image-link').text if item.find('image-link') else None            link_url = item.find('html-link').text if item.find('html-link') else None            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438 \u0441\u0430\u0439\u0442 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c            if img_url and link_url:                images.append({'url': img_url, 'link': link_url})        return images    else:        print(f\"Error: {response.status_code} - {response.text}\")        return HttpResponse(\"Error occurred\", status=response.status_code)def video(service_request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432\u0438\u0434\u0435\u043e    api_key = os.getenv('OTHER_API_KEY')    search_engine_id = os.getenv('SEARCH_ENGINE_ID')    search_query = service_request.GET.get('query', '')    page = service_request.GET.get('page', 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    url = f'https:\/\/customsearch.googleapis.com\/customsearch\/v1\/?q=\u0432\u0438\u0434\u0435\u043e {search_query}&amp;page={page_number}&amp;cx={search_engine_id}&amp;key={api_key}'    headers = {\"Authorization\": f\"Api-Key {api_key}\"}    response = requests.get(url, headers=headers)    print(page)    if response.status_code == 200:        all_data = []        items = response.json()[\"items\"]        for item in items:            try:                thumbnail = item[\"pagemap\"][\"cse_thumbnail\"][0][\"src\"]            except KeyError:                thumbnail = None            all_data.append({'url': item[\"link\"], 'title':item[\"title\"], 'thumbnail': thumbnail})        return all_data    else:        print(f\"Error: {response.status_code} - {response.text}\")        return HttpResponse(\"Error occurred\", status=response.status_code)def search_by_image(service_request, encoded_image):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u0444\u043e\u0442\u043e     folderid = os.getenv('FOLDERID')    api_key = os.getenv('API_KEY')    section = service_request.GET.get('section')    page = service_request.GET.get('page', 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    results = []    url = 'https:\/\/searchapi.api.cloud.yandex.net\/v2\/image\/search_by_image'    headers = {\"Authorization\": f\"Api-Key {api_key}\"}    body = {        \"folderId\": folderid,        \"data\": encoded_image,        \"page\": page_number    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        images = response.json()[\"images\"]        for image in images:            if section == None:                results.append({'link': image['url'], 'title': image['pageTitle'], 'url': image['pageUrl']})            elif section == '\u041f\u043e\u0445\u043e\u0436\u0435\u0435':                results.append({'link': image['url']})            elif section == '\u0421\u0430\u0439\u0442\u044b':                results.append({'title': image['pageTitle'], 'url': image['pageUrl']})        service_request.session['encoded_image'] = encoded_image        return results    else:        print(f\"Error: {response.status_code} - {response.text}\")        return HttpResponse(\"Error occurred\", status=response.status_code)def convert_webm_to_ogg(input_path: str, output_path: str):     # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0430\u0443\u0434\u0438\u043e\u0444\u0430\u0439\u043b\u0430 webm \u0432 ogg    ffmpeg_path = '\/usr\/bin\/ffmpeg'    print('\u0412\u044b\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443')    command = [        ffmpeg_path,        '-y',        '-i', input_path,        '-c:a', 'libopus',        output_path    ]    print('\u041d\u0430\u0447\u0430\u043b\u043e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438')    try:        print('\u041f\u043e\u043f\u044b\u0442\u043a\u0430')        subprocess.run(command, check=True)        print(f\"\u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430: {output_path}\")    except subprocess.CalledProcessError as e:        print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438: {e.stderr.decode()}\")def voice_search(file):    # \u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u0438\u0441\u043a    folderid = os.getenv('FOLDERID')    api_key = os.getenv('API_KEY')    url = f'https:\/\/stt.api.cloud.yandex.net\/speech\/v1\/stt:recognize?topic=general&amp;folderId={folderid}'    headers = {        \"Authorization\": f\"Api-Key {api_key}\",        \"Content-Type\": \"audio\/ogg\"    }    file.seek(0)    data = file.read()    input_webm = 'audio.webm'    # \u0417\u0430\u043f\u0438\u0441\u044c \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0444\u0430\u0439\u043b    with open(input_webm, 'wb') as f_out:        f_out.write(data)    convert_webm_to_ogg(input_webm, 'audio.ogg')    print('\u0423\u0441\u043f\u0435\u0448\u043d\u043e')    with open('audio.ogg', 'rb') as f:        ogg_data = f.read()    response = requests.post(url, headers=headers, data=ogg_data)    try:        return response.json()    except Exception as e:        print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0440\u0430\u0437\u0431\u043e\u0440\u0435 \u043e\u0442\u0432\u0435\u0442\u0430: {e}\")        return None<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0445\u043e\u0447\u0443 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u043c \u043f\u043e\u0438\u0441\u043a\u0435.<\/p>\n<p>\u0417\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0432 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JavaScript \u0437\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0438 \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0440\u0435\u0447\u044c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u201cwebm\u201d (\u0444\u0430\u0439\u043b voice_file.js):<\/p>\n<pre><code class=\"javascript\">document.getElementById('writeVoice').addEventListener('click', () =&gt; {  let mediaRecorder;  let audioChunks = [];  navigator.mediaDevices.getUserMedia({ audio: true })    .then(stream =&gt; {      const options = { mimeType: 'audio\/webm;codecs=opus' };      let recorderOptions = options;      if (!MediaRecorder.isTypeSupported(options.mimeType)) {        console.warn(`${options.mimeType} \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0431\u0435\u0437 \u0442\u0438\u043f\u043e\u0432`);        recorderOptions = {};      } else {        recorderOptions = { mimeType: options.mimeType };      }      \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c Recorder \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u043c \u0442\u0438\u043f\u043e\u043c      mediaRecorder = new MediaRecorder(stream, recorderOptions);      mediaRecorder.onstart = () =&gt; console.log('\u0417\u0430\u043f\u0438\u0441\u044c \u043d\u0430\u0447\u0430\u043b\u0430\u0441\u044c');      mediaRecorder.ondataavailable = event =&gt; {        audioChunks.push(event.data);      };      mediaRecorder.onstop = () =&gt; {        const audioBlob = new Blob(audioChunks, { type: 'audio\/webm' });        audioChunks = [];        const formData = new FormData();        formData.append('file', audioBlob, 'audio.webm');        fetch('\/search\/api\/voice', {          method: 'POST',          body: formData,        }).then(response =&gt; {          return response.json();          })          .then(text =&gt; {              const input = document.querySelector('input[name=\"query\"]');              input.value = text.data.result;              const searchButton = document.querySelector('button.btn.btn-outline-success[type=\"submit\"]');              if (input.value != '') {                searchButton.click()              } else {              }          })          .catch(console.error);      };      console.log('\u041d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u044c...');      mediaRecorder.start();      const audioContext = new AudioContext();      const source = audioContext.createMediaStreamSource(stream);      const analyser = audioContext.createAnalyser();      analyser.fftSize = 512;      source.connect(analyser);      const dataArray = new Uint8Array(analyser.frequencyBinCount);      let silenceStart = null;      \/\/ \u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0442\u0438\u0448\u0438\u043d\u044b      const silenceThreshold = 30;  \/\/ \u041f\u043e\u0440\u043e\u0433 \u0433\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u0438 (0-255), \u043d\u0438\u0436\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u0442\u0438\u0448\u0438\u043d\u0443      const maxSilenceTime = 1500;  \/\/ \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0442\u0438\u0448\u0438\u043d\u044b (\u043c\u0441), \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0437\u0430\u043f\u0438\u0441\u044c      function checkSilence() {        analyser.getByteFrequencyData(dataArray);        const volume = dataArray.reduce((a,b) =&gt; a + b) \/ dataArray.length;        if (volume &lt; silenceThreshold) {          if (silenceStart === null) {            silenceStart = Date.now();          } else {            let silenceDuration = Date.now() - silenceStart;            if (silenceDuration &gt; maxSilenceTime) {              console.log('\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430 \u0442\u0438\u0448\u0438\u043d\u0430, \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u044c');              mediaRecorder.stop();              audioContext.close();              return; \/\/ \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u044b\u0437\u043e\u0432 \u0442\u0430\u0439\u043c\u0435\u0440\u0430            }          }        } else {          silenceStart = null; \/\/ \u0437\u0432\u0443\u043a \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u2014 \u0441\u0431\u0440\u043e\u0441 \u0442\u0430\u0439\u043c\u0435\u0440\u0430 \u0442\u0438\u0448\u0438\u043d\u044b        }        requestAnimationFrame(checkSilence);      }      checkSilence();    })    .catch(err =&gt; console.error('\u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443:', err));});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 API \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \/search\/api\/voice \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0444\u043e\u0440\u043c\u0430\u0442 \u201cwebm\u201d \u0432 \u201cogg\u201d. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e DRF \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442 \u201cwebm\u201d \u0444\u0430\u0439\u043b (\u0444\u0430\u0439\u043b api.py), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e voice_search(file)\u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f services.py:<\/p>\n<pre><code class=\"python\">from rest_framework import genericsfrom rest_framework.response import Responsefrom .services import voice_searchclass VoiceView(generics.GenericAPIView):    def post(self, request, *args, **kwargs):        audio = request.data        file = audio.get('file')        data = voice_search(file)        print('\u0424\u0430\u0439\u043b \u0437\u0430\u043f\u0438\u0441\u0430\u043b\u0441\u044f')        return Response({\"data\": data})<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f convert_webm_to_ogg \u0432 service.py, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0444\u043e\u0440\u043c\u0430\u0442 \u201cwebm\u201d \u0432 \u201cogg\u201d. \u0421\u0434\u0435\u043b\u0430\u043d\u043e \u044d\u0442\u043e, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0442\u0435\u043a\u0441\u0442 \u0438\u0437 ogg \u0444\u0430\u0439\u043b\u0430 \u0447\u0435\u0440\u0435\u0437 Yandex SpeechKit, \u0430 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441 ogg \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c. \u0412 \u043a\u043e\u043d\u0446\u0435 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043a\u0441\u0442 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 \u201cogg\u201d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443, \u0438 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u0451\u043d\u043d\u043e\u043c\u0443 \u0442\u0435\u043a\u0441\u0442\u0443. \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0444\u043e\u0440\u043c\u0430\u0442 \u201cwebm\u201d, \u0430 Yandex SpeechKit \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u201cogg\u201d \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, Yandex SpeechKit \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443. \u042f \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0441\u044f, \u0438 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0432 \u043f\u0443\u0442\u0438 \u0434\u043e ffmpeg, \u044f \u0443\u043a\u0430\u0437\u0430\u043b \u043f\u043e\u043b\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0434\u043e ffmpeg \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0440\u0435\u0448\u0438\u043b \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0438\u0437 \u043c\u043e\u0434\u0443\u043b\u044f services.py \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 views.py \u0434\u043b\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0430:<\/p>\n<pre><code class=\"python\">import base64from django.shortcuts import renderfrom .services import search, image, video, search_by_imagedef search_view(request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435    search_query = request.GET.get('query', '')    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421', search_query)    page = request.GET.get('page', 1)  # \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    try:        results = search(search_query, page)        len_results = len(results)        return render(request, 'search\/result.html',                          {'results': results, 'query': search_query, 'page': page_number, 'total_results': len_results})    except Exception:        return render(request, 'main\/site.html')def image_view(request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u043e\u0442\u043e    search_query = request.GET.get('query', '')    page = request.GET.get('page', 1)    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421 \u041a\u0410\u0420\u0422\u0418\u041d\u041a\u0418', search_query)    try:        page_number = int(page)    except ValueError:        page_number = 1    images = image(request, search_query)    return render(request, 'search\/images.html',                      {'images': images, 'query': search_query, 'page': page_number})def video_view(request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432\u0438\u0434\u0435\u043e    search_query = request.GET.get('query', '')    page = request.GET.get('page', 1)    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421 \u0412\u0418\u0414\u0415\u041e', search_query)    try:        page_number = int(page)    except ValueError:        page_number = 1    video_query = '\u0432\u0438\u0434\u0435\u043e' + search_query    images = image(request, video_query)    return render(request, 'search\/videos.html',                  {'images': images, 'query': search_query, 'page': page_number})def search_by_image_view(request):    img_file = request.FILES.get('image')    print('\u041f\u041e\u0418\u0421\u041a \u041f\u041e \u0418\u0417\u041e\u0411\u0420\u0410\u0416\u0415\u041d\u0418\u042e: ', img_file)    if img_file:        encoded_image = base64.b64encode(img_file.read()).decode('utf-8')    else:        encoded_image = request.session.get('encoded_image')    section = request.GET.get('section')    page = request.GET.get('page', 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    results = search_by_image(request, encoded_image)    return render(request, 'search\/result_image_search.html',                      {'results': results, 'query': img_file,                        'page': page_number, 'encoded_image': encoded_image, 'section': section})def news_view(request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435    query = request.GET.get('query', '')    print('\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421', query)    page = request.GET.get('page', 1)  # \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    news_query = '\u041d\u043e\u0432\u043e\u0441\u0442\u0438' + query    results = search(news_query, page)    len_results = len(results)    return render(request, 'search\/result.html',                      {'results': results, 'query': query, 'page': page_number, 'total_results': len_results})<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL, \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043a\u0443\u0449\u0430\u044f \u0441\u0435\u0441\u0441\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435 \u043f\u043e \u0444\u043e\u0442\u043e (\u0444\u0443\u043d\u043a\u0446\u0438\u044f search_by_image \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 services.py).<\/p>\n<h4>1.2 \u041c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440<\/h4>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7e3\/a5b\/a18\/7e3a5ba18c7950b2acac32198e54bbd1.png\" alt=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430\" title=\"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430\" width=\"1918\" height=\"897\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/7e3\/a5b\/a18\/7e3a5ba18c7950b2acac32198e54bbd1.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7e3\/a5b\/a18\/7e3a5ba18c7950b2acac32198e54bbd1.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u0414\u043b\u044f \u0431\u044d\u043a\u0435\u043d\u0434\u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 Mix\u0421hat \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d API \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Django REST Framework. \u041c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u0435 \u0442\u0438\u043f\u044b API \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432:<\/p>\n<ul>\n<li>\n<p>POST \u2013 \u0434\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0447\u0430\u0442\u043e\u0432 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>GET \u2013 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0447\u0430\u0442\u043e\u0432 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>PUT \u2013 \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439, \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u043b\u0438 \u0432 \u0433\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u0445 \u0447\u0430\u0442\u0430\u0445.<\/p>\n<\/li>\n<li>\n<p>DELETE \u2013 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>Django REST Framework \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0434\u043b\u044f ORM \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u0434\u043b\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0447\u0435\u0440\u0435\u0437 \u0430\u0434\u043c\u0438\u043d \u043f\u0430\u043d\u0435\u043b\u044c \u0438 \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438.<\/p>\n<p>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 ORM \u043c\u043e\u0434\u0435\u043b\u0438 (\u0444\u0430\u0439\u043b models.py):<\/p>\n<pre><code class=\"python\">import uuidfrom django.db import modelsfrom django.contrib.auth.models import AbstractUser# Create your models here.class ChatUser(AbstractUser):    # \u041a\u0430\u0441\u0442\u043e\u043c\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f    id = models.UUIDField(        primary_key=True,        default=uuid.uuid4,        editable=False)    email = models.EmailField(\"\u042d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0447\u0442\u0430\", unique=True)    phone = models.CharField(\"\u0422\u0435\u043b\u0435\u0444\u043e\u043d\")    country_code = models.CharField(\"\u041a\u043e\u0434 \u0441\u0442\u0440\u0430\u043d\u044b\")    code = models.IntegerField(\"\u041a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f\", default=0)    photo = models.ImageField(upload_to='photo\/profilephoto\/', default='photo\/profilephoto\/default.png', null=False, blank=True, verbose_name=\"\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f\")    date_birth = models.DateField(null=True, blank=True)    bio = models.CharField(null=True, blank=True)    fcm_token = models.CharField(null=True, blank=True)    USERNAME_FIELD = 'email'    REQUIRED_FIELDS = ['phone', 'country_code']    def __str__(self):        return self.usernameclass Bot(models.Model):    id = models.UUIDField(        primary_key=True,        default=uuid.uuid4,        editable=False)    name = models.TextField(\"\u0418\u043c\u044f\")    photo = models.ImageField(upload_to='photo\/profilephoto\/', default='photo\/profilephoto\/default.png', null=False,                              blank=True, verbose_name=\"\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f\")    def __str__(self):        return self.nameclass Chat(models.Model):    name = models.TextField(\"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0447\u0430\u0442\u0430\")    photo = models.ImageField(upload_to='photo\/profilephoto\/', default='photo\/profilephoto\/default.png', null=True,                              blank=True, verbose_name=\"\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u044f \u0447\u0430\u0442\u0430\")    bio = models.TextField(\"\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\")    type = models.TextField(\"\u0422\u0438\u043f\")class ChatMembership(models.Model):    user_id = models.UUIDField()    chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='id_chat')    user_role = models.TextField(\"\u0420\u043e\u043b\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\")    def __str__(self):        return f\"Membership of chat{self.chat}\"class Channel(models.Model):    id = models.AutoField(primary_key=True)    name = models.TextField(\"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\")class ChannelMembership(models.Model):    id = models.AutoField(primary_key=True)    user = models.ForeignKey(ChatUser, on_delete=models.CASCADE, related_name='id_user')    chanel = models.ForeignKey(Channel, on_delete=models.CASCADE, related_name='chanel_id')class Message(models.Model):    id = models.AutoField(primary_key=True)    sender_user = models.ForeignKey(ChatUser, verbose_name=\"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c\", null=True, blank=True, on_delete=models.CASCADE, related_name='sent_messages')    sender_bot = models.ForeignKey(Bot, verbose_name=\"\u0411\u043e\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c\", null=True, blank=True, on_delete=models.CASCADE, related_name='sent_messages')    content = models.TextField(\"\u0422\u0435\u043a\u0441\u0442\")    image = models.ImageField(\"\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\", upload_to='messages\/images\/', null=True, blank=True)    video = models.FileField(\"\u0412\u0438\u0434\u0435\u043e\", upload_to='messages\/videos\/', null=True, blank=True)    audio = models.FileField(\"\u0410\u0443\u0434\u0438\u043e\", upload_to='messages\/audios\/', null=True, blank=True)    timestamp = models.DateTimeField(\"\u0414\u0430\u0442\u0430\", auto_now_add =True)    chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='messages')    is_edit = models.BooleanField(\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e?\", null=True, blank=True)    delete_at_home = models.BooleanField(\"\u0423\u0434\u0430\u043b\u0435\u043d\u043e \u0443 \u0441\u0435\u0431\u044f?\", null=True, blank=True)    id_for_answer = models.IntegerField(\"\u041e\u0442\u0432\u0435\u0442\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\", null=True, blank=True)    id_for_transmission = models.IntegerField(\"\u041f\u0435\u0440\u0435\u0441\u043b\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (ID)\", null=True, blank=True)    is_forwarded = models.BooleanField(\"\u041f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0430?\", default=False)    transmission_content = models.TextField(\"\u041f\u0435\u0440\u0435\u0441\u043b\u0430\u043d\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\")    def __str__(self):        return f\"Message from {self.sender_user} to chat {self.chat.id}\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0411\u0414 PostrgreSQL. <\/p>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f (\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435) \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0432\u0432\u043e\u0434 \u043f\u043e\u0447\u0442\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043b\u043e\u0433\u0438\u043d\u0430 \u0438 \u043f\u0430\u0440\u043e\u043b\u044f. \u042d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u043d\u0438\u0445 \u0441\u043e\u0437\u0434\u0430\u043d \u0444\u0430\u0439\u043b serialazers.py.<\/p>\n<pre><code class=\"python\">def send_code_to_email(email, user_name, code):    send_mail(        f\"{user_name}, \u0432\u0430\u0448 \u043a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f\",        f\"Mixchat, \u041a\u043e\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f: {code}. \u041d\u0438\u043a\u043e\u043c\u0443 \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434!\",        EMAIL_HOST_USER,        [email]    )class UserSerializer(serializers.ModelSerializer):    password = serializers.CharField(max_length=128, min_length=8, write_only=True)    class Meta:        model = ChatUser        fields = ['email', 'username', 'password']    def create(self, validated_data):        email = validated_data[\"email\"]        validated_data[\"is_active\"] = False        code = random.randint(1000, 9999)        validated_data[\"code\"] = code        user = ChatUser.objects.create_user(**validated_data)        send_code_to_email(email, user.username, code)        return user<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u043d\u043e\u0441\u0438\u0442 \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u043e \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bee\/fdd\/e55\/beefdde55e354ad1da145681f1edf3b5.png\" width=\"779\" height=\"365\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/bee\/fdd\/e55\/beefdde55e354ad1da145681f1edf3b5.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bee\/fdd\/e55\/beefdde55e354ad1da145681f1edf3b5.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u0432\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u0448\u0451\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043d\u0430 \u0432\u0432\u0435\u0434\u0451\u043d\u043d\u0443\u044e \u043f\u043e\u0447\u0442\u0443.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9b\/cef\/45e\/a9bcef45e68e751baa5169254d0534d4.png\" width=\"779\" height=\"370\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a9b\/cef\/45e\/a9bcef45e68e751baa5169254d0534d4.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a9b\/cef\/45e\/a9bcef45e68e751baa5169254d0534d4.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u043e\u0434\u0438\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u0442\u043e \u043e\u043d \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c, \u0435\u0441\u043b\u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439, \u0442\u043e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c, \u0430 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0443\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u00a0\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u0444\u0430\u0439\u043b\u0435 views.py \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 auth_in_chat, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<pre><code class=\"python\">def auth_in_chat(request):    users = ChatUser.objects.filter(is_active=False).all()    if users:        for user in users:            update_date = datetime.now()            date_joined = user.date_joined + timedelta(0, 10800)            if update_date - date_joined.replace(tzinfo=None) &gt;= timedelta(0, 3600):                user.delete()    return render(request, 'main\/account.html')<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0435\u0441\u0442\u044c \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u043c \u0438 \u0432\u0432\u043e\u0434\u0438\u0442 \u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0442\u043e \u043e\u043d \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430, \u0438 \u0435\u043c\u0443 \u0432\u044b\u0434\u0430\u0451\u0442\u0441\u044f JWT \u0442\u043e\u043a\u0435\u043d. \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0438\u0437 \u044d\u0442\u0438\u0445 \u043f\u0443\u043d\u043a\u0442\u043e\u0432 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f, \u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u0432\u043e\u0439\u0442\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443. <\/p>\n<p>\u0414\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c HTML (\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u0430\u043d\u0438\u0446), CSS (\u0434\u043b\u044f \u0441\u0442\u0438\u043b\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u043e\u0432) \u0438 JavaScript (\u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430).<\/p>\n<p>\u0412 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435 \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439. \u041f\u0440\u043e \u043d\u0438\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435. \u0414\u043b\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0441\u0435\u0440\u0432\u0438\u0441 Google Firebase. \u041f\u043e\u0434 web \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 \u044f \u0441\u043e\u0437\u0434\u0430\u043b 2 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 (SDK) \u0434\u043b\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439: \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 SDK \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 SDK.<\/p>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 SDK \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Google Firebase, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f FCM \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0447\u0435\u0440\u0435\u0437 \u0442\u043e\u0442 \u0436\u0435 Firebase \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0435\u0433\u043e \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<pre><code class=\"javascript\">import { getMessaging, getToken, deleteToken } from \"https:\/\/www.gstatic.com\/firebasejs\/12.10.0\/firebase-messaging.js\";const messaging = getMessaging();getToken(messaging, {vapidKey: 'BISnUtSCVp9xdEpjULSIJVAmSSxDpZyjddQdR7NHlko2tAZNX7apnVL5feKslk1iS71cMQ8xJuH5_lx0O_Yx3UM'}).then((currentToken) =&gt; {  if (currentToken) {    fetch('\/api\/save_fcm\/', {       method: 'PUT',       headers: {           'Content-Type': 'application\/json',           'Authorization': 'Bearer ' + localStorage.getItem('access_token')       },       body: JSON.stringify({\"token\": currentToken})   })  } else {    console.log('No registration token available. Request permission to generate one.');  }}).catch((err) =&gt; {  console.log('An error occurred while retrieving token. ', err);});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 SDK \u2013 \u0442\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f FCM \u0442\u043e\u043a\u0435\u043d\u0430 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u044b\u043c FCM \u0442\u043e\u043a\u0435\u043d\u043e\u043c.<\/p>\n<pre><code class=\"python\">import osimport firebase_adminfrom firebase_admin import credentials, messagingfrom dotenv import load_dotenvfrom pathlib import Pathload_dotenv()BASE_DIR = Path(__file__).resolve().parent.parentcred_path = {            \"type\": \"service_account\",            \"project_id\": os.getenv('FIREBASE_PROJECT_ID'),            \"private_key\": os.getenv('FIREBASE_PRIVATE_KEY'),            \"client_email\": os.getenv('FIREBASE_CLIENT_EMAIL'),            \"private_key_id\": os.getenv('FIREBASE_PRIVATE_KEY_ID'),            \"client_id\": os.getenv('FIREBASE_CLIENT_ID'),            \"auth_uri\": os.getenv('FIREBASE_AUTH_URI'),            \"token_uri\": os.getenv('FIREBASE_TOKEN_URI'),            \"auth_provider_x509_cert_url\": os.getenv('FIREBASE_AUTH_PROVIDER_X509_CERT_URL'),            \"client_x509_cert_url\": os.getenv('FIREBASE_CLIENT_X509_CERT_URL'),            \"universe_domain\": os.getenv('FIREBASE_UNIVERSE_DOMAIN')        }try:    # \u041f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c Firebase    cred = credentials.Certificate(cred_path)    firebase_app = firebase_admin.initialize_app(cred)    app = firebase_admin.get_app()    print(\"Firebase Admin SDK \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d!\")except Exception as e:    print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Firebase: {e}\")    firebase_app = Nonedef send_push_notification(fcm_token, title, body, image, data=None):    # \u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 push-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 FCM    try:        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435        message = messaging.Message(            notification=messaging.Notification(                title=title,                body=body,                image=image            ),            data=data or {},            token=fcm_token,        )        # \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c        response = messaging.send(message)        print(\"\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e\")        return response    except Exception as e:        print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438: {e}\")        return None      <\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u044f \u0441\u043e\u0437\u0434\u0430\u043b Service Worker.<\/p>\n<pre><code class=\"javascript\">const CACHE_VERSION = 'v1.0.1';console.log(`[SW] Service Worker \u0432\u0435\u0440\u0441\u0438\u0438 ${CACHE_VERSION} \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d`);self.addEventListener('install', (event) =&gt; {  console.log('install event');  self.skipWaiting();});self.addEventListener('activate', (event) =&gt; {  console.log('activate event');  event.waitUntil(clients.claim());});self.addEventListener('message', event =&gt; {    if (event.data.action === 'getVersion') {        event.source.postMessage({ version: CACHE_VERSION });    }});self.addEventListener('push', function(event) {  console.log('push event \u043f\u043e\u043b\u0443\u0447\u0435\u043d!');  let data = {};  if (event.data) {    data = event.data.json();  }  const notification = data.notification  const options = {    body: notification.body,    icon: notification.image,    vibrate: [200, 100, 200, 100, 200, 100, 200],    tag: \"notification-\" + Date.now(),  }  let promise = self.registration.showNotification(notification.title, options);  event.waitUntil(promise);});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435 \u0435\u0441\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 \u0431\u043e\u0442 MixRobot \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0438\u0448\u0435\u0442 \u0432 \u0447\u0430\u0442 \u0441 \u0431\u043e\u0442\u043e\u043c \u043b\u044e\u0431\u043e\u0435 \u0441\u043b\u043e\u0432\u043e, \u0430 \u0431\u043e\u0442 \u0432\u044b\u0434\u0430\u0451\u0442 \u0435\u043c\u0443 \u043e\u0442\u0432\u0435\u0442\u044b \u0438\u0437 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430.<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8e6\/b73\/856\/8e6b73856cc41beeb174dec04ca985a8.png\" alt=\"\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430    \" title=\"\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430    \" width=\"780\" height=\"457\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/8e6\/b73\/856\/8e6b73856cc41beeb174dec04ca985a8.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/8e6\/b73\/856\/8e6b73856cc41beeb174dec04ca985a8.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430    <\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u043e\u0442 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u043f\u0443\u0441\u0442\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b. \u042f \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0435. \u0412 HTML \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u0441\u0441\u044b\u043b\u043a\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430. \u0420\u0430\u043d\u0435\u0435 \u044d\u0442\u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u0432\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u0438 \u0431\u044b\u043b\u043e \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043a\u0430\u043a\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u043b\u044f \u0431\u043e\u0442\u0430. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u0447\u0435\u0440\u0435\u0437 \u0431\u043e\u0442\u0430 \u0441\u0442\u0430\u043b\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0438 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0430\u043c.<\/p>\n<p>HTML \u0444\u0430\u0439\u043b \u0441 \u043e\u0442\u0432\u0435\u0442\u0430\u043c\u0438 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 (result.html):<\/p>\n<pre><code class=\"xml\">&lt;!DOCTYPE html&gt;&lt;html lang=\"ru\"&gt;&lt;head&gt;    &lt;meta charset=\"UTF-8\"&gt;    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;    &lt;title&gt;MixRech&lt;\/title&gt;    {% load static %}    &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.2\/dist\/css\/bootstrap.min.css\"&gt;    &lt;link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/@flaticon\/font-flaticon@1.0.0\/css\/font-flaticon.css\"&gt;    &lt;link rel=\"stylesheet\" href=\"{% static 'search\/css\/static.css' %}\"&gt;    &lt;link rel=\"icon\" href=\"{% static 'main\/png\/favicon.ico' %}\"&gt;&lt;\/head&gt;&lt;body&gt;    &lt;div class=\"header\"&gt;        &lt;div class=\"header-content\"&gt;            &lt;h2&gt;&lt;a href=\"{% url 'home' %}\" style=\"text-decoration: none;\"&gt;MixRech&lt;\/a&gt;&lt;\/h2&gt;            &lt;form method=\"get\" action=\"{% url 'search' %}\" class=\"search-form\"&gt;                &lt;div class=\"search-input-wrapper\"&gt;                    &lt;input type=\"text\" name=\"query\" value=\"{{ query }}\" placeholder=\"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438\u043b\u0438 URL\" aria-label=\"\u041f\u043e\u0438\u0441\u043a\"&gt;                &lt;\/div&gt;            &lt;\/form&gt;            &lt;div class=\"category-buttons\"&gt;                &lt;a href=\"{% url 'search' %}?query={{ query }}\" class=\"category-btn {% if request.path == '\/search' %}active{% endif %}\"&gt;                    \u0412\u0441\u0435                &lt;\/a&gt;                &lt;a href=\"{% url 'images' %}?query={{ query }}\" class=\"category-btn {% if request.path == '\/images' %}active{% endif %}\"&gt;                    \u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438                &lt;\/a&gt;                &lt;a href=\"{% url 'videos' %}?query={{ query }}\" class=\"category-btn {% if request.path == '\/videos' %}active{% endif %}\"&gt;                    \u0412\u0438\u0434\u0435\u043e                &lt;\/a&gt;                &lt;a href=\"{% url 'news' %}?query={{ query }}\" class=\"category-btn {% if request.path == '\/news' %}active{% endif %}\"&gt;                    \u041d\u043e\u0432\u043e\u0441\u0442\u0438                &lt;\/a&gt;            &lt;\/div&gt;        &lt;\/div&gt;    &lt;\/div&gt;    &lt;div class=\"results-container\"&gt;        {% if results %}            {% for result in results %}            &lt;div class=\"result-card\"&gt;                &lt;div class=\"result-title\"&gt;                    {% if result.favicon_url %}                    &lt;img src=\"{{ result.favicon_url }}\" alt=\"\" onerror=\"this.style.display='none'\"&gt;                    {% endif %}                    &lt;a href=\"{{ result.url }}\"&gt;{{ result.title }}&lt;\/a&gt;                &lt;\/div&gt;                &lt;div class=\"result-url\"&gt;                    &lt;a href=\"{{ result.url }}\"&gt;                        &lt;svg class=\"icon\" viewBox=\"0 0 24 24\" style=\"width: 14px; height: 14px;\"&gt;                            &lt;circle cx=\"12\" cy=\"12\" r=\"3\" stroke=\"currentColor\" fill=\"none\"\/&gt;                            &lt;path d=\"M19.4 15a8 8 0 00-14.8 0M5 9a8 8 0 0114 0\" stroke=\"currentColor\"\/&gt;                        &lt;\/svg&gt;                        {{ result.url|truncatechars:60 }}                    &lt;\/a&gt;                &lt;\/div&gt;            &lt;\/div&gt;            {% endfor %}        {% else %}            &lt;li&gt;\u041d\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.&lt;\/li&gt;        {% endif %}        &lt;div class=\"pagination\"&gt;            {% if page &gt; 1 %}                &lt;a href=\"?query={{ query }}&amp;page={{ page|add:-1 }}\"&gt;                    &lt;svg class=\"icon\" viewBox=\"0 0 24 24\" style=\"width: 18px; height: 18px;\"&gt;                        &lt;path d=\"M19 12H5M12 19l-7-7 7-7\" stroke=\"currentColor\" stroke-width=\"2\"\/&gt;                    &lt;\/svg&gt;                    \u041f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0430\u044f                &lt;\/a&gt;                {% endif %}                &lt;span class=\"page-info\"&gt;\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 {{ page }}&lt;\/span&gt;                &lt;a href=\"?query={{ query }}&amp;page={{ page|add:1 }}\"&gt;                    \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f                    &lt;svg class=\"icon\" viewBox=\"0 0 24 24\" style=\"width: 18px; height: 18px;\"&gt;                        &lt;path d=\"M5 12h14M12 5l7 7-7 7\" stroke=\"currentColor\" stroke-width=\"2\"\/&gt;                    &lt;\/svg&gt;                &lt;\/a&gt;        &lt;\/div&gt;    &lt;\/div&gt;&lt;\/body&gt;&lt;\/html&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u043e\u0442\u043e\u043c MixRobot &#8212; microservice_functions.py:<\/p>\n<pre><code class=\"python\">import jsonimport requestsfrom bs4 import BeautifulSoupdef mixrech(query):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439    url = f'https:\/\/mixrech.com\/search\/?query={query}'    response = requests.get(url)    if response.status_code == 200:        print(\"\u0423\u0441\u043f\u0435\u0448\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\")        text_html = response.text        soup = BeautifulSoup(text_html, 'html.parser')  # \u041f\u0430\u0440\u0441\u0438\u043c HTML-\u043a\u043e\u0434        links = [item.a for item in soup.find_all('div', class_='result-url')]        titles = [item.text.strip() for item in soup.find_all('div', class_='result-title')]        hrefs = [item.get('href') for item in links]        #print(titles)        #print(hrefs)        return json.dumps(dict(zip(titles, hrefs)), ensure_ascii=False)    else:        print(f\"\u041e\u0448\u0438\u0431\u043a\u0430: {response.status_code}\")        print(response.text)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>2. \u0421\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h3>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a2f\/d0d\/1ad\/a2fd0d1ada2f9bbe36f0266ea09fa025.png\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u043c\u043e\u0434\u0435\u043b\u044c &quot;\u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440&quot;\" title=\"\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u043c\u043e\u0434\u0435\u043b\u044c &quot;\u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440&quot;\" width=\"970\" height=\"545\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a2f\/d0d\/1ad\/a2fd0d1ada2f9bbe36f0266ea09fa025.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a2f\/d0d\/1ad\/a2fd0d1ada2f9bbe36f0266ea09fa025.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u043c\u043e\u0434\u0435\u043b\u044c &#171;\u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440&#187;<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0430 \u0441\u0445\u0435\u043c\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u043c\u043e\u0434\u0435\u043b\u044c &#171;\u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440&#187;. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 web \u0441\u0435\u0440\u0432\u0435\u0440 Nginx. \u0422\u0430\u043a\u0436\u0435 \u043a\u0430\u0436\u0434\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0441 \u043d\u0438\u043c \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0414\u043b\u044f \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043e\u043c\u0435\u0449\u0435\u043d\u044b \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 (\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0443\u044e \u043c\u0430\u0448\u0438\u043d\u0443) \u043e\u0442 TimeWeb Cloud \u043d\u0430 \u0431\u0430\u0437\u0435 Ubuntu.<\/p>\n<p>\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker-Compose, \u0432 Docker \u0444\u0430\u0439\u043b\u044b \u043f\u043e\u043c\u0435\u0449\u0435\u043d\u044b \u043e\u0431\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 Nginx.<\/p>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c \u043f\u043e HTTP \u0438 \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u0434\u043e\u0432\u0435\u0440\u0438\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u0441 \u0434\u043e\u043c\u0435\u043d\u0430 (<a href=\"http:\/\/mixrech.com\" rel=\"noopener noreferrer nofollow\">mixrech.com<\/a>), \u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0441 \u043f\u043e\u0434\u0434\u043e\u043c\u0435\u043d\u0430 (<a href=\"http:\/\/chat.mixrech.com\" rel=\"noopener noreferrer nofollow\">chat.mixrech.com<\/a>), \u0430 \u0442\u0430\u043a\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u044b DNS \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438 \u0434\u043e\u043c\u0435\u043d\u043e\u0432 \u0441 IP \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0437\u0430\u0449\u0438\u0442\u0438\u0442\u044c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0442 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d SSL wildcard \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442 \u0434\u043b\u044f \u0434\u043e\u043c\u0435\u043d\u0430 \u0438 \u043f\u043e\u0434\u0434\u043e\u043c\u0435\u043d\u0430, \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043f\u043e HTTPS.<\/p>\n<p>Nginx \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430:<\/p>\n<ul>\n<li>\n<p>\u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e\u0442\u00a0\u0434\u043e\u043c\u0435\u043d\u043e\u0432,<\/p>\n<\/li>\n<li>\n<p>SSL \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0438 SSL \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u0435,<\/p>\n<\/li>\n<li>\n<p>\u0440\u0430\u0437\u0434\u0430\u0447\u0443 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0438 \u043c\u0435\u0434\u0438\u0430 \u0444\u0430\u0439\u043b\u043e\u0432,<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0432\u00a0Nginx \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d \u0440\u0430\u0437\u043c\u0435\u0440 \u0432\u0438\u0434\u0435\u043e\u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u043f\u0440\u043e\u043a\u0441\u0438. <\/p>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d MQTT \u0431\u0440\u043e\u043a\u0435\u0440 mosquitto.<\/p>\n<h4>2.1 CI\/CD<\/h4>\n<p>\u041a\u043e\u0434\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u043b\u0435\u0436\u0430\u0442 \u0432 GitHub \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0431\u043e\u0440\u043a\u0438 \u0438 \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u0451\u043d CI\/CD. \u041f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0438\/\u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u043d\u0430 Github, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 Github Actions, \u0433\u0434\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u043e \u0434\u0435\u043f\u043b\u043e\u044e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0421\u0430\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 CI\/CD \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e .github\/workflows\/action.yml<\/p>\n<pre><code class=\"yaml\">name: Deploy to TimeWeb Cloudon:   push:      branches:         - mainjobs:     deploy:       runs-on: ubuntu-latest       steps:       - name: Checkout code         uses: actions\/checkout@v2       - name: Debug         run: echo \"SSH_PRIVATE_KEY=${{ secrets.SSH_PRIVATE_KEY }}\"       - name: Set up SSH         uses: webfactory\/ssh-agent@v0.9.0         with:           ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}       - name: Add server to known_hosts         run: |           mkdir -p ~\/.ssh           ssh-keyscan -H ${{ secrets.IP_ADRESS }} &gt;&gt; ~\/.ssh\/known_hosts       - name: Add SSH key to agent         run: echo \"${{ secrets.SSH_PRIVATE_KEY }}\" | ssh-add -       - name: Deploy to server         run: |           ssh ${{ secrets.USERNAME }}@${{ secrets.IP_ADRESS }} \"cd \/root\/browser &amp;&amp; git pull &amp;&amp; docker-compose down &amp;&amp; docker-compose up --build -d &amp;&amp; docker cp browser_mixrech_1:\/mixrech\/staticfiles .\/mixrech &amp;&amp; docker cp browser_mixchat_1:\/mixchat\/staticfiles .\/mixchat\"                logs:       runs-on: ubuntu-latest       needs: deploy              steps:       - name: Set up SSH         uses: webfactory\/ssh-agent@v0.9.0         with:           ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}       - name: Add server to known_hosts         run: |           mkdir -p ~\/.ssh           ssh-keyscan -H ${{ secrets.IP_ADRESS }} &gt;&gt; ~\/.ssh\/known_hosts                 - name: Fetch logs         run: |           ssh ${{ secrets.USERNAME }}@${{ secrets.IP_ADRESS }} \"cd \/root\/browser &amp;&amp; git pull &amp;&amp; docker-compose logs -f\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b 2 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<p>&#8212; \u0421\u0430\u043c \u0434\u0435\u043f\u043b\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>&#8212; \u0412\u044b\u0432\u043e\u0434 \u043b\u043e\u0433\u043e\u0432.<\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u043d\u0435\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u043b JavaScript, \u043d\u0435\u00a0\u0437\u0430\u0433\u0440\u0443\u0436\u0430\u043b\u0438\u0441\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0438 \u0441\u0442\u0438\u043b\u0438 \u043d\u0435\u00a0\u0431\u044b\u043b\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b. \u042f \u043f\u043e\u043d\u044f\u043b, \u0447\u0442\u043e\u00a0\u0432\u043e\u00a0\u0432\u0440\u0435\u043c\u044f \u0434\u0435\u043f\u043b\u043e\u044f \u043d\u0435\u00a0\u0441\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0444\u0430\u0439\u043b\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0438\u00a0\u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u0432\u00a0actions.yml \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438\u0437\u00a0\u0445\u043e\u0441\u0442\u0430 \u0432\u00a0\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0447\u0442\u043e\u00a0\u0440\u0435\u0448\u0438\u043b\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<\/p>\n<p>Github \u0441\u0432\u044f\u0437\u0430\u043d \u0441\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0447\u0435\u0440\u0435\u0437 SSH \u043a\u043b\u044e\u0447. \u042d\u0442\u0430\u043f\u044b \u0434\u0435\u043f\u043b\u043e\u044f: <\/p>\n<ul>\n<li>\n<p>Github \u043f\u044b\u0442\u0430\u0435\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u0430\u0442\u044c\u0441\u044f \u0441\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0447\u0435\u0440\u0435\u0437 SSH \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043a\u00a0\u043d\u0435\u043c\u0443. <\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u00a0\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0447\u0435\u0440\u0435\u0437 git pull, \u0437\u0430\u0442\u0435\u043c \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u0441\u0431\u043e\u0440\u043a\u0430 docker \u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u0438 \u0437\u0430\u043f\u0443\u0441\u043a \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430, \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0438 Nginx. <\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u00a0\u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435. <\/p>\n<\/li>\n<\/ul>\n<h3>3. IoT \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438<\/h3>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ee8\/7aa\/e68\/ee87aae684640d7706d0ebcc632acdc7.png\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c\" title=\"\u0421\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c\" width=\"780\" height=\"540\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/ee8\/7aa\/e68\/ee87aae684640d7706d0ebcc632acdc7.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ee8\/7aa\/e68\/ee87aae684640d7706d0ebcc632acdc7.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0445\u0435\u043c\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c<\/figcaption><\/div>\n<\/figure>\n<p>\u0417\u0434\u0435\u0441\u044c \u043f\u043e\u0439\u0434\u0451\u0442 \u0440\u0435\u0447\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c.<\/p>\n<p>\u041d\u0430 \u0441\u0445\u0435\u043c\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c ESP32. \u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u043e\u0442\u0430 SmartMix. \u0421\u0432\u044f\u0437\u0443\u044e\u0449\u0435\u043c \u0443\u0437\u043b\u043e\u043c \u043c\u0435\u0436\u0434\u0443 ESP32 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u043e\u043c \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 MQTT \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0449\u0451\u043d \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u0435. \u041d\u0430 ESP32 \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0434\u0430\u0442\u0447\u0438\u043a \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b DS18B20. \u042d\u0442\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0431\u043e\u0442\u0430 \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435 \u043f\u043e MQTT.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u044f \u0432\u044b\u0431\u0440\u0430\u043b ESP32, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0434\u0440\u0430\u0439\u0432\u0435\u0440 \u0438 \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443. <\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u0441\u0440\u0435\u0434\u0443 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 PyCharm \u0434\u043b\u044f MicroPython.<\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b \u0434\u0430\u0442\u0447\u0438\u043a \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b DS18B20 \u043a ESP32. \u041d\u0430\u043f\u0438\u0441\u0430\u043b \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430, \u0433\u0434\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434\u0430 \u0438 \u0441\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u044b. \u0414\u0430\u043d\u043d\u044b\u0435 \u0441 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 OneWire , \u0430 \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 GPIO.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043d\u0430 \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443:<\/p>\n<ul>\n<li>\n<p>\u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b \u043f\u0440\u043e\u0448\u0438\u0432\u043a\u0443 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u043d\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438,<\/p>\n<\/li>\n<li>\n<p>\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Wi-Fi \u0441\u0435\u0442\u0438 \u043f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b,<\/p>\n<\/li>\n<li>\n<p>\u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u043b \u0440\u0430\u0431\u043e\u0442\u0443 \u043e\u0442 \u0432\u043d\u0435\u0448\u043d\u0435\u0433\u043e \u0431\u043b\u043e\u043a\u0430 \u043f\u0438\u0442\u0430\u043d\u0438\u044f (\u0431\u0435\u0437 USB-\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u041f\u041a).<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0431\u044b\u043b\u0430 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f ESP32 \u0441 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u043e\u043c. \u0414\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0431\u043e\u0442 SmartMix. \u0421 \u043d\u0435\u0433\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043d\u0430 MQTT \u0441\u0435\u0440\u0432\u0435\u0440, \u043e\u0442\u043a\u0443\u0434\u0430 ESP32 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0441\u043e SmartMix \u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434\u0430\/\u0441\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0434\u0430\u0442\u0447\u0438\u043a\u0430. \u0414\u0430\u043d\u043d\u044b\u0435 \u0441 \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u044e\u0442 \u043d\u0430 MQTT, \u043e\u0442\u043a\u0443\u0434\u0430 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 PostgreSQL \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0435\u0451 \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. <\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043d\u0435 \u043c\u043e\u0433 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e, \u0438 \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u043a\u043e\u043c\u0430\u043d\u0434 \u0441 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u043b\u043e. \u042f \u043f\u043e\u043d\u044f\u043b \u0438 \u043f\u0440\u0438\u0448\u0451\u043b \u043a \u0432\u044b\u0432\u043e\u0434\u0443, \u0447\u0442\u043e \u043c\u043d\u043e\u0433\u0438\u0435 Wi-FI \u0440\u043e\u0443\u0442\u0435\u0440\u044b \u0438\u043c\u0435\u044e\u0442 2 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u042f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0441\u044f \u043a \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u043e\u0447\u043a\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0441 \u043c\u0435\u043d\u044c\u0448\u0438\u043c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u043e\u043c \u0447\u0430\u0441\u0442\u043e\u0442. \u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e, \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0438 \u043d\u0430\u0447\u0430\u043b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434 \u0438 \u0434\u0430\u0442\u0447\u0438\u043a.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043e\u0437\u0434\u0430\u043d \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u043c\u0430\u043a\u0435\u0442\u0430 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441 \u043c\u0430\u043a\u0435\u0442\u043e\u043c \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430.<\/p>\n<h2>SEO \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f<\/h2>\n<p>\u0414\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 SEO \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0432 Google Search Console \u0438 \u042f\u043d\u0434\u0435\u043a\u0441 \u041c\u0435\u0442\u0440\u0438\u043a\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 \u0432\u0435\u0440\u0445\u043d\u044e\u044e \u0441\u0442\u0440\u043e\u0447\u043a\u0443 \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430\u0445 \u043f\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u043c \u0441\u043b\u043e\u0432\u0430\u043c.<\/p>\n<h2>\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438<\/h2>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u043b\u044f Android.<\/p>\n<p>\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0438\u0441\u044c \u0432 Android Studio \u043d\u0430 \u044f\u0437\u044b\u043a\u0435 Java. <\/p>\n<p>\u0414\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043e\u0431\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 API \u0432\u044d\u0431 \u0432\u0435\u0440\u0441\u0438\u0439, \u0430 \u0434\u0438\u0437\u0430\u0439\u043d \u0441\u043e\u0437\u0434\u0430\u043d \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u043f\u043e\u0434 \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435 \u0435\u0449\u0451 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u044b\u0445 \u0442\u0435\u043c \u0438 \u0438\u043a\u043e\u043d\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0430. \u041c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043c\u0435\u044e\u0442 \u0442\u043e\u0442 \u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u0447\u0442\u043e \u0438 \u0438\u0445 \u0432\u044d\u0431 \u0432\u0435\u0440\u0441\u0438\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u044d\u043a\u0441\u043a\u043b\u044e\u0437\u0438\u0432\u043d\u043e \u0434\u043b\u044f Android \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0435\u043e\u043a\u0440\u0443\u0436\u043a\u0438.<\/p>\n<p>\u041a\u0430\u0441\u0430\u0435\u043c\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u0442\u043e \u0431\u044b\u043b \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d Google Firebase, \u043d\u043e \u0434\u043b\u044f Android \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f\u043c\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0445 SDK. \u041f\u0440\u0438\u043d\u0446\u0438\u043f \u0440\u0430\u0431\u043e\u0442\u044b SDK \u0442\u0430\u043a\u043e\u0439 \u0436\u0435, \u043a\u0430\u043a \u0438 \u043d\u0430 web \u0432\u0435\u0440\u0441\u0438\u044f\u0445.<\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u0440\u0430\u0437\u043d\u043e\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044f\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0438 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u043a\u0430\u043a \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0443\u043c\u044f \u0432\u044d\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c\u0438, \u0442\u0430\u043a \u0438 \u043c\u0435\u0436\u0434\u0443 \u0432\u044d\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u0438 \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c.<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043b\u0430\u043c\u0438 \u0434\u0432\u0443\u0445 \u044d\u043d\u0442\u0443\u0437\u0438\u0430\u0441\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e \u043c\u0435\u0440\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u043e\u0431\u0443\u0447\u0430\u044e\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u043c \u043d\u0430\u0432\u044b\u043a\u0430\u043c \u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0430\u0441\u043f\u0435\u043a\u0442\u0430\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p><a href=\"https:\/\/github.com\/Mik-23\/browser.git\" rel=\"noopener noreferrer nofollow\"><strong>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 Github \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/strong><\/a><\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1030480\/\">https:\/\/habr.com\/ru\/articles\/1030480\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u0435\u042f \u041c\u0438\u0445\u0430\u0438\u043b\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044c \u0438 \u0433\u043b\u0430\u0432\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432\u044d\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0412\u0442\u043e\u0440\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u00a0\u2014 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u00a0\u2014 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u0439 \u0438 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u00a0SEO \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044e.\u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b:\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0430\u044f \u0432\u00a0\u0441\u0435\u0431\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u0438\u0441\u043a, \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u00a0\u0444\u043e\u0442\u043e \u0438 \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u00a0\u043e\u0431\u044b\u0447\u043d\u043e\u043c\u0443 \u0442\u0435\u043a\u0441\u0442\u0443.\u041c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u0441\u00a0\u0434\u0432\u0443\u043c\u044f \u0431\u043e\u0442\u0430\u043c\u0438: \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0449\u0430\u0435\u0442\u0441\u044f \u0441\u00a0\u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u043e\u043c, \u0430\u00a0\u0432\u0442\u043e\u0440\u043e\u0439\u00a0\u2014 \u0441\u00a0\u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c (\u0441\u043c. IoT \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438).\u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438\u042d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a \u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b \u043d\u0430 3 \u0447\u0430\u0441\u0442\u0438: \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438, IoT \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u0434\u043b\u044f \u043c\u0438\u043a\u0440\u043e\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.1. \u0422\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439\u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0438 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043e\u043d\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0431\u043e\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u043e\u0442\u0430 \u0432 \u043c\u0435\u0441\u0441\u0435\u043d\u0434\u0436\u0435\u0440\u0435, \u0438 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0434\u0440\u0443\u0433\u043e\u0433\u043e.1.1 \u041f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430\u0411\u044d\u043a\u0435\u043d\u0434 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Django \u043d\u0430 \u044f\u0437\u044b\u043a\u0435 python \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 API \u0437\u0430\u043f\u0440\u043e\u0441\u044b (POST \u0438 GET). \u0412 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u043b\u0435\u0436\u0438\u0442 Yandex API Search, \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0434\u043e\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u043e\u0441\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 Yandex Speech Kit \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u0430 \u0432 \u0442\u0435\u043a\u0441\u0442.Django \u0432\u044b\u0431\u0440\u0430\u043d, \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043e\u0431\u0449\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0422\u0430\u043a, \u043f\u043e\u0447\u0442\u0438 \u0432\u0435\u0441\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0438 \u0433\u043e\u043b\u043e\u0441\u0430, \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0431\u044d\u043a\u0435, \u0438 \u0447\u0435\u0440\u0435\u0437 Django \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u043d\u0434\u0435\u0440, \u0430 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 \u043e\u0442\u0432\u0435\u0442\u044b \u0438\u0437 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e \u0433\u0438\u043f\u0435\u0440\u0441\u0441\u044b\u043b\u043a\u0430\u043c.\u0412 \u043c\u043e\u0434\u0443\u043b\u0435 service.py \u044f \u043e\u043f\u0438\u0441\u0430\u043b \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0417\u0434\u0435\u0441\u044c \u0435\u0441\u0442\u044c: \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u0412\u0441\u0435\u00bb, \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438\u00bb,\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b \u00ab\u0412\u0438\u0434\u0435\u043e\u00bb,\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u0444\u043e\u0442\u043e,\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0430\u0443\u0434\u0438\u043e\u0444\u0430\u0439\u043b\u0430\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430import osfrom dotenv import load_dotenvimport base64import timeimport loggingimport requestsimport subprocessfrom django.http import HttpResponsefrom bs4 import BeautifulSoupimport xml.etree.ElementTree as ETload_dotenv()logging.basicConfig(level=logging.INFO, format=&#8217;%(asctime)s &#8212; %(levelname)s &#8212; %(message)s&#8217;)def search(search_query, page):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435    folderid = os.getenv(&#8216;FOLDERID&#8217;)    api_key = os.getenv(&#8216;API_KEY&#8217;)    print(&#8216;\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421&#8217;, search_query)    print(&#8216;\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u043e\u043c\u0435\u0440: &#8216;, page)    try:        page_number = int(page)    except ValueError:        page_number = 1    results = []    url = &#8216;https:\/\/searchapi.api.cloud.yandex.net\/v2\/web\/search&#8217;    headers = {&#171;Authorization&#187;: f&#187;Api-Key {api_key}&#187;}    body = {        &#171;query&#187;: {          &#171;searchType&#187;: &#171;SEARCH_TYPE_COM&#187;,          &#171;queryText&#187;: search_query,          &#171;familyMode&#187;: &#171;FAMILY_MODE_NONE&#187;,          &#171;page&#187;: page_number,          &#171;fixTypoMode&#187;: &#171;FIX_TYPO_MODE_ON&#187;        },        &#171;folderId&#187;: folderid,        &#171;userAgent&#187;: &#171;Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/141.0.0.0 Safari\/537.36&#187;,        &#171;responseFormat&#187;: &#171;FORMAT_XML&#187;,    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        start_time = time.time()        encode_response = response.json()[&#171;rawData&#187;]        # \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0437 base64        decoded_bytes = base64.b64decode(encode_response)        # \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0431\u0430\u0439\u0442\u044b \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 (UTF-8)        xml_data = decoded_bytes.decode(&#8216;utf-8&#8217;)        # \u041f\u0430\u0440\u0441\u0438\u043c XML        root = ET.fromstring(xml_data)        # \u0418\u0449\u0435\u043c \u0432\u0441\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b        for doc in root.findall(&#8216;.\/\/doc&#8217;):            url_elem = doc.find(&#8216;url&#8217;)            title_elem = doc.find(&#8216;title&#8217;)            domain = doc.find(&#8216;domain&#8217;)            header = &#187;.join([title for title in title_elem.itertext()])            if &#8216;\u0423\u043a\u0440\u0430\u0438\u043d&#8217; in header or &#8216;\u0443\u043a\u0440\u0430\u0438\u043d&#8217; in header:                continue            else:                results.append({                    &#8216;url&#8217;: url_elem.text if url_elem is not None else &#187;,                    &#8216;title&#8217;: header if header is not None else &#187;,                    &#8216;favicon_url&#8217;: f&#8217;https:\/\/{domain.text}\/favicon.ico&#8217;                })        return results    else:        print(f&#187;Error: {response.status_code} &#8212; {response.text}&#187;)        return HttpResponse(&#171;Error occurred&#187;, status=response.status_code)def image(service_request, search_query):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u043e\u0442\u043e    folderid = os.getenv(&#8216;FOLDERID&#8217;)    api_key = os.getenv(&#8216;API_KEY&#8217;)    page = service_request.GET.get(&#8216;page&#8217;, 1)    print(&#8216;\u041f\u041e\u0418\u0421\u041a\u041e\u0412\u042b\u0419 \u0417\u0410\u041f\u0420\u041e\u0421 \u041a\u0410\u0420\u0422\u0418\u041d\u041a\u0418&#8217;, search_query)    try:        page_number = int(page)    except ValueError:        page_number = 1    images = []    url = &#8216;https:\/\/searchapi.api.cloud.yandex.net\/v2\/image\/search&#8217;    headers = {&#171;Authorization&#187;: f&#187;Api-Key {api_key}&#187;}    body = {        &#171;query&#187;: {            &#171;searchType&#187;: &#171;SEARCH_TYPE_COM&#187;,            &#171;queryText&#187;: search_query,            &#171;familyMode&#187;: &#171;FAMILY_MODE_NONE&#187;,            &#171;page&#187;: page_number,            &#171;fixTypoMode&#187;: &#171;FIX_TYPO_MODE_ON&#187;        },        &#171;folderId&#187;: folderid,        &#171;userAgent&#187;: &#171;Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/141.0.0.0 Safari\/537.36&#187;,    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        encode_response = response.json()[&#171;rawData&#187;]        # \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c \u0438\u0437 base64        decoded_bytes = base64.b64decode(encode_response)        # \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0431\u0430\u0439\u0442\u044b \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 (UTF-8)        text_xml = decoded_bytes.decode(&#8216;utf-8&#8217;)        soup = BeautifulSoup(text_xml, &#8216;lxml&#8217;)  # \u041f\u0430\u0440\u0441\u0438\u043c HTML-\u043a\u043e\u0434        # \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439        for item in soup.find_all(&#8216;doc&#8217;):            img_url = item.find(&#8216;image-link&#8217;).text if item.find(&#8216;image-link&#8217;) else None            link_url = item.find(&#8216;html-link&#8217;).text if item.find(&#8216;html-link&#8217;) else None            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438 \u0441\u0430\u0439\u0442 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c            if img_url and link_url:                images.append({&#8216;url&#8217;: img_url, &#8216;link&#8217;: link_url})        return images    else:        print(f&#187;Error: {response.status_code} &#8212; {response.text}&#187;)        return HttpResponse(&#171;Error occurred&#187;, status=response.status_code)def video(service_request):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0432\u0438\u0434\u0435\u043e    api_key = os.getenv(&#8216;OTHER_API_KEY&#8217;)    search_engine_id = os.getenv(&#8216;SEARCH_ENGINE_ID&#8217;)    search_query = service_request.GET.get(&#8216;query&#8217;, &#187;)    page = service_request.GET.get(&#8216;page&#8217;, 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    url = f&#8217;https:\/\/customsearch.googleapis.com\/customsearch\/v1\/?q=\u0432\u0438\u0434\u0435\u043e {search_query}&amp;page={page_number}&amp;cx={search_engine_id}&amp;key={api_key}&#8217;    headers = {&#171;Authorization&#187;: f&#187;Api-Key {api_key}&#187;}    response = requests.get(url, headers=headers)    print(page)    if response.status_code == 200:        all_data = []        items = response.json()[&#171;items&#187;]        for item in items:            try:                thumbnail = item[&#171;pagemap&#187;][&#171;cse_thumbnail&#187;][0][&#171;src&#187;]            except KeyError:                thumbnail = None            all_data.append({&#8216;url&#8217;: item[&#171;link&#187;], &#8216;title&#8217;:item[&#171;title&#187;], &#8216;thumbnail&#8217;: thumbnail})        return all_data    else:        print(f&#187;Error: {response.status_code} &#8212; {response.text}&#187;)        return HttpResponse(&#171;Error occurred&#187;, status=response.status_code)def search_by_image(service_request, encoded_image):    # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e \u0444\u043e\u0442\u043e     folderid = os.getenv(&#8216;FOLDERID&#8217;)    api_key = os.getenv(&#8216;API_KEY&#8217;)    section = service_request.GET.get(&#8216;section&#8217;)    page = service_request.GET.get(&#8216;page&#8217;, 1)    try:        page_number = int(page)    except ValueError:        page_number = 1    results = []    url = &#8216;https:\/\/searchapi.api.cloud.yandex.net\/v2\/image\/search_by_image&#8217;    headers = {&#171;Authorization&#187;: f&#187;Api-Key {api_key}&#187;}    body = {        &#171;folderId&#187;: folderid,        &#171;data&#187;: encoded_image,        &#171;page&#187;: page_number    }    response = requests.post(url, headers=headers, json=body)    if response.status_code == 200:        images = response.json()[&#171;images&#187;]        for image in images:            if section == None:                results.append({&#8216;link&#8217;: image[&#8216;url&#8217;], &#8216;title&#8217;: image[&#8216;pageTitle&#8217;], &#8216;url&#8217;: image[&#8216;pageUrl&#8217;]})            elif section == &#8216;\u041f\u043e\u0445\u043e\u0436\u0435\u0435&#8217;:                results.append({&#8216;link&#8217;: image[&#8216;url&#8217;]})            elif section == &#8216;\u0421\u0430\u0439\u0442\u044b&#8217;:                results.append({&#8216;title&#8217;: image[&#8216;pageTitle&#8217;], &#8216;url&#8217;: image[&#8216;pageUrl&#8217;]})        service_request.session[&#8216;encoded_image&#8217;] = encoded_image        return results    else:        print(f&#187;Error: {response.status_code} &#8212; {response.text}&#187;)        return HttpResponse(&#171;Error occurred&#187;, status=response.status_code)def convert_webm_to_ogg(input_path: str, output_path: str):     # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0430\u0443\u0434\u0438\u043e\u0444\u0430\u0439\u043b\u0430 webm \u0432 ogg    ffmpeg_path = &#8216;\/usr\/bin\/ffmpeg&#8217;    print(&#8216;\u0412\u044b\u0432\u0435\u0441\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443&#8217;)    command = [        ffmpeg_path,        &#8216;-y&#8217;,        &#8216;-i&#8217;, input_path,        &#8216;-c:a&#8217;, &#8216;libopus&#8217;,        output_path    ]    print(&#8216;\u041d\u0430\u0447\u0430\u043b\u043e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438&#8217;)    try:        print(&#8216;\u041f\u043e\u043f\u044b\u0442\u043a\u0430&#8217;)        subprocess.run(command, check=True)        print(f&#187;\u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430: {output_path}&#187;)    except subprocess.CalledProcessError as e:        print(f&#187;\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438: {e.stderr.decode()}&#187;)def voice_search(file):    # \u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u0438\u0441\u043a    folderid = os.getenv(&#8216;FOLDERID&#8217;)    api_key = os.getenv(&#8216;API_KEY&#8217;)    url = f&#8217;https:\/\/stt.api.cloud.yandex.net\/speech\/v1\/stt:recognize?topic=general&amp;folderId={folderid}&#8217;    headers = {        &#171;Authorization&#187;: f&#187;Api-Key {api_key}&#187;,        &#171;Content-Type&#187;: &#171;audio\/ogg&#187;    }    file.seek(0)    data = file.read()    input_webm = &#8216;audio.webm&#8217;    # \u0417\u0430\u043f\u0438\u0441\u044c \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u0439 \u0444\u0430\u0439\u043b    with open(input_webm, &#8216;wb&#8217;) as f_out:        f_out.write(data)    convert_webm_to_ogg(input_webm, &#8216;audio.ogg&#8217;)    print(&#8216;\u0423\u0441\u043f\u0435\u0448\u043d\u043e&#8217;)    with open(&#8216;audio.ogg&#8217;, &#8216;rb&#8217;) as f:        ogg_data = f.read()    response = requests.post(url, headers=headers, data=ogg_data)    try:        return response.json()    except Exception as e:        print(f&#187;\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0440\u0430\u0437\u0431\u043e\u0440\u0435 \u043e\u0442\u0432\u0435\u0442\u0430: {e}&#187;)        return None\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0445\u043e\u0447\u0443 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0433\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u043c \u043f\u043e\u0438\u0441\u043a\u0435.\u0417\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0432 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JavaScript \u0437\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0438 \u0441\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0440\u0435\u0447\u044c \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0444\u0430\u0439\u043b \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u201cwebm\u201d (\u0444\u0430\u0439\u043b&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-478248","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/478248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=478248"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/478248\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=478248"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=478248"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=478248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}