{"id":467333,"date":"2025-07-17T21:01:34","date_gmt":"2025-07-17T21:01:34","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=467333"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=467333","title":{"rendered":"<span>\u041e\u0441\u043d\u043e\u0432\u044b ETL \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Superset, Airflow \u0438 ClickHouse<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0432\u044f\u0437\u043a\u0443 Superset, Airflow \u0438 ClickHouse. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u044f \u0432\u0437\u044f\u043b <a href=\"https:\/\/beget.com\/ru\/cloud\/marketplace\/supersetairflow\" rel=\"noopener noreferrer nofollow\">\u0433\u043e\u0442\u043e\u0432\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043e\u0442 Beget, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0443\u044e Superset \u0438 Airflow \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438<\/a> \u2014 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u0442\u043e\u0432\u0430\u0440\u0430\u0445 \u0441 \u0441\u0430\u0439\u0442\u0430 Wildberries.<\/p>\n<p>\u0414\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Python-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>selenium<\/code> \u0438 <code>BeautifulSoup<\/code> \u2014 \u043e\u043d\u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c <code>re<\/code> \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.<\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 ETL<\/h2>\n<p>ETL (Extract \u2014 Transform \u2014 Load) \u2014 \u044d\u0442\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0442\u0440\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<ul>\n<li>\n<p><strong>\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 (Extract):<\/strong> \u043d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u2014 \u0431\u0443\u0434\u044c \u0442\u043e \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, API, \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438\u043b\u0438 \u0444\u0430\u0439\u043b\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 (Transform):<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0447\u0438\u0449\u0430\u044e\u0442\u0441\u044f, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043a \u043d\u0443\u0436\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 (Load):<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u2014 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e ClickHouse, \u0433\u0434\u0435 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Superset.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u043e\u0441\u043d\u043e\u0432\u0430\u043c\u0438 ETL, \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u2014 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Airflow \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. <\/p>\n<h2>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f Airflow<\/h2>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0440\u0435\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Python-\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u2014 \u044d\u0442\u043e Airflow, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 Docker-\u043e\u0431\u0440\u0430\u0437. \u041c\u044b \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0441\u0431\u043e\u0440\u043a\u0443 \u043d\u0430 \u0431\u0430\u0437\u0435 <code>apache\/airflow:2.8.2<\/code>.<\/p>\n<h3>\u041a\u0440\u0430\u0442\u043a\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043a\u0430 \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 Airflow 2.8.2<\/h3>\n<p>Airflow \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432:<\/p>\n<p>Airflow 2.8.2 (\u0432\u0435\u0440\u0441\u0438\u044f, \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043d\u0430 \u043c\u043c\u043e\u0435\u043d\u0442 \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0441\u0442\u0430\u0442\u044c\u0435\u0439) \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 Docker \u0438\u043b\u0438 Docker Compose \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c. \u0412\u043e\u0442 \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438 \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u0435\u043b\u0430\u0435\u0442:<\/p>\n<ol>\n<li>\n<p><strong>Webserver<\/strong> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c DAG&#8217;\u0438, \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u044b, \u043b\u043e\u0433\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0437\u0430\u0434\u0430\u0447 \u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u043a\u0430\u043c\u0438. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430 \u0431\u0430\u0437\u0435 Flask, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>Scheduler<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u044b, \u0438 \u0438\u0445 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 DAG&#8217;\u043e\u0432 \u0438 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u043e\u0432 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Worker(s)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 DAG&#8217;\u043e\u0432. \u0412 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u0438\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u043e\u043c. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Celery Executor, \u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e worker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Database (Postgres\/MySQL)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 Airflow, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 DAG&#8217;\u043e\u0432, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447, \u043b\u043e\u0433\u0438 \u0438 \u0434\u0440\u0443\u0433\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u0412 Docker Compose \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f PostgreSQL, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>Redis (\u0438\u043b\u0438 RabbitMQ)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u042f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u0440\u043e\u043a\u0435\u0440\u043e\u043c \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 \u0437\u0430\u0434\u0430\u0447 \u0434\u043b\u044f Celery Executor. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0437\u0430\u0434\u0430\u0447 \u043e\u0442 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 \u043a \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: Redis \u0438\u043b\u0438 RabbitMQ \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e\u0442 \u0432 \u0440\u043e\u043b\u0438 message broker, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Triggerer (\u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, AsyncSensor), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0439.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0421\u0431\u043e\u0440\u043a\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430<\/h3>\n<pre><code class=\"bash\">cd \/opt\/beget\/airflow<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c <code>Dockerfile<\/code>:<\/p>\n<pre><code class=\"bash\">FROM apache\/airflow:2.8.2 USER root RUN apt-get update &amp;&amp; \\    apt-get install -y wget unzip &amp;&amp; \\    wget https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    apt install -y .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    rm -f .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    export LATEST_VERSION=$(curl -s https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE) &amp;&amp; \\    wget -O \/tmp\/chromedriver.zip \"https:\/\/chromedriver.storage.googleapis.com\/$LATEST_VERSION\/chromedriver_linux64.zip\" &amp;&amp; \\    unzip \/tmp\/chromedriver.zip -d \/usr\/local\/bin\/ &amp;&amp; \\    rm \/tmp\/chromedriver.zip &amp;&amp; \\    rm -rf \/var\/lib\/apt\/lists\/* USER airflow RUN pip3 install pandas numpy selenium webdriver-manager beautifulsoup4<\/code><\/pre>\n<p>\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0437 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e:<\/p>\n<pre><code class=\"bash\">docker build -t local\/airflow:2.8.2 .<\/code><\/pre>\n<p>\u041c\u0435\u043d\u044f\u0435\u043c <code>image:<\/code> \u0432 <code>docker-compose.yml<\/code> \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c:<\/p>\n<pre><code class=\"bash\">docker-compose down &amp;&amp; docker-compose up -d<\/code><\/pre>\n<h3>\u041f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f \u043a RUN<\/h3>\n<p>\u042d\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432 Dockerfile \u0432 \u0431\u043b\u043e\u043a\u0435 \u0441 \u043f\u0435\u0440\u0432\u044b\u043c RUN, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c Google Chrome \u0438 ChromeDriver \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u0412\u043e\u0442 \u0440\u0430\u0437\u0431\u043e\u0440 \u043f\u043e \u0448\u0430\u0433\u0430\u043c:<\/p>\n<ol>\n<li>\n<p><code>RUN apt-get update &amp;&amp; \\<\/code> \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u042d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u0430\u0445 \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><code>apt-get install -y wget unzip &amp;&amp; \\<\/code> \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b: <code>wget<\/code>: \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u043e HTTP, HTTPS \u0438 FTP. <code>unzip<\/code>: \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438 ZIP-\u0430\u0440\u0445\u0438\u0432\u043e\u0432. \u0424\u043b\u0430\u0433 <code>-y<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 apt-get \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c &#171;\u0434\u0430&#187; \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435.<\/p>\n<\/li>\n<li>\n<p><code>wget <\/code><a href=\"https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb<\/code><\/a><code> &amp;&amp; \\<\/code> \u0421\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u043a\u0435\u0442 .deb, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e Google Chrome \u0434\u043b\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b amd64 (64-\u0431\u0438\u0442\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f).<\/p>\n<\/li>\n<li>\n<p><code>apt install -y .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\<\/code> \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f Google Chrome \u0438\u0437 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430 <code>.deb<\/code>. \u0424\u043b\u0430\u0433 <code>-y<\/code> \u0441\u043d\u043e\u0432\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p><code>rm -f .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\<\/code> \u0423\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 <code>.deb<\/code> \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0441\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p><code>export LATEST_VERSION=$(curl -s <\/code><a href=\"https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE<\/code><\/a><code>) &amp;&amp; \\<\/code> \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <code>LATEST_VERSION<\/code> \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0432\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 ChromeDriver. <code>curl -s<\/code> \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0431\u0435\u0437 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (\u0444\u043b\u0430\u0433 <code>-s<\/code> \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 &#171;silent&#187; \u2014 \u0431\u0435\u0437 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430).<\/p>\n<\/li>\n<li>\n<p><code>echo \"Latest ChromeDriver version: $LATEST_VERSION\" &amp;&amp; \\<\/code> \u0412\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0441 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 ChromeDriver, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.<\/p>\n<\/li>\n<li>\n<p><code>wget -O <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> \"<\/code><a href=\"https:\/\/chromedriver.storage.googleapis.com\/%24LATEST_VERSION\/chromedriver_linux64.zip\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/chromedriver.storage.googleapis.com\/$LATEST_VERSION\/chromedriver_linux64.zip<\/code><\/a><code>\" &amp;&amp; \\<\/code> \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f ZIP-\u0430\u0440\u0445\u0438\u0432 \u0441 ChromeDriver \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <code>\/tmp\/<\/code> \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0435\u043c <a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\">chromedriver.zip<\/a>.<\/p>\n<\/li>\n<li>\n<p><code>unzip <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> -d <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/usr\/local\/bin<\/code><\/a><code>\/ &amp;&amp; \\<\/code> \u0420\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 <a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a> \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/usr\/local\/bin<\/code><\/a><code>\/<\/code>, \u0447\u0442\u043e\u0431\u044b ChromeDriver \u0431\u044b\u043b \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 PATH.<\/p>\n<\/li>\n<li>\n<p><code>rm <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> &amp;&amp; \\<\/code> \u0423\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 ZIP-\u0444\u0430\u0439\u043b \u043f\u043e\u0441\u043b\u0435 \u0435\u0433\u043e \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0441\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p><code>rm -rf <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/var\/lib\/apt\/lists<\/code><\/a><code>\/*<\/code> \u0423\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 <code>apt-get<\/code>, \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435, \u044d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a \u043a\u043e\u043c\u0430\u043d\u0434 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 Google Chrome \u0438 ChromeDriver \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0447\u0442\u043e \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0438 \u043c\u0435\u0441\u0442\u0430 \u0438 \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430.<\/p>\n<h2>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Airflow<\/h2>\n<h3>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b<\/h3>\n<p>\u041e\u0431 airflow \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e, \u043d\u043e \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043c\u0441\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u043f\u0440\u0430\u0432\u043a\u043e\u0439, \u0430 \u0434\u0430\u043b\u0435\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0435\u0433\u043e \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u0435\u0439\u0441\u0430.<\/p>\n<p>Apache Airflow \u2014 \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 (DAGs \u2014 Directed Acyclic Graphs). \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0432 Airflow \u044d\u0442\u043e DAG. \u041e\u043d \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447 \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.<\/p>\n<p>DAG \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442:<\/p>\n<ul>\n<li>\n<p><strong>\u0417\u0430\u0434\u0430\u0447\u0438 (Tasks)<\/strong>: \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b. \u0417\u0430\u0434\u0430\u0447\u0438 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430, \u0438 \u044d\u0442\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u044b\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 \u0433\u0440\u0430\u0444\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (Schedule)<\/strong>: DAG \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0436\u0435\u0434\u043d\u0435\u0432\u043d\u043e \u0432 9 \u0443\u0442\u0440\u0430) \u0438\u043b\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440<\/strong>: DAG \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0446\u0438\u043a\u043b\u043e\u0432, \u0442.\u0435. \u043d\u0435\u043b\u044c\u0437\u044f \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043b\u0430\u043d \u0442\u0430\u043a\u043e\u0439: \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u0448 \u043a\u0435\u0439\u0441 \u043f\u043e ETL, \u0442\u043e \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e &#8212; &#8230; \ud83e\udd41 &#8230; <code>extract<\/code>. \u0410 \u0437\u0430\u0442\u0435\u043c \u0443\u0436\u0435 <code>transform<\/code> \u0438 <code>load<\/code>.<\/p>\n<p>\u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u043a\u0430\u043a\u0443\u044e \u0442\u043e \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u0430 \u043f\u0440\u044f\u043c\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430 wildberries, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u0441\u0435\u0445 \u0440\u043e\u0431\u043e\u0442\u043e\u0432-\u043f\u044b\u043b\u0435\u0441\u043e\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u0442\u0430\u043c \u043d\u0430\u0439\u0434\u0435\u043c, \u0441 \u0438\u0445 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 \u0438 \u0446\u0435\u043d\u0430\u043c\u0438. \u0412\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043e\u0447\u0438\u0441\u0442\u043a\u0430, \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435), \u0430 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 ClickHouse. \u0412\u0441\u0435 \u044d\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434y\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430 python \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043d\u0430 vps.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 DAG\u0430 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438. \u041e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e, \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from airflow import DAG from airflow.decorators import dag, task from airflow_clickhouse_plugin.hooks.clickhouse import ClickHouseHook from datetime import datetime, timedelta import pandas as pd import numpy as np import re from time import sleep from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043f\u0446\u0438\u0439 Chrome options = webdriver.ChromeOptions() options.add_argument('--headless')  # \u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 options.add_argument('window-size=1920x1080')  # \u0417\u0430\u0434\u0430\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u043e\u043a\u043d\u0430 options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-gpu')  # \u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c GPU \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 options.add_argument('--incognito') # \u0420\u0435\u0436\u0438\u043c \u0438\u043d\u043a\u043e\u0433\u043d\u0438\u0442\u043e default_args = {    'depends_on_past': False,    'retries': 1,    'retry_delay': timedelta(minutes=5),    'start_date': datetime(2024, 10, 29)} schedule_interval = '0 9 * * *' def get_page_content(articles):    '''    \u0424\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043e\u0434\u0435\u0436\u0438\u043c\u043e\u0433\u043e \u0442\u0435\u0433\u043e\u0432 article,    \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0434\u0435\u0436\u0438\u0442\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0438\u0437 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0442\u043e\u0432\u0430\u0440\u0430 \u043f\u0440\u0438 \u0431\u044b\u0441\u0442\u0440\u043e\u043c \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0435    \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c\u0438 \u043f\u043e\u043b\u044f\u043c\u0438:    - id - \u0430\u0440\u0442\u0438\u043a\u0443\u043b \u0442\u043e\u0432\u0430\u0440\u0430, string    - name - \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0442\u043e\u0432\u0430\u0440\u0430, string    - price - \u0446\u0435\u043d\u0430, int    - old_price - \u0441\u0442\u0430\u0440\u0430\u044f \u0446\u0435\u043d\u0430, float \u0438\u043b\u0438 nan    - brand - \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0431\u0440\u0435\u043d\u0434\u0430, string    - rate - \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u0442\u043e\u0432\u0430\u0440\u0432, float \u0438\u043b\u0438 nan    - estimate - \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0446\u0435\u043d\u043e\u043a, float \u0438\u043b\u0438 nan    - href - \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u0442\u043e\u0432\u0430\u0440\u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, string    '''    idx_lst, name_lst, price_lst, old_price_lst, brand_lst, rate_lst, estimate_lst, href_lst = [], [], [], [], [], [], [], []    for i in range(len(articles)):        try:            idx_lst.append(articles[i].attrs['data-nm-id'])        except Exception as e:            idx_lst.append(None)        try:            name_lst.append(articles[i].a.get('aria-label'))        except Exception as e:            name_lst.append(None)        try:            brand_lst.append(articles[i].find('span', class_='product-card__brand').get_text(strip=True))        except Exception as e:            brand_lst.append(None)        try:            price_lst.append(int(''.join(filter(str.isdigit, articles[i].find('ins', class_='price__lower-price')                                                .get_text(strip=True)))))        except Exception as e:            price_lst.append(None)        try:            old_price_lst.append(int(''.join(filter(str.isdigit, articles[i].find('del').get_text(strip=True)))))        except Exception as e:            old_price_lst.append(None)        try:            rate_lst.append(float(articles[i].find('span', class_='address-rate-mini address-rate-mini--sm')                                  .get_text(strip=True)))        except Exception as e:            rate_lst.append(None)        try:            estimate_lst.append(int(''.join(filter(str.isdigit, articles[i].find('span', class_='product-card__count')                                                   .get_text(strip=True)))))        except Exception as e:            estimate_lst.append(None)        try:            href_lst.append(articles[i].a.get('href'))        except Exception as e:            href_lst.append(None)    # \u0443 \u0442\u0435\u0445, \u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043d\u0435\u0442 id - \u044d\u0442\u043e \u0438\u0437 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043b\u0435\u043d\u0442\u044b, \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c    df = pd.DataFrame({'id': idx_lst, 'name': name_lst, 'price': price_lst, 'old_price': old_price_lst,                  'brand': brand_lst, 'rate': rate_lst, 'estimate': estimate_lst, 'href': href_lst})\\        .query('id==id').reset_index(drop=True)    return df # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u043e\u043b\u043b\u0430 def slow_scroll(driver, step, delay):    \"\"\"\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.\"\"\"    # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u044b\u0441\u043e\u0442\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b    scroll_height = driver.execute_script(\"return document.body.scrollHeight\")    current_scroll_position = 0    while current_scroll_position &lt; scroll_height:        # \u041f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0432\u043d\u0438\u0437 \u043d\u0430 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439        driver.execute_script(f\"window.scrollBy(0, {step});\")        current_scroll_position += step        sleep(delay)        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0432\u044b\u0441\u043e\u0442\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e\u0441\u043b\u0435 \u0441\u043a\u0440\u043e\u043b\u043b\u0430        scroll_height = driver.execute_script(\"return document.body.scrollHeight\") def map_to_base_brand(brand):    if pd.isna(brand):        return brand    brand = brand.lower()    for base_brand, pattern in brand_mapping.items():        if re.search(pattern, brand):            return base_brand    return brand  # \u0412\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 def map_to_base_color(color):    if pd.isna(color):        return color    color = color.lower()    for base_color, pattern in color_mapping.items():        if re.search(pattern, color):            return base_color    return '\u0434\u0440\u0443\u0433\u043e\u0439'  # \u0412\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 def dict_to_records(data_dict):    keys = list(data_dict.keys())    records = [tuple(data_dict[key][str(i)] for key in keys) for i in range(len(data_dict[keys[0]]))]    return records columns_russian = ['\u0426\u0432\u0435\u0442', '\u041e\u0431\u044a\u0435\u043c \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430', '\u0420\u0435\u0436\u0438\u043c\u044b \u0443\u0431\u043e\u0440\u043a\u0438',                   '\u041e\u0431\u044a\u0435\u043c \u0440\u0435\u0437\u0435\u0440\u0432\u0443\u0430\u0440\u0430 \u0434\u043b\u044f \u0432\u043e\u0434\u044b', '\u041c\u043e\u0434\u0435\u043b\u044c', '\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0439\u043d\u044b\u0439 \u0441\u0440\u043e\u043a',                   '\u0412\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b (\u043c\u0438\u043d)', '\u0415\u043c\u043a\u043e\u0441\u0442\u044c \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430', '\u041f\u0438\u0442\u0430\u043d\u0438\u0435',                   '\u0412\u0440\u0435\u043c\u044f \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b', '\u0422\u0438\u043f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f', '\u0422\u0438\u043f \u0443\u0431\u043e\u0440\u043a\u0438',                   '\u0422\u0438\u043f \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430', '\u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435', '\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430',                   '\u0412\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440', '\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0437\u0432\u0443\u043a\u0430\/\u0448\u0443\u043c\u0430',                   '\u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430', '\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0438\u0437\u0434\u0435\u043b\u0438\u044f',                   '\u0418\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430', '\u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u0430\u044f \u0432\u044b\u0441\u043e\u0442\u0430 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f',                   '\u0412\u0435\u0441 \u0442\u043e\u0432\u0430\u0440\u0430 \u0431\u0435\u0437 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (\u0433)', '\u041a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0430\u0446\u0438\u044f', '\u0421\u0442\u0440\u0430\u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0430',                   '\u0412\u0435\u0441 \u0442\u043e\u0432\u0430\u0440\u0430 \u0441 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u043e\u0439 (\u0433)', '\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043d\u0430 \u0437\u0430\u0440\u044f\u0434\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e',                   '\u0412\u044b\u0441\u043e\u0442\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430', '\u0428\u0438\u0440\u0438\u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430', '\u0413\u043b\u0443\u0431\u0438\u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430',                   '\u0414\u043b\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438', '\u0412\u044b\u0441\u043e\u0442\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438', '\u0428\u0438\u0440\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438',                   '\u0410\u0440\u0442\u0438\u043a\u0443\u043b', '\u041e\u0431\u044a\u0435\u043c \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430', '\u0420\u0435\u0436\u0438\u043c\u044b \u0443\u0431\u043e\u0440\u043a\u0438',                   '\u041e\u0431\u044a\u0435\u043c \u0440\u0435\u0437\u0435\u0440\u0432\u0443\u0430\u0440\u0430 \u0434\u043b\u044f \u0432\u043e\u0434\u044b', '\u041c\u043e\u0434\u0435\u043b\u044c', '\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0432\u0441\u0430\u0441\u044b\u0432\u0430\u043d\u0438\u044f',                   '\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u0435\u043b\u044c \u0437\u043e\u043d\u044b \u0443\u0431\u043e\u0440\u043a\u0438', 'Charging_installation', '\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043d\u0430\u0441\u0430\u0434\u043a\u0438',                   '\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u043e\u0432 \u0432 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0435', '\u0422\u0438\u043f \u043d\u0430\u0441\u0430\u0434\u043a\u0438', '\u0412\u0435\u0441 \u0441 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u043e\u0439 (\u043a\u0433)',                   '\u0412\u0435\u0441 \u0431\u0435\u0437 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (\u043a\u0433)', '\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0432\u0441\u0430\u0441\u044b\u0432\u0430\u043d\u0438\u044f (\u041f\u0430)', '\u0414\u0438\u0430\u043c\u0435\u0442\u0440 \u0441\u043e\u043f\u043b\u0430'] # \u041d\u043e\u0432\u044b\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u043d\u0430 \u043b\u0430\u0442\u0438\u043d\u0438\u0446\u0435 columns_latin = ['Color', 'Dustbin_capacity', 'Cleaning_modes',                 'Water_tank_capacity', 'Model', 'Warranty_period',                 'Runtime_min', 'Battery_capacity', 'Power_supply',                 'Autonomy_time', 'Control_type', 'Cleaning_type',                 'Dustbin_type', 'Spatial_orientation', 'Device_power',                 'Output_filter', 'Max_noise_level',                 'Additional_features', 'Material',                 'Robot_vacuum_indication', 'Obstacle_height',                 'Weight_g', 'Package_contents', 'Country_of_manufacture',                 'Weight_with_packaging_g', 'Charging_installation',                 'Item_height', 'Item_width', 'Item_depth',                 'Package_length', 'Package_height', 'Package_width',                 'Article', 'Dustbin_capacity', 'Cleaning_modes',                 'Water_tank_capacity', 'Model', 'Suction_power',                 'Cleaning_area_limiter', 'Charging_installation','Nozzle material',                 'Number_of_items_in_package','Nozzle_type','Weight_with_packaging_kg',                 'Weight_wo_packaging_kg','Suction_power_Pa','Nozzle diameter'] # \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044f \u0434\u043b\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u044f \u0441\u0442\u0430\u0440\u044b\u0445 \u0438 \u043d\u043e\u0432\u044b\u0445 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u043a\u043e\u043b\u043e\u043d\u043e\u043a column_mapping = dict(zip(columns_russian, columns_latin)) brand_mapping = {    'xiaomi': r'(?i)\\b\u043a\u0441\u0438\u043e\u043c\u0438\\b|\\bxiaomi robot\\b|\\bxioamii\\b|\\b\u0441 \u044f \u043e \u043c \u0438\\b|\\bx i a o m i\\b|\\bmi\\b|\\bxiaomi mi\\b',    'samsung':r'(?i)\\bsamsung\\b|\\b\u0441\u0430\u043c\u0441\u0443\u043d\\b',    'honor': r'(?i)\\bhonor\\b|\\bhonor choice\\b|\\bhonorchoice\\b|\\b\u0445\u043e\u043d\u043e\u0440\\b',    'polaris': r'(?i)\\bpolaris\\b|\\b\u043f\u043e\u043b\u044f\u0440\u0438\u0441\\b',    'dreame': r'(?i)\\bdreame\\b',    'lydsto': r'(?i)\\blydsto\\b|\\blidsto\\b',    'roborock': r'(?i)\\broborock\\b',    'redmond': r'(?i)\\bredmond\\b',    'filterix': r'(?i)\\bfilterix\\b',    'hobot': r'(?i)\\bhobot\\b|\\b\u0445\u043e\u0431\u043e\u0442\\b',    'mijia': r'(?i)\\bmijia\\b',    'futula': r'(?i)\\bfutula\\b',    'ilife': r'(?i)\\bilife\\b', } # \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0446\u0432\u0435\u0442\u043e\u0432 \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043e\u0442\u0442\u0435\u043d\u043a\u043e\u0432 color_mapping = {    '\u0431\u0435\u043b\u044b\u0439': r'(?i)\\b\u0431\u0435\u043b\u044b\u0439\\b|\\bwhite\\b',    '\u0447\u0435\u0440\u043d\u044b\u0439': r'(?i)\\b\u0447\u0435\u0440\u043d\u044b\u0439\\b|\\b\u0433\u0440\u0430\u0444\u0438\u0442\\b|\\bblack\\b',    '\u0441\u0438\u043d\u0438\u0439': r'(?i)\\b\u0441\u0438\u043d\u0438\u0439\\b|\\b\u0433\u043e\u043b\u0443\u0431\u043e\u0439\\b|\\b\u84dd\u8272\\b|\\b\u84dd\u8272\\b|\\bdark blue\\b',    '\u0441\u0435\u0440\u044b\u0439': r'(?i)\\b\u0441\u0435\u0440\u044b\u0439\\b|\\b\u0441\u0435\u0440\u0435\u0431\u0440\u0438\u0441\u0442\u044b\u0439\\b|\\b\u0441\u0435\u0440\u0435\u0431\u0440\u0438\u0441\u0442\u043e\\b|\\b\u0441\u0435\u0440\u043e\\b',    '\u043a\u0440\u0430\u0441\u043d\u044b\u0439': r'(?i)\\b\u043a\u0440\u0430\u0441\u043d\u044b\u0439\\b|\\b\u044f\u0440\u043a\u043e-\u043a\u0440\u0430\u0441\u043d\u044b\u0439\\b|\\b\u043a\u0440\u0430\u0441\u043d\u043e\\b',    '\u0437\u0435\u043b\u0435\u043d\u044b\u0439': r'(?i)\\b\u0437\u0435\u043b\u0435\u043d\u044b\u0439\\b|\\b\u0441\u0430\u043b\u0430\u0442\u043e\u0432\u044b\u0439\\b',    '\u0436\u0435\u043b\u0442\u044b\u0439': r'(?i)\\b\u0436\u0435\u043b\u0442\u044b\u0439\\b',    '\u0431\u0435\u0436\u0435\u0432\u044b\u0439': r'(?i)\\b\u0431\u0435\u0436\u0435\u0432\u044b\u0439\\b',    '\u043e\u0440\u0430\u043d\u0436\u0435\u0432\u044b\u0439': r'(?i)\\b\u043e\u0440\u0430\u043d\u0436\u0435\u0432\u044b\u0439\\b',    '\u0440\u043e\u0437\u043e\u0432\u044b\u0439': r'(?i)\\b\u0440\u043e\u0437\u043e\u0432\u044b\u0439\\b',    '\u0444\u0438\u043e\u043b\u0435\u0442\u043e\u0432\u044b\u0439': r'(?i)\\b\u0444\u0438\u043e\u043b\u0435\u0442\u043e\u0432\u044b\u0439\\b',    '\u0437\u043e\u043b\u043e\u0442\u0438\u0441\u0442\u044b\u0439': r'(?i)\\b\u0437\u043e\u043b\u043e\u0442\u0438\u0441\u0442\u044b\u0439\\b',    '\u043c\u0435\u0442\u0430\u043b\u043b': r'(?i)\\b\u043c\u0435\u0442\u0430\u043b\u043b\\b|\\b\u0445\u0440\u043e\u043c\\b|\\b\u0441\u0435\u0440\u0435\u0431\u0440\u043e\\b|\\b\u043f\u043b\u0430\u0442\u0438\u043d\u0430\\b', } # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441\u043a\u0440\u043e\u043b\u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0443\u0437\u043a\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 scroll_height = 15000 # \u0412\u044b\u0441\u043e\u0442\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b step = 100  # \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0437\u0430 \u043e\u0434\u0438\u043d \u0448\u0430\u0433 delay = 0.5  # \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u0435\u0436\u0434\u0443 \u0448\u0430\u0433\u0430\u043c\u0438 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 @dag(default_args=default_args, schedule_interval=schedule_interval, catchup=False) def robot_vacuum__scraper_wb():    @task    def scraping_data():        # \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432\u0435\u0431-\u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f        driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)        print('driver created')        sleep(2)        try:            # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043a\u044d\u0448 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443            driver.delete_all_cookies()            driver.refresh()            driver.get('https:\/\/www.wildberries.ru\/') # \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443            sleep(5)            ## \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443            search_box = WebDriverWait(driver, 18).until(EC.presence_of_element_located((By.ID, 'searchInput')))            sleep(3)            search_box.send_keys('\u0440\u043e\u0431\u043e\u0442-\u043f\u044b\u043b\u0435\u0441\u043e\u0441') # \u0412\u0432\u043e\u0434\u0438\u043c \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441            sleep(4)            # \u0421\u0438\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 Enter            search_box.send_keys(Keys.RETURN)            sleep(10)            slow_scroll(driver, step, delay) # \u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u043a\u0440\u043e\u043b\u043b\u0430            sleep(2)            content = BeautifulSoup(driver.page_source, \"html.parser\") # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b            # \u0418\u043d\u0438\u0446\u0438\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u0442\u0430\u0444\u0440\u0435\u0439\u043c, \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044f \u0432 \u043d\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b            df = get_page_content(articles = content.find_all('article')).assign(number_page = 1)            print(f\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b 1 \u0432 {datetime.now().strftime('%H:%M:%S')}\")            # \u0412 \u0446\u0438\u043a\u043b\u0435 \u043f\u0435\u0440\u0435\u043b\u0438\u0441\u0442\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b - \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c 10 \u043f\u0435\u0440\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435            for page in range(2, 10):                try:                    # \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043d\u0430 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443                    next_page = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.LINK_TEXT, str(page))))                    driver.execute_script(\"arguments[0].click();\", next_page)                    sleep(2)                    slow_scroll(driver, step, delay) # \u0421\u043a\u0440\u043e\u043b\u043b\u0438\u043c                    sleep(2)                    content = BeautifulSoup(driver.page_source, \"html.parser\") # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435                    page_content = get_page_content(articles = content.find_all('article')).assign(number_page = page)                    df = pd.concat([df, page_content]).reset_index(drop=True)                    print(f\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b {page} \u0432 {datetime.now().strftime('%H:%M:%S')}\")                except Exception as e:                    print(f\"\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u044b {page} \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 {repr(e)}\")                    break            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b            df = df.drop_duplicates('id').reset_index(drop=True)            # \u0414\u0435\u043b\u0430\u0435\u043c \u043e\u0431\u0445\u043e\u0434 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043f\u043e\u043b\u0443\u0447\u0430\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u0430            all_details = pd.DataFrame()            # \u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0430\u043c \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u043e\u0432, \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0435\u0442\u0430\u043b\u0438            for i, product_url in enumerate(df.href):                try:                    driver.get(product_url) # \u041e\u0442\u043a\u0440\u044b\u0442\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u043e\u0432\u0430\u0440\u0430                    sleep(4)                    # \u042f\u0432\u043d\u043e\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \"\u0412\u0441\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\"                    button = WebDriverWait(driver, 14).until(EC.element_to_be_clickable((By.CLASS_NAME, 'product-page__btn-detail')))                    print(f'goods {i}, waited load')                    driver.execute_script(\"arguments[0].scrollIntoView(true);\", button) #\u041f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u043a \u043a\u043d\u043e\u043f\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0432 \u0432\u0438\u0434\u0438\u043c\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438                    button.click() # \u041d\u0430\u0436\u0438\u043c\u0430\u0435\u043c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443                    sleep(5)                    page_source = driver.page_source # \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438                    soup = BeautifulSoup(page_source, 'html.parser') # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f BeautifulSoup \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b                    #print(f'goods {i}, start pardced')                    details = pd.DataFrame([[i.span.text for i in soup.find_all(\"td\", class_=\"product-params__cell\")]],                            columns=[i.span.text for i in soup.find_all(\"span\", class_=\"product-params__cell-decor\")])                    select_index = pd.DataFrame({'col':details.rename(columns=column_mapping).columns})                            .drop_duplicates('col').index.to_list()                    details = details.rename(columns=column_mapping).iloc[:, select_index]                    all_details = pd.concat([all_details, details]).reset_index(drop=True)                except Exception as e:                    print(f'href {product_url}: {repr(e)}')            # \u0417\u0430\u043a\u0440\u044b\u0442\u0438\u0435 \u0432\u0435\u0431-\u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430            driver.quit()            # \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0430\u0442\u0443 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0432 \u0441\u0442\u0440\u043e\u043a\u0443, \u0442.\u043a. JSON \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 Timestamp \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e            df = (df.drop_duplicates('id').rename(columns={'id':'Article'})              .merge(all_details.drop_duplicates('Article'), on='Article')            .assign(number_position = lambda df: np.arange(len(df)))\\              .assign(date = pd.to_datetime(datetime.now()).normalize().strftime('%Y-%m-%d')))            #df = df.where(pd.notnull(df), None)            df = df.astype(object).where(pd.notnull(df), None)            return df.to_dict()        finally:            print(f'quit driver')            driver.quit()    @task    def preprocessing_data(records):        print('Start prepocessing')        df = pd.DataFrame(records)  # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 records \u0432 DataFrame        #df['price'] = df['price'].astype('Int64')        # \u0411\u0440\u0435\u043d\u0434        df_brand = (df[['Article', 'brand']].rename(columns={'brand': 'old_brand'})                    .assign(sup_brand=lambda df: df.old_brand.apply(map_to_base_brand),                            num_brand=lambda df: df.groupby('sup_brand')['sup_brand'].transform('count'))                    .assign(brand=lambda df: [brand if num_brand &gt; 1 else \"other\" for brand, num_brand in zip(df.sup_brand, df.num_brand)])[['Article', 'brand']])        print('get brand')        # \u0421\u0442\u0440\u0430\u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0430        # \u0420\u0435\u0448\u0438\u043b\u0430 \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u0441\u0430\u043c\u044b\u0439 \u043b\u0443\u0447\u0448\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442        country = pd.DataFrame({'Country_of_manufacture':['\u041a\u0438\u0442\u0430\u0439', '\u0420\u043e\u0441\u0441\u0438\u044f', '\u0422\u0430\u0439\u0432\u0430\u043d\u044c', '\u4e2d\u56fd', '\u0412\u044c\u0435\u0442\u043d\u0430\u043c', '\u0422\u043e\u0433\u043e', '\u0420\u0435\u0441\u043f\u0443\u0431\u043b\u0438\u043a\u0430 \u041a\u043e\u0440\u0435\u044f',                                                '\u0413\u0435\u0440\u043c\u0430\u043d\u0438\u044f', 'China', '\u0413\u043e\u043d\u043a\u043e\u043d\u0433', '\u041c\u0430\u043b\u0430\u0439\u0437\u0438\u044f', '\u041a\u041d\u0414\u0420', '\u042e\u0436\u043d\u0430\u044f \u041a\u043e\u0440\u0435\u044f'],                     'country_of_manufacture':['\u041a\u0438\u0442\u0430\u0439', '\u0420\u043e\u0441\u0441\u0438\u044f', '\u0422\u0430\u0439\u0432\u0430\u043d\u044c', '\u041a\u0438\u0442\u0430\u0439', '\u0412\u044c\u0435\u0442\u043d\u0430\u043c', '\u0422\u043e\u0433\u043e', '\u0420\u0435\u0441\u043f\u0443\u0431\u043b\u0438\u043a\u0430 \u041a\u043e\u0440\u0435\u044f',                                                '\u0413\u0435\u0440\u043c\u0430\u043d\u0438\u044f', '\u041a\u0438\u0442\u0430\u0439', '\u0413\u043e\u043d\u043a\u043e\u043d\u0433', '\u041c\u0430\u043b\u0430\u0439\u0437\u0438\u044f', '\u041a\u041d\u0414\u0420', '\u0420\u0435\u0441\u043f\u0443\u0431\u043b\u0438\u043a\u0430 \u041a\u043e\u0440\u0435\u044f']})        df_country = df[['Article','Country_of_manufacture']].merge(country, on='Country_of_manufacture', how='left')                    .fillna('other')[['Article','country_of_manufacture']]        print('get country')        # \u0426\u0432\u0435\u0442 \u043a\u043e\u0440\u043f\u0443\u0441\u0430        df_color = df[['Article', 'Color']].rename(columns={'Color':'old_color'})            .assign(color = lambda df: df.old_color.apply(map_to_base_color))[['Article','color']]        print('get color')        # \u0421\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430 \u0434\u0430\u043b\u0435\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u044b\u043b\u0435\u0441\u043e\u0441 \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c        # \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e.        # \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u043b\u0430\u0433\u0438 (\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f 1 \u0438\u043b\u0438 0) - \u0435\u0441\u0442\u044c \u043e\u043f\u0446\u0438\u044f \u0438\u043b\u0438 \u043d\u0435\u0442        # \u0422\u0438\u043f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f ('Control_type')        # \u041c\u0435\u0445\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435, \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u043a\u043d\u043e\u043f\u043a\u0438        df_manual_control = df[(df.Control_type.fillna('na').str.contains('\u043a\u043d\u043e\u043f\u043a|\u043a\u043d\u043e\u043f\u043e\u0447\u043d|\u043c\u0435\u0445\u0430\u043d\u0438\u0447|\u043d\u0430 \u043a\u043e\u0440\u043f\u0443\u0441\u0435', case=False))]                    [['Article']].assign(manual_control = 1)        print('get manual control')        # \u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u043c\u043e\u0449\u043d\u0438\u043a - \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 ('Spatial_orientation')        # \u0438 \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features')        pattern = '\u0443\u043c\u043d\u044b\u0439 \u0434\u043e\u043c|\u0443\u043c\u043d\u0430\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0430|\u0430\u043b\u0438\u0441|\u0433\u043e\u043b\u043e\u0441|alex|\u043c\u0430\u0440\u0443\u0441|assistant'        df_voice_assistant = df[(df.Control_type.fillna('na').str.contains(pattern, case=False))|                (df.Spatial_orientation.fillna('na').str.contains(pattern, case=False))|                (df.Additional_features.fillna('na').str.contains(pattern, case=False))]\\                [['Article']].assign(voice_assistant = 1)        print('get voice assistant')        # \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430 - \u0442\u0430\u043a\u0436\u0435 \u0431\u0435\u0440\u0435\u043c \u0438\u0437 \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features')        df_app_control = df[(df.Control_type.fillna('na').str.contains('\u043c\u043e\u0431\u0438\u043b\u044c\u043d|\u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d|\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d|\u0442\u0435\u043b\u0435\u0444\u043e\u043d', case=False))|                    (df.Additional_features.fillna('na').str.contains('\u043c\u043e\u0431\u0438\u043b\u044c\u043d|\u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d|\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d|\u0442\u0435\u043b\u0435\u0444\u043e\u043d', case=False))]\\                    [['Article']].assign(app_control = 1)        print('get app control')        # \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0443\u043b\u044c\u0442\u043e\u043c - \u0442\u0430\u043a\u0436\u0435 \u0431\u0435\u0440\u0435\u043c \u0438\u0437 \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features')        df_remote_control = df[(df.Control_type.fillna('na').str.contains('\u043f\u0443\u043b\u044c\u0442|\u0414\u0423', case=False))|                              (df.Additional_features.fillna('na').str.contains('\u043f\u0443\u043b\u044c\u0442|\u0414\u0423', case=False))]\\                    [['Article']].assign(remote_control = 1)        print('get remove control')        # \u0422\u0438\u043f \u0443\u0431\u043e\u0440\u043a\u0438 ('Cleaning_type') - \u0442\u0430\u043a\u0436\u0435 \u0431\u0435\u0440\u0435\u043c \u0438\u0437 \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features')        # \u041c\u043e\u044e\u0449\u0438\u0439        df_wash_cleaning = df[(df.Cleaning_type.fillna('na').str.contains('\u043c\u043e\u0439\u043a|\u043c\u043e\u044e\u0449', case=False))|                             (df.Additional_features.fillna('na').str.contains('\u043c\u043e\u0439\u043a|\u043c\u043e\u044e\u0449', case=False))]\\                    [['Article']].assign(wash_cleaning = 1)        print('get wash cleaning')        # \u0421\u0443\u0445\u0430\u044f \u0443\u0431\u043e\u0440\u043a\u0430        df_dry_cleaning = df[(df.Cleaning_type.fillna('na').str.contains('\u0441\u0443\u0445\u0430\u044f', case=False))|                            (df.Additional_features.fillna('na').str.contains('\u0441\u0443\u0445\u0430\u044f', case=False))]\\                    [['Article']].assign(dry_cleaning = 1)        print('get dry cleaning')        # \u0412\u043b\u0430\u0436\u043d\u0430\u044f \u0443\u0431\u043e\u0440\u043a\u0430        df_wet_cleaning = df[(df.Cleaning_type.fillna('na').str.contains('\u0432\u043b\u0430\u0436\u043d\u0430\u044f', case=False))|                            (df.Additional_features.fillna('na').str.contains('\u0432\u043b\u0430\u0436\u043d\u0430\u044f', case=False))]\\                    [['Article']].assign(wet_cleaning = 1)        print('get wet cleaning')        # \u0421\u0443\u0445\u0430\u044f \u0438 \u0432\u043b\u0430\u0436\u043d\u0430\u044f \u0443\u0431\u043e\u0440\u043a\u0430 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e        df_dry_and_wet_cleaning = df[(df.Cleaning_type.fillna('na').str.contains('\u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d|\u041a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d|\u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u043d', case=False))]                    [['Article']].assign(dry_and_wet_cleaning = 1)        print('get dry and wet cleaning')        # \u0422\u0438\u043f \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430 ('Dustbin_type')        # \u041c\u0435\u0448\u043e\u043a \u0434\u043b\u044f \u043c\u0443\u0441\u043e\u0440\u0430        df_garbage_bag = df[(df.Dustbin_type.fillna('na').str.contains('\u043c\u0435\u0448\u043e\u043a', case=False))]                    [['Article']].assign(garbage_bag = 1)        print('get garbage bag')        # \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440        df_container = df[(df.Dustbin_type.fillna('na').str.contains(r'\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440|\u0435\u043c\u043a\u043e\u0441\u0442\u044c', case=False))]                    [['Article']].assign(container = 1)        print('get container')        # \u0410\u043a\u0432\u0430\u0444\u0438\u043b\u044c\u0442\u0440        df_aquafilter = df[(df.Dustbin_type.fillna('na').str.contains('\u0430\u043a\u0432\u0430\u0444\u0438\u043b\u044c\u0442\u0440', case=False))|                        (df.Dustbin_type.fillna('na').str.contains(r'(?=.*\u043c\u043e\u044e\u0449\u0438\u0439\u0441\u044f)(?=.*\u0444\u0438\u043b\u044c\u0442\u0440)', case=False))]\\                        [['Article']].assign(aquafilter = 1)        print('get aquafilter')        # \u041e\u0440\u0438\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 ('Spatial_orientation')        # \u0442\u0430\u043a\u0436\u0435 \u0432\u0441\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0431\u0435\u0440\u0435\u043c \u0438\u0437 \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features'), \u0442.\u043a. \u043e\u043d\u0438 \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0442\u0430\u043c \u0443\u043a\u0430\u0437\u0430\u043d\u044b        # \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u043d\u0430 \u0431\u0430\u0437\u0443        df_return_to_base = df[(df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u043f\u043e\u0438\u0441\u043a)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u043f\u043e\u0438\u0441\u043a)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0432\u043e\u0437\u0432\u0440\u0430\u0442)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0432\u043e\u0437\u0432\u0440\u0430\u0442)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0432\u043e\u0437\u0432\u0440\u0430\u0442)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0432\u043e\u0437\u0432\u0440\u0430\u0442)(?=.*\u0437\u0430\u0440\u044f\u0434\u043d)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u043f\u043e\u0438\u0441\u043a)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u043f\u043e\u0438\u0441\u043a)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a)(?=.*\u0431\u0430\u0437)', case=False))|                        (df.Spatial_orientation.fillna('na').str.contains(r'\u043f\u0430\u0440\u043a\u043e\u0432\u043a', case=False))|                        (df.Additional_features.fillna('na').str.contains(r'\u043f\u0430\u0440\u043a\u043e\u0432\u043a', case=False))]\\                        [['Article']].assign(return_to_base= 1)        print('get return to base')        # \u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0442\u0435\u043d\u0430        df_virtual_wall = df[(df.Spatial_orientation.fillna('na').str.contains('\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u0435\u043b\u044c', case=False))|                    (df.Additional_features.fillna('na').str.contains('\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u0435\u043b\u044c', case=False))|                    (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0437\u0430\u043f\u0440\u0435\u0442\u043d)(?=.*\u0437\u043e\u043d)', case=False))|                    (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0437\u0430\u043f\u0440\u0435\u0442\u043d)(?=.*\u0437\u043e\u043d)', case=False))|                    (df.Spatial_orientation.fillna('na').str.contains(r'(?=.*\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d)(?=.*\u0441\u0442\u0435\u043d)', case=False))|                    (df.Additional_features.fillna('na').str.contains(r'(?=.*\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d)(?=.*\u0441\u0442\u0435\u043d)', case=False))]\\                    [['Article']].assign(virtual_wall = 1)        print('get virtual wall')        # \u0421\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b        df_space_map = df[(df.Spatial_orientation.fillna('na').str.contains('\u043f\u043b\u0430\u043d|\u043a\u0430\u0440\u0442|\u0437\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d', case=False))|                         (df.Additional_features.fillna('na').str.contains('\u043f\u043b\u0430\u043d|\u043a\u0430\u0440\u0442|\u0437\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d', case=False))]\\                    [['Article']].assign(space_map = 1)        print('get space map')        # \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0434\u043b\u044f \u043a\u043e\u0432\u0440\u0430, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u0432\u0440\u0430        df_carped_mode = df[(df.Spatial_orientation.fillna('na').str.contains('\u043a\u043e\u0432\u0440|\u043a\u043e\u0432\u0435\u0440', case=False))|                           (df.Additional_features.fillna('na').str.contains('\u043a\u043e\u0432\u0440|\u043a\u043e\u0432\u0435\u0440', case=False))]\\                    [['Article']].assign(carped_mode = 1)        print('get carped mode')        # \u041b\u0430\u0437\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438        df_laser_sensor = df[(df.Spatial_orientation.fillna('na').str.contains('\u043b\u0430\u0437\u0435\u0440', case=False))|                            (df.Additional_features.fillna('na').str.contains('\u043b\u0430\u0437\u0435\u0440', case=False))]\\                    [['Article']].assign(laser_sensor = 1)        print('get laser sensor')        # \u0433\u0438\u0440\u043e\u0441\u043a\u043e\u043f        df_giroscope = df[(df.Spatial_orientation.fillna('na').str.contains('\u0433\u0438\u0440\u043e\u0441\u043a\u043e\u043f', case=False))|                         (df.Additional_features.fillna('na').str.contains('\u0433\u0438\u0440\u043e\u0441\u043a\u043e\u043f', case=False))]\\                    [['Article']].assign(giroscope = 1)        print('get giroscope')        # \u043b\u0438\u0434\u0430\u0440        df_lidar = df[(df.Spatial_orientation.fillna('na').str.contains('\u043b\u0438\u0434\u0430\u0440|LiDAR', case=False))|                     (df.Additional_features.fillna('na').str.contains('\u043b\u0438\u0434\u0430\u0440|LiDAR', case=False))]\\                    [['Article']].assign(lidar = 1)        print('get lidar')        # \u0414\u0435\u0442\u0435\u043a\u0446\u0438\u044f \u043a\u0440\u0430\u044f, \u0441\u0442\u0443\u043f\u0435\u043d\u0435\u043a, \u043f\u0435\u0440\u0435\u043f\u0430\u0434\u0430 \u0432\u044b\u0441\u043e\u0442\u044b        df_edge_detection = df                [(df.Spatial_orientation.fillna('na').str.contains('\u0441\u0442\u0443\u043f\u0435\u043d|\u0432\u044b\u0441\u043e\u0442|\u043e\u0431\u0440\u044b\u0432|\u043a\u0440\u0430\u044f|\u043a\u0440\u0430\u0439|\u043f\u0435\u0440\u0435\u043f\u0430\u0434|\u043f\u0430\u0434\u0435\u043d', case=False))|                (df.Additional_features.fillna('na').str.contains('\u0441\u0442\u0443\u043f\u0435\u043d|\u0432\u044b\u0441\u043e\u0442|\u043e\u0431\u0440\u044b\u0432|\u043a\u0440\u0430\u044f|\u043a\u0440\u0430\u0439|\u043f\u0435\u0440\u0435\u043f\u0430\u0434|\u043f\u0430\u0434\u0435\u043d', case=False))]\\                [['Article']].assign(edge_detection = 1)        print('get edge detection')        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u0439        df_obstancle_detection = df                [(df.Spatial_orientation.fillna('na').str.contains('\u0441\u0442\u0435\u043d|\u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d|\u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432|\u043f\u0440\u0435\u0434\u043c\u0435\u0442|\u043f\u0440\u043e\u0432\u043e\u0434', case=False))|                (df.Additional_features.fillna('na').str.contains('\u0441\u0442\u0435\u043d|\u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d|\u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432|\u043f\u0440\u0435\u0434\u043c\u0435\u0442|\u043f\u0440\u043e\u0432\u043e\u0434', case=False))]\\                [['Article']].assign(obstancle_detection = 1)        print('obstancle detection')        # \u0414\u043e\u043f. \u043e\u043f\u0446\u0438\u0438 \u0440\u043e\u0431\u043e\u0442\u0430 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430 ('Additional_features')        # \u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a        df_scheduler = df[(df.Additional_features.fillna('na')                      .str.contains('\u0442\u0430\u0439\u043c\u0435\u0440|\u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d|\u0433\u0440\u0430\u0444\u0438\u043a|\u0430\u0432\u0442\u043e\u0432\u043a\u043b\u044e\u0447\u0435\u043d|\u0430\u0432\u0442\u043e\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d|\u0430\u0432\u0442\u043e\u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d|\u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u0440\u0442', case=False))]\\                    [['Article']].assign(scheduler = 1)        print('get scheduler')        # \u0432\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0431\u043e\u0440\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0434\u0437\u0430\u0440\u044f\u0434\u043a\u0438        df_continued_after_charging = df[(df.Additional_features.fillna('na')                                          .str.contains('\u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0440\u044f\u0434\u043a\u0438|\u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0434\u0437\u0430\u0440\u044f\u0434\u043a\u0438', case=False))]\\                    [['Article']].assign(continued_after_charging= 1)        print('get continued after charging')        # \u0422\u0443\u0440\u0431\u043e\u0449\u0435\u0442\u043a\u0430        df_turbo_brush = df[(df.Additional_features.fillna('na').str.contains('\u0442\u0443\u0440\u0431\u043e\u0440\u0435\u0436\u0438\u043c', case=False))|                    (df.Additional_features.fillna('na').str.contains('\u0422\u0443\u0440\u0431\u043e\u0449\u0435\u0442\u043a\u0430', case=False))]\\                    [['Article']].assign(turbo_brush = 1)        print('get turbo brush')        # \u0411\u0430\u043c\u043f\u0435\u0440        df_bamper = df[(df.Additional_features.fillna('na').str.contains('\u0431\u0430\u043c\u043f\u0435\u0440', case=False))]                    [['Article']].assign(bamper = 1)        print('get bamper')        # \u0443\u0444 \u043b\u0430\u043c\u043f\u0430        pattern = '\u0443\u0444 \u043b\u0430\u043c\u043f\u0430|\u0443\u0444\u043b\u0430\u043c\u043f\u0430|\u0423\u0424 \u0441\u0442\u0435\u0440\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u044f|\u043b\u0430\u043c\u043f\u0430 \u0434\u043b\u044f \u0441\u0442\u0435\u0440\u0438\u043b\u0438\u0437\u0430\u0446\u0438\u0438|\u0423\u0424-\u043e\u0431\u0435\u0437\u0437\u0430\u0440\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u0435|\u0423\u0424-\u043b\u0430\u043c\u043f\u0430|\u0423\u0444\u0430\u043c\u043f\u0430'+                '|\u0423\u0424 \u0434\u0435\u0437\u0438\u043d\u0444\u0435\u043a\u0446\u0438\u044f|\u0423\u043b\u044c\u0442\u0440\u0430\u0444\u0438\u043e\u043b\u0435\u0442\u043e\u0432\u0430\u044f \u043b\u0430\u043c\u043f\u0430'        df_uv_lamp = df[(df.Additional_features.fillna('na').str.contains(pattern, case=False))]                    [['Article']].assign(uv_lamp = 1)        print('get uv lamp')        # \u0432\u0438\u0434\u0435\u043e\u043a\u0430\u043c\u0435\u0440\u0430        df_video_camera = df[(df.Additional_features.fillna('na').str.contains('\u0432\u0438\u0434\u0435\u043e|\u043a\u0430\u043c\u0435\u0440', case=False))]                    [['Article']].assign(video_camera = 1)        print('get video camera')        # \u0441\u0430\u043c\u043e\u043e\u0447\u0438\u0441\u0442\u043a\u0430        df_self_cleaning = df[(df.Additional_features.fillna('na').str.contains('\u0441\u0430\u043c\u043e\u043e\u0447\u0438\u0441\u0442\u043a|\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430', case=False))]                    [['Article']].assign(self_cleaning = 1)        print('get self cleaning')        # \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0441 \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f        # \u0440\u0430\u0441\u043f\u0430\u0440\u0448\u0438\u0432\u0430\u0435\u043c \u043e\u0434\u043d\u0438\u043c \u0446\u0438\u043a\u043b\u043e\u043c        '''        \u0414\u043b\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 ('Package_length')        \u0412\u044b\u0441\u043e\u0442\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 ('Package_height')        \u0428\u0438\u0440\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 ('Package_width')        \u0428\u0438\u0440\u0438\u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430 ('Item_width',)        \u0412\u044b\u0441\u043e\u0442\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430 ('Item_height')        \u0412\u0435\u0441 \u0442\u043e\u0432\u0430\u0440\u0430 \u0441 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u043e\u0439 (\u0433) ('Weight_with_packaging_g')        \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0437\u0432\u0443\u043a\u0430\/\u0448\u0443\u043c\u0430 ('Max_noise_level')        \u041e\u0431\u044a\u0435\u043c \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430 ('Dustbin_capacity')        '''        col_name=['Package_length', 'Package_height', 'Package_width','Item_width','Item_height',                  'Weight_with_packaging_g', 'Dustbin_capacity', 'Max_noise_level']        new_col_name=['package_length_sm', 'package_height_sm', 'package_width_sm', 'item_width_sm','item_height_sm',                      'weight_with_packaging_g','dustbin_capacity_l', 'max_noise_level_db']        measures = [' \u0441\u043c', ' \u0441\u043c', ' \u0441\u043c', ' \u0441\u043c', ' \u0441\u043c', ' \u0433', ' \u043b', ' \u0434\u0411']        low_borders = [5, 5, 5, 4, 4, 500, 0.05, 0]        upper_borders = [200, 200, 200, 100, 100, 20000, 3, 100]        df_fixed_measure = df[['Article']].copy()        for col, new_col, meas, low, upper in zip(col_name, new_col_name, measures, low_borders, upper_borders):            values = []            for val in df[col]:                try:                    values.append(float(val.split(meas)[0]))                except Exception as e:                    values.append(None)            df_fixed_measure[new_col] = values            df_fixed_measure.loc[(df_fixed_measure[new_col] &lt; low)|(df_fixed_measure[new_col] &gt; upper), new_col] = None            print(f'get {col}')        # \u0420\u0430\u0441\u043f\u0430\u0440\u0448\u0438\u0432\u0430\u0435\u043c \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439        '''        \u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 ('Device_power') - \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0430 \u0432 \u0412\u0442, \u041f\u0430, \u043f\u0440\u0438\u0447\u0435\u043c \u0435\u0434. \u0438\u0437\u043c-\u044f \u043c\u043e\u0433\u0443\u0442 \u043f\u044b\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u043c\u0438, \u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0435\u0439 \u0438\u043b\u0438 \u043b\u0430\u0442\u0438\u043d\u0438\u0446\u0435\u0439, \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0438\u043c\u0432\u043e\u043b\u0430\u043c\u0438        \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0439\u043d\u044b\u0439 \u0441\u0440\u043e\u043a ('Warranty_period') - \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432 \u0434\u043d\u044f\u0445, \u0433\u043e\u0434\u0430\u0445, \u043c\u0435\u0441\u044f\u0446\u0430\u0445 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u043a\u0441\u0442\u043e\u043c        \u0415\u043c\u043a\u043e\u0441\u0442\u044c \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430 ('Battery_capacity') - \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432 \u0432 \u043c\u0410\u0447, \u0410\u0447        '''        # \u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 ('Device_power')        # \u0420\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439        watt_pattern = re.compile(r'(\\d+(\\.\\d+)?)(?:\\s*[Ww\u0412\u0432\u0442\u0442]+|W|\u0412\u0442)?')        pa_pattern = re.compile(r'(\\d+(\\.\\d+)?)\\s*(?:\u041f\u0430|Pa|\u043f\u0410|pa|PA|\u043f\u0430|\u041f\u0410)')        # \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u041f\u0430 \u0432 \u0412\u0442        def pa_to_watt(pa):            return pa \/ 71        # \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0441\u043f\u0438\u0441\u043e\u043a        device_power_watt = []        Article = []        for article, item in zip(df.Article, df.Device_power):            try:                if pd.isna(item):                    device_power_watt.append(None)                    Article.append(article)                else:                    # \u041f\u043e\u0438\u0441\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u043f\u0430\u0441\u043a\u0430\u043b\u044f\u0445                    pa_match = pa_pattern.search(item)                    if pa_match:                        pa_value = float(pa_match.group(1))                        device_power_watt.append(pa_to_watt(pa_value))                        Article.append(article)                    else:                        # \u041f\u043e\u0438\u0441\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0432\u0430\u0442\u0442\u0430\u0445                        watt_match = watt_pattern.search(item)                        if watt_match:                            device_power_watt.append(float(watt_match.group(1)))                            Article.append(article)                        else:                            # \u0415\u0441\u043b\u0438 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c np.nan                            device_power_watt.append(None)                            Article.append(article)            except Exception as e:                device_power_watt.append(None)                Article.append(article)        df_device_power_watt = pd.DataFrame({'Article': Article, 'device_power_watt': device_power_watt})        print('get device power watt')        # \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0439\u043d\u044b\u0439 \u0441\u0440\u043e\u043a ('Warranty_period')        warranty_days = []        pattern = re.compile(r'(\\d+|\\b[\u041e\u043e]\u0434\u0438\u043d\\b|\\b[\u041e\u043e]\u0434\u043d\u043e\u0433\u043e\\b|\\b[\u041f\u043f]\u043e\u043b\u0433\u043e\u0434\u0430\\b|\\b1\\b|\\( \u043e\u0434\u0438\u043d\\)|\\(\u043e\u0434\u0438\u043d\\))[\\s\\-\u00b7\/]*([\u0433\u0413]\u043e\u0434|[\u043cM][\u0435\u0415\u0435]\u0441(?:[\u0435\u0415\u0435]\u0446)?|\u0434[\u0435\u0415\u0435]\u043d|\u0433|\u0433\\.|\u043c|\u0434\u043d\u0435\u0439|\u0434\u0435\u043d\u044c|\u0434\u043d\u0435\u0439)',                             re.IGNORECASE)        for entry in df['Warranty_period']:            try:                if pd.isna(entry):                    warranty_days.append(None)                    continue                match = pattern.search(entry)                if match:                    value, unit = match.groups()                    if value.lower() in ['\u043e\u0434\u0438\u043d', '\u043e\u0434\u043d\u043e\u0433\u043e', '1', '( \u043e\u0434\u0438\u043d)', '(\u043e\u0434\u0438\u043d)']:                        value = 1                    elif value.lower() == '\u043f\u043e\u043b\u0433\u043e\u0434\u0430':                        value = 0.5 * 12                    else:                        value = int(value)                    if '\u0433\u043e\u0434' in unit.lower() or '\u0433' in unit.lower():                        days = value * 365                    elif '\u043c\u0435\u0441' in unit.lower() or '\u043c' in unit.lower():                        days = value * 30                    elif '\u0434\u0435\u043d' in unit.lower() or '\u0434\u043d\u0435\u0439' in unit.lower() or '\u0434\u0435\u043d\u044c' in unit.lower():                        days = value                    else:                        days = None                    warranty_days.append(days)                else:                    warranty_days.append(None)            except Exception as e:                warranty_days.append(None)        df_warranty_days = df[['Article']].assign(warranty_days = warranty_days)        print('get warranty days')        # \u0415\u043c\u043a\u043e\u0441\u0442\u044c \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430 ('Battery_capacity')        battery_capacity_measure = []        battery_capacity = []        # \u0420\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f        mAh_pattern = re.compile(r'(?:\u043c|m)[\\s\\-*\u00b7\/]*[A\u0430][\\s\\-*\u00b7\/]*[h\u0447]', re.IGNORECASE)        Ah_pattern = re.compile(r'[A\u0430][\\s\\-*\u00b7\/]*[h\u0447]', re.IGNORECASE)        #V_pattern = re.compile(r'[\u0412v]', re.IGNORECASE)        # \u0420\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439        number_pattern = re.compile(r'\\d+(?:[\\.,]\\d+)?')        for item in df.Battery_capacity:            try:                if isinstance(item, str):                    # \u041f\u043e\u0438\u0441\u043a \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f                    if mAh_pattern.search(item):                        battery_capacity_measure.append('mAh')                    elif Ah_pattern.search(item):                        battery_capacity_measure.append('Ah')                    else:                        battery_capacity_measure.append(None)                    # \u041f\u043e\u0438\u0441\u043a \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439                    numbers = number_pattern.findall(item)                    if numbers:                        battery_capacity.append(float(numbers[0].replace(',', '.')))                    else:                        battery_capacity.append(None)                else:                    battery_capacity_measure.append(None)                    battery_capacity.append(None)            except Exception as e:                print(repr(e))                print(item)                battery_capacity_measure.append(None)                battery_capacity.append(None)        df_battery_capacity = df[['Article']].assign(battery_capacity=battery_capacity)                                .assign(battery_capacity_measure=battery_capacity_measure)        df_battery_capacity['battery_capacity_mAh'] = np.where(df_battery_capacity.battery_capacity_measure==\"Ah\",                 df_battery_capacity.battery_capacity*1000,                 df_battery_capacity.battery_capacity)        df_battery_capacity = df_battery_capacity[['Article', 'battery_capacity_mAh']]        print('get battery capacity')        new_df = (df[['Article','name','price','old_price','rate','estimate','number_page','number_position','date']]            .merge(df_country, on='Article', how='left').merge(df_brand, on='Article', how='left')            .merge(df_color, on='Article', how='left').merge(df_manual_control, on='Article', how='left')            .merge(df_voice_assistant, on='Article', how='left').merge(df_app_control, on='Article', how='left')            .merge(df_remote_control, on='Article', how='left').merge(df_wash_cleaning, on='Article', how='left')            .merge(df_dry_cleaning, on='Article', how='left').merge(df_wet_cleaning, on='Article', how='left')            .merge(df_dry_and_wet_cleaning, on='Article', how='left').merge(df_garbage_bag, on='Article', how='left')            .merge(df_container, on='Article', how='left').merge(df_aquafilter, on='Article', how='left')            .merge(df_return_to_base, on='Article', how='left').merge(df_virtual_wall, on='Article', how='left')            .merge(df_space_map, on='Article', how='left').merge(df_carped_mode, on='Article', how='left')            .merge(df_laser_sensor, on='Article', how='left').merge(df_giroscope, on='Article', how='left')            .merge(df_lidar, on='Article', how='left').merge(df_edge_detection, on='Article', how='left')            .merge(df_obstancle_detection, on='Article', how='left').merge(df_continued_after_charging, on='Article', how='left')            .merge(df_turbo_brush, on='Article', how='left').merge(df_bamper, on='Article', how='left')            .merge(df_uv_lamp, on='Article', how='left').merge(df_video_camera, on='Article', how='left')            .merge(df_self_cleaning, on='Article', how='left').merge(df_fixed_measure, on='Article', how='left')            .merge(df_device_power_watt, on='Article', how='left').merge(df_warranty_days, on='Article', how='left')            .merge(df_battery_capacity, on='Article', how='left')            # \u0412 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u0445 \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u043f\u0440\u043e\u043f\u0443\u0441\u043a \u043d\u0430 0            .fillna({'manual_control': 0, 'voice_assistant': 0, 'app_control': 0, 'remote_control': 0, 'wash_cleaning': 0,                'dry_cleaning': 0, 'wet_cleaning': 0, 'dry_and_wet_cleaning': 0, 'garbage_bag': 0, 'container': 0,                'aquafilter': 0, 'return_to_base': 0, 'virtual_wall': 0, 'space_map': 0, 'carped_mode': 0, 'laser_sensor': 0,                'giroscope': 0, 'lidar': 0, 'edge_detection': 0, 'obstancle_detection': 0, 'continued_after_charging': 0,                'turbo_brush': 0, 'bamper': 0, 'uv_lamp': 0, 'video_camera': 0, 'self_cleaning': 0,'rate':0,'estimate':0})            )        print('union df')        # \u041e\u0447\u0438\u0441\u0442\u0438\u043c \u043e\u0442 \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u043e\u0432        patern = '\u0414\u0435\u0442\u0441\u043a\u0438\u0439|\u0444\u0438\u043b\u044c\u0442\u0440|\u0441\u0430\u043b\u0444\u0435\u0442|\u0449\u0435\u0442\u043a|\u0422\u0440\u044f\u043f\u043a|\u0440\u0443\u0447\u043d\u043e\u0439|\u043d\u0430\u0441\u0430\u0434\u043a|\u0410\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440|\u0421\u043c\u0435\u043d\u043d|\u0424\u0438\u0431\u0440|\u0449\u0435\u0442\u043e\u043a'+                        '|\u0429\u0451\u0442\u043a|\u041a\u043e\u043c\u043f\u043b\u0435\u043a\u0442|\u041d\u0430\u043a\u043b\u0430\u0434\u043a|\u0430\u043a\u0441\u0435\u0441\u0443\u0430\u0440|\u041c\u0435\u0448\u043e\u043a|\u0434\u0430\u0442\u0447\u0438\u043a|\u0420\u0430\u043c\u043f\u0430|\u0430\u0432\u0442\u043e\u043c\u043e\u0431\u0438\u043b|\u0411\u0430\u0442\u0430\u0440\u0435\u0439\u043a\u0430'        new_df = new_df[~new_df.name.str.contains(patern, case=False)].query('price&gt;700').reset_index(drop=True).replace({np.nan: None})        print('cleaned df')        return new_df.to_dict()    @task    def load_data(records):        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c DataFrame \u0438\u0437 \u0441\u043b\u043e\u0432\u0430\u0440\u044f records        df = pd.DataFrame.from_dict(records)        # \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u043e\u0432 \u0434\u043b\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0432 ClickHouse        df['Article'] = df['Article'].astype(str)        df['name'] = df['name'].astype(pd.StringDtype()).fillna('')        df['price'] = df['price'].astype('Int64')        df['old_price'] = df['old_price'].astype('float64')        df['rate'] = df['rate'].astype('float64')        df['estimate'] = df['estimate'].astype('float64')        df['number_page'] = df['number_page'].astype('Int64')        df['number_position'] = df['number_position'].astype('Int64')        df['date'] = pd.to_datetime(df['date']).dt.date        df['country_of_manufacture'] = df['country_of_manufacture'].astype(pd.StringDtype()).fillna('')        df['brand'] = df['brand'].astype(pd.StringDtype()).fillna('')        df['color'] = df['color'].astype(pd.StringDtype()).fillna('')        # \u0414\u043b\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0441 \u0442\u0438\u043f\u043e\u043c Nullable(UInt8) - \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a \u0442\u0438\u043f\u0443 Int64 \u0441 \u0437\u0430\u043c\u0435\u0449\u0435\u043d\u0438\u0435\u043c None \u043d\u0430 0        uint8_columns = [            'manual_control', 'voice_assistant', 'app_control', 'remote_control',            'wash_cleaning', 'dry_cleaning', 'wet_cleaning', 'dry_and_wet_cleaning',            'garbage_bag', 'container', 'aquafilter', 'return_to_base', 'virtual_wall',            'space_map', 'carped_mode', 'laser_sensor', 'giroscope', 'lidar',            'edge_detection', 'obstancle_detection', 'continued_after_charging',            'turbo_brush', 'bamper', 'uv_lamp', 'video_camera', 'self_cleaning'        ]        for col in uint8_columns:            df[col] = df[col].astype('Int64')        # \u0414\u043b\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0441 \u0442\u0438\u043f\u043e\u043c Nullable(Float64)        float64_columns = [            'package_length_sm', 'package_height_sm', 'package_width_sm',            'item_width_sm', 'item_height_sm', 'weight_with_packaging_g',            'dustbin_capacity_l', 'max_noise_level_db', 'device_power_watt',            'warranty_days', 'battery_capacity_mAh'        ]        for col in float64_columns:            df[col] = df[col].astype('float64')        # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c DataFrame \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u0440\u0442\u0435\u0436\u0435\u0439        records_tuples = [tuple(x) for x in df.to_numpy()]        ch_hook = ClickHouseHook(clickhouse_conn_id='clickhouse_default')        ch_hook.execute('''CREATE DATABASE IF NOT EXISTS scraper_wb''')        ch_hook.execute('''CREATE TABLE IF NOT EXISTS scraper_wb.robot_vacuum (                        Article String,                        name Nullable(String),                        price Nullable(Int64),                        old_price Nullable(Float64),                        rate Nullable(Float64),                        estimate Nullable(Float64),                        number_page Nullable(Int64),                        number_position Nullable(Int64),                        date Date,                        country_of_manufacture Nullable(String),                        brand Nullable(String),                        color Nullable(String),                        manual_control Nullable(UInt8),                        voice_assistant Nullable(UInt8),                        app_control Nullable(UInt8),                        remote_control Nullable(UInt8),                        wash_cleaning Nullable(UInt8),                        dry_cleaning Nullable(UInt8),                        wet_cleaning Nullable(UInt8),                        dry_and_wet_cleaning Nullable(UInt8),                        garbage_bag Nullable(UInt8),                        container Nullable(UInt8),                        aquafilter Nullable(UInt8),                        return_to_base Nullable(UInt8),                        virtual_wall Nullable(UInt8),                        space_map Nullable(UInt8),                        carped_mode Nullable(UInt8),                        laser_sensor Nullable(UInt8),                        giroscope Nullable(UInt8),                        lidar Nullable(UInt8),                        edge_detection Nullable(UInt8),                        obstancle_detection Nullable(UInt8),                        continued_after_charging Nullable(UInt8),                        turbo_brush Nullable(UInt8),                        bamper Nullable(UInt8),                        uv_lamp Nullable(UInt8),                        video_camera Nullable(UInt8),                        self_cleaning Nullable(UInt8),                        package_length_sm Nullable(Float64),                        package_height_sm Nullable(Float64),                        package_width_sm Nullable(Float64),                        item_width_sm Nullable(Float64),                        item_height_sm Nullable(Float64),                        weight_with_packaging_g Nullable(Float64),                        dustbin_capacity_l Nullable(Float64),                        max_noise_level_db Nullable(Float64),                        device_power_watt Nullable(Float64),                        warranty_days Nullable(Float64),                        battery_capacity_mAh Nullable(Float64)                    ) ENGINE = MergeTree()                    PRIMARY KEY (Article, date)                    ORDER BY (Article, date)''')        ch_hook.execute('INSERT INTO scraper_wb.robot_vacuum VALUES', records_tuples)    df = scraping_data()    transformed_df = preprocessing_data(df)    load_data(transformed_df) robot_vacuum__scraper_wb = robot_vacuum__scraper_wb()<\/code><\/pre>\n<\/div>\n<\/details>\n<h3>\u0417\u0430\u0434\u0430\u0447\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0440\u0435\u0448\u0430\u0435\u043c<\/h3>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043c\u044b \u0441\u0442\u0440\u043e\u0438\u043c ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 (Extract, Transform, Load) \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u0440\u043e\u0431\u043e\u0442\u0430\u0445-\u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430\u0445 \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430. \u042d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u044b\u0445 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0446\u0435\u043d \u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a, \u0447\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u043e\u0433\u043e \u0446\u0435\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f. \u041d\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435, \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u0432 Airflow, \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0432 ClickHouse \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e BI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430 Apache Superset. <\/p>\n<h3>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b<\/h3>\n<p><strong>Airflow<\/strong> \u2014 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 DAG (Directed Acyclic Graph), \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043a\u0430\u0436\u0434\u044b\u0439 Task \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. pandas \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043e\u0447\u0438\u0449\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0434 \u0432\u0441\u0442\u0430\u0432\u043a\u043e\u0439 \u0432 ClickHouse.<\/p>\n<p><strong>ClickHouseHook<\/strong> \u2014 \u043c\u043e\u0434\u0443\u043b\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 ClickHouse \u0432 Airflow. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 ClickHouse.<\/p>\n<p><strong>Requests<\/strong> \u2014 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u0443, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p><strong>BeautifulSoup<\/strong> \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 HTML \u0438 XML \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446. \u041e\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u0430, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0443\u0436\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 HTML-\u043a\u043e\u0434\u043e\u043c, \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u043c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, Selenium.<\/p>\n<p><strong>Selenium<\/strong> \u2014 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0435\u0431-\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c JavaScript-\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0441\u0430\u0439\u0442\u0430. \u0415\u0441\u043b\u0438 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0441\u0430\u0439\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JavaScript \u0438\u043b\u0438 \u0438\u043c\u0435\u0435\u0442 \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u043e Selenium \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u0435\u043d. \u0421 \u0435\u0433\u043e \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e:<\/p>\n<ul>\n<li>\n<p>\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0434\u043e \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0432\u0430\u0440\u044b.<\/p>\n<\/li>\n<li>\n<p>\u041a\u043b\u0438\u043a\u0430\u0442\u044c \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0438, \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u043a\u0440\u044b\u0442\u044b\u0435 \u0441\u0435\u043a\u0446\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0432\u0435\u0431-\u0441\u043a\u0440\u0435\u0439\u043f\u0438\u043d\u0433\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Selenium \u0438 BeautifulSoup \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<ul>\n<li>\n<p>Selenium \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 JavaScript, \u043a\u043b\u0438\u043a\u0430\u0435\u0442 \u043f\u043e \u043a\u043d\u043e\u043f\u043a\u0430\u043c, \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043f\u043e\u043b\u044f \u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p>BeautifulSoup \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 HTML-\u043a\u043e\u0434 \u0443\u0436\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0435\u0440\u0435\u0437 <a href=\"http:\/\/driver.page\" rel=\"noopener noreferrer nofollow\">driver.page<\/a>_source \u0432 Selenium) \u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u043e \u0438 \u0431\u044b\u0441\u0442\u0440\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044f \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c HTML \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0445 \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u043e\u0432, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a find, find_all, select, \u0447\u0442\u043e \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0435\u0442 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043f\u043e \u0434\u0435\u0440\u0435\u0432\u0443 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Selenium \u0438 BeautifulSoup:<\/p>\n<pre><code class=\"python\">from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.get(\"URL\") driver.execute_script(\"window.scrollTo(0, document.body.scrollHeight);\") html = driver.page_source driver.quit() soup = BeautifulSoup(html, \"html.parser\") articles = soup.find_all('article') for article in articles:    title = article.find('span', class_='product-title').text    price = article.find('span', class_='product-price').text    print(f\"Title: {title}, Price: {price}\")<\/code><\/pre>\n<blockquote>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0443 ETL \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c, \u043d\u043e \u0432\u0430\u0436\u043d\u043e \u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e Selenium \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043d\u0430\u043b\u0438\u0447\u0438\u044f \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u0438 WebDriver.<\/p>\n<\/blockquote>\n<h3>\u0420\u0430\u0437\u0431\u043e\u0440 DAG: \u0442\u0440\u0438 \u044d\u0442\u0430\u043f\u0430 ETL<\/h3>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u0435\u0441\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u043b\u043e\u0433\u0438\u0438 Airflow \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <strong>DAG<\/strong> (Directed Acyclic Graph), \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0440\u0451\u0445 \u044d\u0442\u0430\u043f\u043e\u0432 \u2014 <strong>tasks<\/strong>. \u041a\u0430\u0436\u0434\u044b\u0439 task \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430:<\/p>\n<ol>\n<li>\n<p><code>extract_data<\/code> \u2014 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0441\u0430\u0439\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p><code>transform_data<\/code> \u2014 \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><code>load_data<\/code> \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0432 ClickHouse.<\/p>\n<\/li>\n<\/ol>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0438\u0445 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<h4>Task 1: extract_data<\/h4>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 extract_data \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0441\u0430\u0439\u0442\u0430. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c HTTP-\u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 URL \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u0430 \u0437\u0430\u0442\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<p>\u0410 \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435:<\/p>\n<ol>\n<li>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0435\u0431-\u0434\u0440\u0430\u0439\u0432\u0435\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 (\u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043a\u044d\u0448\u0430, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043a\u0443\u043a\u043e\u0432, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b) \u0438 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u043e\u043c\u0443 \u0441\u043b\u043e\u0432\u0443 (&#8216;\u0440\u043e\u0431\u043e\u0442-\u043f\u044b\u043b\u0435\u0441\u043e\u0441&#8217;).<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0435 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 (\u0434\u043e 10). slow_scroll \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u043d\u0433, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u0442\u0430\u043a \u043a\u0430\u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0442\u043e\u0432\u0430\u0440\u044b \u043f\u043e \u043c\u0435\u0440\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438. \u041d\u0430 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 BeautifulSoup \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 HTML-\u043a\u043e\u0434\u0430, \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u0432\u0430\u0440\u044b, \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 DataFrame.<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0445\u043e\u0434 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0442\u043e\u0432\u0430\u0440\u0443. \u041a\u0430\u0436\u0434\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0442\u043e\u0432\u0430\u0440 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435, \u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u043e\u0432\u0430\u0440\u0430 \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043a\u043d\u043e\u043f\u043a\u0430 &#171;\u0412\u0441\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435&#187;, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u0414\u0430\u043b\u0435\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043f\u0430\u0440\u0441\u0438\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e BeautifulSoup, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a \u0440\u0430\u0437\u043c\u0435\u0440\u044b, \u0432\u0435\u0441, \u0442\u0438\u043f \u0444\u0438\u043b\u044c\u0442\u0440\u0430 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0435\u0442\u0430\u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 (\u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435, \u0434\u0430\u0442\u0430 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445). \u041f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439 DataFrame \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0442\u0430\u0441\u043a\u0430\u0445.<\/p>\n<\/li>\n<\/ol>\n<h4>\u041f\u043e\u0447\u0435\u043c\u0443 DataFrame \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c<\/h4>\n<p>\u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f DataFrame \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043c\u0435\u0436\u0434\u0443 \u0442\u0430\u0441\u043a\u0430\u043c\u0438 \u0432 Airflow \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043e\u0431\u043e\u0439\u0442\u0438 \u0440\u044f\u0434 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b DAG.<\/p>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u044d\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c \u0441 XCom: Airflow \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 XCom (cross-communication) \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0435\u0436\u0434\u0443 \u0442\u0430\u0441\u043a\u0430\u043c\u0438. XCom \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0432 JSON (\u0441\u043f\u0438\u0441\u043a\u0438, \u0441\u043b\u043e\u0432\u0430\u0440\u0438, \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0447\u0438\u0441\u043b\u0430). \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 DataFrame \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 JSON-\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442, \u0442\u0430\u043a\u043e\u0439 \u043a\u0430\u043a \u0441\u043b\u043e\u0432\u0430\u0440\u044c.<\/p>\n<p>\u0432\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c DataFrame \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f), \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u044c \u0438 \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438.<\/p>\n<p>\u0432-\u0442\u0440\u0435\u0442\u044c\u0438\u0445, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u044f \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u0430\u043a \u043a\u0430\u043a XCom \u0438\u043c\u0435\u0435\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043d\u0430 \u0440\u0430\u0437\u043c\u0435\u0440 \u0445\u0440\u0430\u043d\u0438\u043c\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 DataFrame \u0447\u0435\u0440\u0435\u0437 XCom<\/p>\n<pre><code class=\"python\">@task def extract_data():    df = pd.DataFrame(...)  # \u0421\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435    return df.to_dict()     # \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0434\u043b\u044f XCom @task def transform_data(data):    df = pd.DataFrame.from_dict(data)  # \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c DataFrame    # \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443...<\/code><\/pre>\n<h4>Task 2: preprocessing_data<\/h4>\n<p>\u0412 \u044d\u0442\u043e\u043c Task \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0438 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043a \u043d\u0443\u0436\u043d\u044b\u043c \u0442\u0438\u043f\u0430\u043c \u0434\u043b\u044f \u0438\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 ClickHouse.<\/p>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432<\/strong><\/p>\n<p>\u042d\u0442\u043e \u043e\u043f\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u0438\u0437 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0437 &#8216;Additional_features&#8217;, &#8216;Control_type&#8217;, &#8216;Spatial_orientation&#8217;, &#8216;Cleaning_type&#8217;. \u041e\u0434\u043d\u0430 \u0438 \u0442\u0430 \u0436\u0435 \u043e\u043f\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0434\u0430\u0432\u0446\u0430 \u0443\u043a\u0430\u0437\u0430\u043d\u0430 \u0432 \u043e\u0434\u043d\u043e\u043c \u043f\u043e\u043b\u0435, \u0430 \u0443 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u2014 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c. \u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u043e\u043f\u0446\u0438\u0438 \u0437\u0430\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u044b &#171;\u043c\u0435\u0448\u043a\u0438 \u0441\u043b\u043e\u0432&#187; \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 <code>pandas.Series.str.contains()<\/code>.<\/p>\n<p>\u041a\u0430\u0436\u0434\u043e\u043c\u0443 \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0443 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u0438\u043d\u0430\u0440\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 (\u0444\u043b\u0430\u0433) \u0434\u043b\u044f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430 (1 \u0438\u043b\u0438 0 \u2014 \u0435\u0441\u0442\u044c \u043e\u043f\u0446\u0438\u044f \u0438\u043b\u0438 \u043d\u0435\u0442).<\/p>\n<p>\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u041f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a (&#8216;scheduler&#8217;)<\/p>\n<\/li>\n<li>\n<p>USB-\u0437\u0430\u0440\u044f\u0434\u043a\u0430 (&#8216;usb_charging&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0443\u0431\u043e\u0440\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0434\u0437\u0430\u0440\u044f\u0434\u043a\u0438 (&#8216;continued_after_charging&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0442 \u043d\u0430 \u0431\u0430\u0437\u0443 (&#8216;return_to_base&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u043f\u043e\u043c\u043e\u0449\u043d\u0438\u043a (&#8216;voice_assistant&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430 (&#8216;smartphone_control&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0443\u043b\u044c\u0442\u043e\u043c (&#8216;remote_control&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0414\u0438\u0441\u0442\u0430\u043d\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 (&#8216;distance_control&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u043b\u0430\u0436\u043d\u0430\u044f \u0443\u0431\u043e\u0440\u043a\u0430 (&#8216;wet_cleaning&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0442\u0435\u043d\u0430 (&#8216;virtual_wall&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b (&#8216;room_map&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0434\u043b\u044f \u043a\u043e\u0432\u0440\u0430, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u0432\u0440\u0430 (&#8216;carped_mode&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u041b\u0430\u0437\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u0442\u0447\u0438\u043a\u0438 (&#8216;laser_sensor&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0413\u0438\u0440\u043e\u0441\u043a\u043e\u043f (&#8216;giroscope&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u041b\u0438\u0434\u0430\u0440 (&#8216;lidar&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0411\u043e\u043b\u044c\u0448\u0438\u0435 \u043a\u043e\u043b\u0435\u0441\u0430 (&#8216;big_wheels&#8217;)<\/p>\n<\/li>\n<li>\n<p>WiFi (&#8216;wifi&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0422\u0443\u0440\u0431\u043e\u0449\u0435\u0442\u043a\u0430 (&#8216;turbo_brush&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u043c\u043f\u0435\u0440 (&#8216;bamper&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0423\u0424-\u043b\u0430\u043c\u043f\u0430 (&#8216;uv_lamp&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0421\u0430\u043c\u043e\u043e\u0447\u0438\u0441\u0442\u043a\u0430 (&#8216;self_cleaning&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0442\u0435\u043a\u0446\u0438\u044f \u043a\u0440\u0430\u044f, \u0441\u0442\u0443\u043f\u0435\u043d\u0435\u043a, \u043f\u0435\u0440\u0435\u043f\u0430\u0434\u0430 \u0432\u044b\u0441\u043e\u0442\u044b (&#8216;edge_detection&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u0439 (&#8216;obstancle_detection&#8217;)<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432, \u0443 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u044b<\/strong><\/p>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u044b \u0432\u0441\u0435 \u0432\u0435\u043b\u0438\u0447\u0438\u043d\u044b \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\u0445 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435. \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0432\u044b\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430 \u044d\u0442\u0438 \u0433\u0440\u0430\u043d\u0438\u0446\u044b, \u0437\u0430\u043c\u0435\u043d\u0435\u043d\u044b \u043d\u0430 \u043f\u0440\u043e\u043f\u0443\u0441\u043a (None). \u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0441 \u044d\u0442\u0438\u043c\u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u044e \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0438\u0445 \u043d\u0430 \u043c\u043e\u0434\u0443, \u043c\u0435\u0434\u0438\u0430\u043d\u0443, \u0441\u0440\u0435\u0434\u043d\u0435\u0435.<\/p>\n<p>\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0414\u043b\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (&#8216;Package_length&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0441\u043e\u0442\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (&#8216;Package_height&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0428\u0438\u0440\u0438\u043d\u0430 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0438 (&#8216;Package_width&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0428\u0438\u0440\u0438\u043d\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430 (&#8216;Item_width&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0441\u043e\u0442\u0430 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430 (&#8216;Item_height&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u0412\u0435\u0441 \u0442\u043e\u0432\u0430\u0440\u0430 \u0441 \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u043e\u0439 (\u0433) (&#8216;Weight_with_packaging_g&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u044a\u0435\u043c \u043f\u044b\u043b\u0435\u0441\u0431\u043e\u0440\u043d\u0438\u043a\u0430 (&#8216;Dustbin_capacity&#8217;)<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0437\u0432\u0443\u043a\u0430\/\u0448\u0443\u043c\u0430 (&#8216;Max_noise_level&#8217;)<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439<\/strong><\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430 \u0431\u044b\u043b\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0448\u0438\u0440\u043e\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>re<\/code>.<\/p>\n<p>\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u041c\u043e\u0449\u043d\u043e\u0441\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 (&#8216;Device_power&#8217;) \u2014 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0430 \u0432 \u0412\u0442, \u041f\u0430, \u043f\u0440\u0438\u0447\u0451\u043c \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u043c\u0438, \u043a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0435\u0439 \u0438\u043b\u0438 \u043b\u0430\u0442\u0438\u043d\u0438\u0446\u0435\u0439, \u0441 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0438\u043c\u0432\u043e\u043b\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0439\u043d\u044b\u0439 \u0441\u0440\u043e\u043a (&#8216;Warranty_period&#8217;) \u2014 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432 \u0434\u043d\u044f\u0445, \u0433\u043e\u0434\u0430\u0445, \u043c\u0435\u0441\u044f\u0446\u0430\u0445 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c \u0442\u0435\u043a\u0441\u0442\u043e\u043c;<\/p>\n<\/li>\n<li>\n<p>\u0415\u043c\u043a\u043e\u0441\u0442\u044c \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u0430 (&#8216;Battery_capacity&#8217;) \u2014 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432 \u043c\u0410\u0447, \u0410\u0447.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0432 \u043e\u0434\u0438\u043d DataFrame \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0430<\/strong><\/p>\n<p>\u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0430\u043a\u0441\u0435\u0441\u0441\u0443\u0430\u0440\u044b \u0434\u043b\u044f \u043f\u044b\u043b\u0435\u0441\u043e\u0441\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044e \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u043a (&#171;\u0449\u0435\u0442\u043a\u0438&#187;, &#171;\u0442\u0440\u044f\u043f\u043a\u0438&#187; \u0438 \u043f\u0440.) \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0435\u043c \u043f\u043e \u0446\u0435\u043d\u0435 (\u043d\u0435 \u043d\u0438\u0436\u0435 700 \u0440\u0443\u0431.). \u0414\u043b\u044f \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c <code>NaN<\/code> \u043d\u0430 0 (\u0433\u0434\u0435 \u044d\u0442\u043e \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439 \u0441 <code>UInt8<\/code> \u0442\u0438\u043f\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c 0 \u0438\u043b\u0438 1), \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0445 \u2014 \u043d\u0430 \u043f\u0443\u0441\u0442\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 <code>''<\/code>. \u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0442\u0430\u0441\u043a\u0443.<\/p>\n<h4>Task 3: load_data<\/h4>\n<p>\u042d\u0442\u043e\u0442 Task \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043e\u0447\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 ClickHouse. \u0412 \u043d\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u0435\u0449\u0435 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>ClickHouseHook<\/code> \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0432\u0441\u0442\u0430\u0432\u043a\u0443 \u0432 \u043d\u0435\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. <\/p>\n<h4>\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b Airflow \u0438 \u0438\u0445 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430<\/h4>\n<p>\u0412 Airflow \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u043e\u0434\u0445\u043e\u0434\u0430 \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0442\u0430\u0441\u043a\u043e\u0432: \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <code>PythonOperator<\/code> \u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u0412 \u044d\u0442\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b <code>@dag<\/code> \u0438 <code>@task<\/code>. \u041e\u043d\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043e\u0434 \u0447\u0438\u0449\u0435 \u0438 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435.<\/p>\n<h4>\u041f\u0440\u0438\u043c\u0435\u0440 1: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 PythonOperator<\/h4>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u0435\u043d \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c, \u043d\u043e \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0430\u0441\u043a\u0430. \u041a\u043b\u044e\u0447\u0435\u0432\u044b\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u0432 <code>PythonOperator<\/code>.<\/p>\n<pre><code class=\"python\">from airflow import DAG from airflow.operators.python import PythonOperator from datetime import datetime import pandas as pd # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447 def task_1():    print(\"\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f task_1\") def task_2(data):    print(\"\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f task_2\") # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c DAG with DAG(dag_id=\"example_python_operator_dag\", start_date=datetime(2025, 1, 1), schedule_interval=\"@daily\") as dag:    t1 = PythonOperator(        task_id=\"task_1\",        python_callable=task_1,    )    t2 = PythonOperator(        task_id=\"task_2\",        python_callable=task_2,        op_args=[\"\u0417\u0434\u0435\u0441\u044c \u043c\u043e\u0433\u0443\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 task_1\"]    )    t1 &gt;&gt; t2<\/code><\/pre>\n<h4>\u041f\u0440\u0438\u043c\u0435\u0440 2: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432<\/h4>\n<p>Airflow 2.x \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0441\u043a\u043e\u0432, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043a\u043e\u0434 \u043f\u0440\u043e\u0449\u0435 \u0438 \u043b\u0435\u0433\u0447\u0435 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u043c. \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b \u0443\u043f\u0440\u043e\u0449\u0430\u044e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u0435\u0436\u0434\u0443 \u0442\u0430\u0441\u043a\u0430\u043c\u0438 \u0447\u0435\u0440\u0435\u0437 XCom, \u0438 \u043a\u043e\u0434 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0431\u043e\u043b\u0435\u0435 \u00ab\u0447\u0438\u0441\u0442\u044b\u043c\u00bb.<\/p>\n<pre><code class=\"python\">from airflow import DAG from airflow.decorators import dag, task from datetime import datetime import pandas as pd @dag(    dag_id=\"example_taskflow_api_dag_with_dag_decorator\",    start_date=datetime(2025, 1, 1),    schedule_interval=\"@daily\",    catchup=False ) def example_taskflow_dag():    @task    def task_1():        print(\"\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f task_1\")        data = {\"key\": \"value\"}        return data    @task    def task_2(data):        print(f\"\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f task_2 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438: {data}\")    data_from_task_1 = task_1()    task_2(data_from_task_1) example_dag = example_taskflow_dag()<\/code><\/pre>\n<h3>\u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 PythonOperator \u0438 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p>\u0410\u0441\u043f\u0435\u043a\u0442<\/p>\n<\/th>\n<th>\n<p>PythonOperator<\/p>\n<\/th>\n<th>\n<p>\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u044b (<code>@task<\/code>)<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0427\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u043a\u043e\u0434\u0430<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0434\u043b\u0438\u043d\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0411\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0439 \u043a\u043e\u0434<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u041f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043d\u043d\u044b\u0445<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u042f\u0432\u043d\u0430\u044f (XCom \u0438\u043b\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e)<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0447\u0435\u0440\u0435\u0437 XCom<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0413\u0438\u0431\u043a\u043e\u0441\u0442\u044c<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0412\u044b\u0441\u043e\u043a\u0430\u044f<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 ETL-\u043a\u0435\u0439\u0441\u043e\u0432<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0421\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0441\u0435\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 Airflow<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0422\u043e\u043b\u044c\u043a\u043e \u0432 Airflow 2.x<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u041b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0421\u043b\u043e\u0436\u043d\u044b\u0435\/\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0442\u0430\u0441\u043a\u0438<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u0440\u043e\u0441\u0442\u044b\u0435 DAG \u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u044b<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h4>\u0412\u044b\u0432\u043e\u0434<\/h4>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440\u043e\u0432 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f ETL-\u043a\u0435\u0439\u0441\u043e\u0432, \u0433\u0434\u0435 \u0432\u0430\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0430\u0441\u043a\u0430 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439. <code>PythonOperator<\/code> \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438\u043b\u0438 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432.<\/p>\n<h4>\u0417\u0430\u043f\u0443\u0441\u043a \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u0430<\/h4>\n<p><strong>\u0421\u043e\u0432\u0435\u0442: <\/strong>\u0414\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0434\u043b\u044f \u0441\u043a\u0440\u0430\u043f\u0438\u043d\u0433\u0430, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 DAG.<\/p>\n<p>\u0421\u043a\u0440\u0438\u043f\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<pre><code class=\"bash\">scp &lt;\u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u043a \u0441\u043a\u0440\u0438\u043f\u0442\u0443&gt; &lt;\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c&gt;@&lt;\u0445\u043e\u0441\u0442&gt;:\/opt\/beget\/airflow\/dags\/&lt;\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430&gt;<\/code><\/pre>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0440:<\/strong><\/p>\n<pre><code class=\"bash\">scp scripts\/ETL_robot_vacuum__scraper_wb.py root@31.128.45.77:\/opt\/beget\/airflow\/dags\/ETL_robot_vacuum__scraper_wb.py<\/code><\/pre>\n<p>\u0412\u0441\u0435 DAG-\u0444\u0430\u0439\u043b\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u044b \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>\/opt\/beget\/airflow\/dags<\/code>.<\/p>\n<p>\u0427\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442 Airflow \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u0438\u0442 DAG, \u0435\u0441\u043b\u0438 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 \u043d\u0435\u0442 \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0448\u0438\u0431\u043e\u043a. \u041e\u043d \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 DAG&#8217;\u043e\u0432 \u043d\u0430 Dashboard.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3a5\/214\/5ac\/3a52145acf6ff2c13d3d9e1d4cdf5425.jpg\" width=\"1280\" height=\"366\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3a5\/214\/5ac\/3a52145acf6ff2c13d3d9e1d4cdf5425.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3a5\/214\/5ac\/3a52145acf6ff2c13d3d9e1d4cdf5425.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0427\u0442\u043e \u0431\u044b \u043e\u043d \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0441\u044f \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0432 <code>schedule_interval<\/code> \u0432\u0440\u0435\u043c\u044f, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>Apache Airflow \u0438\u043c\u0435\u0435\u0442 \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0432\u0438\u0434\u0436\u0435\u0442\u043e\u0432 \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u0434\u043b\u044f \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 (DAGs \u0438\u043b\u0438 \u0414\u0430\u0433\u043e\u0432). \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Airflow \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438\u043d\u0442\u0443\u0438\u0442\u0438\u0432\u0435\u043d. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0436\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0435 \u043d\u0435\u0443\u0432\u0435\u0440\u0435\u043d\u043d\u043e\u0441\u0442\u044c, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0438\u043b\u0438 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0435 \u0433\u0430\u0439\u0434\u044b, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0435 \u0438\u043c\u0435\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u044d\u0442\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u043e\u0442 \u0442\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0437\u0430 3 \u0434\u043d\u044f:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/654\/634\/172\/65463417286a5df466ca79321a828d7c.jpg\" width=\"1280\" height=\"637\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/654\/634\/172\/65463417286a5df466ca79321a828d7c.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/654\/634\/172\/65463417286a5df466ca79321a828d7c.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>Airflow \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u044b\u0435 \u0432\u0438\u0434\u0436\u0435\u0442\u044b \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447:<\/p>\n<ul>\n<li>\n<p><strong>Graph View<\/strong> (\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u044d\u043a\u0440\u0430\u043d\u0430): \u044d\u0442\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u0438\u0434 DAG, \u0433\u0434\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044b \u0437\u0430\u0434\u0430\u0447\u0438 (<code>scraping_data<\/code>, <code>preprocessing_data<\/code> \u0438 <code>load_data<\/code>), \u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0441\u0442\u0430\u0442\u0443\u0441. \u0426\u0432\u0435\u0442\u043e\u0432\u0430\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 \u0437\u0430\u0434\u0430\u0447: \u0437\u0435\u043b\u0435\u043d\u044b\u0439 \u0434\u043b\u044f <code>success<\/code> (\u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435), \u043a\u0440\u0430\u0441\u043d\u044b\u0439 \u0434\u043b\u044f <code>failed<\/code> (\u043e\u0448\u0438\u0431\u043a\u0430), \u0438 \u043e\u0440\u0430\u043d\u0436\u0435\u0432\u044b\u0439 \u0434\u043b\u044f <code>upstream_failed<\/code> (\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0430\u0441\u044c \u043e\u0448\u0438\u0431\u043a\u043e\u0439). \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0431\u044b\u043b\u0438 \u043d\u0435 \u0443\u0434\u0430\u0447\u043d\u044b\u043c\u0438, \u0430 \u0432 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 2 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0432\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 DAG \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b, \u043e \u0447\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u0437\u0435\u043b\u0435\u043d\u044b\u0439 \u0446\u0432\u0435\u0442.<\/p>\n<\/li>\n<li>\n<p><strong>Task Duration<\/strong> (\u043b\u0435\u0432\u0430\u044f \u043f\u0430\u043d\u0435\u043b\u044c): \u044d\u0442\u043e\u0442 \u0432\u0438\u0434\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0448\u043a\u0430\u043b\u0435. \u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u043e\u0441\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043d\u0430 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 <code>scraping_data<\/code>, <code>preprocessing_data<\/code> \u0438 <code>load_data<\/code>. \u041a\u0440\u0430\u0441\u043d\u044b\u0435 \u043f\u043e\u043b\u043e\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0438 \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0445 \u0437\u0430\u0434\u0430\u0447. \u041f\u043e\u043b\u0435\u0437\u043d\u043e \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447, \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u044f\u0432\u0438\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0438\u0437 \u043d\u0438\u0445 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0442\u0430\u0441\u043a \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 <strong>Logs<\/strong> \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043b\u043e\u0433\u0438 (\u0441\u0440\u0435\u0434\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0448\u0438 \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0442\u044b). \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u0442\u0430\u0441\u043a\u0435 <code>preprocessing_data<\/code> \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443 <code>ETAIL:  Token \"NaN\" is invalid.<\/code><\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/15e\/b36\/4c5\/15eb364c53199c4b328c95ec95e5bb54.jpg\" width=\"1280\" height=\"532\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/15e\/b36\/4c5\/15eb364c53199c4b328c95ec95e5bb54.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/15e\/b36\/4c5\/15eb364c53199c4b328c95ec95e5bb54.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 <strong>XCom<\/strong> \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0442\u0430\u0441\u043a\u0430 <code>scraping_data<\/code> \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432 \u0442\u0430\u0441\u043a 2 \u0438 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e <code>nan<\/code> \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442. \u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0445\u043e\u0442\u044f \u0431\u044b\u043b\u0430 \u0441\u0434\u0435\u043b\u0430\u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 <code>df.where(pd.notnull(df), None)<\/code> \u043f\u0435\u0440\u0435\u0434 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0442\u0430\u0441\u043a\u0430, \u043d\u043e \u043e\u043d\u0430 \u043d\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 <code>None<\/code> \u0434\u043b\u044f <code>int<\/code> \u0438 <code>float<\/code> (\u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 <code>rate<\/code>, <code>estimate<\/code>, <code>old_price<\/code>). \u0415\u0441\u043b\u0438 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0438\u043c\u0435\u0435\u0442 \u0442\u0438\u043f \u0434\u0430\u043d\u043d\u044b\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 <code>None<\/code> (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>int64<\/code> \u0438\u043b\u0438 <code>float64<\/code>), \u0442\u043e <code>None<\/code> \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d \u0432 \u044d\u0442\u0438\u0445 \u044f\u0447\u0435\u0439\u043a\u0430\u0445. \u0412 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 <code>None<\/code> \u0431\u0443\u0434\u0435\u0442 \u043b\u0438\u0431\u043e \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u043b\u0438\u0431\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0430 <code>NaN<\/code>. <\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/168\/7f8\/7c2\/1687f87c26d31181643b6a31c1b486ed.jpg\" width=\"1280\" height=\"540\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/168\/7f8\/7c2\/1687f87c26d31181643b6a31c1b486ed.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/168\/7f8\/7c2\/1687f87c26d31181643b6a31c1b486ed.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c <code>DataFrame<\/code> \u043a \u0442\u0438\u043f\u0443 <code>object<\/code>, \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">df = df.astype(object).where(pd.notnull(df), None)<\/code><\/pre>\n<p>\u0411\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u043e \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0435\u043a\u0443\u043d\u0434 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b, \u0437\u0430\u0439\u0434\u044f \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0443 Code:<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f22\/ab8\/fb8\/f22ab8fb8ea805165aa8360a05845a41.jpg\" width=\"1280\" height=\"545\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/f22\/ab8\/fb8\/f22ab8fb8ea805165aa8360a05845a41.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f22\/ab8\/fb8\/f22ab8fb8ea805165aa8360a05845a41.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043d\u0435\u0441\u043e\u043c\u043d\u0435\u043d\u043d\u043e\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e Airflow \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u043e\u043c cron, \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438, \u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043b\u043e\u0433\u0438 \u0438 \u0438\u0441\u0442\u043e\u0440\u0438\u044e \u0437\u0430\u043f\u0443\u0441\u043a\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a.<\/p>\n<p>\u0421\u043e\u0432\u0435\u0442: \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0435 \u043f\u043e\u043b\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043f\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043e\u0431\u0445\u043e\u0434 \u0447\u0430\u0441\u0442\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0442\u043e\u0432\u0430\u0440\u0430, \u0430 \u043d\u0435 \u0432\u0441\u0435\u0445.<\/p>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0435\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043a\u0440\u0438\u043f\u0442\u0430 &#8212; \u043a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043f\u043e\u043b\u0435\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0439.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3e2\/43e\/14b\/3e243e14b0b3703eb0ca694ca504017d.jpg\" width=\"1280\" height=\"587\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3e2\/43e\/14b\/3e243e14b0b3703eb0ca694ca504017d.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3e2\/43e\/14b\/3e243e14b0b3703eb0ca694ca504017d.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h2>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Superset<\/h2>\n<h3>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 Superset<\/h3>\n<p>Apache Superset \u2014 \u044d\u0442\u043e \u043c\u043e\u0449\u043d\u0430\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u043e\u0432. \u0412 Superset \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: datasets (\u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445), charts (\u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b), queries (\u0437\u0430\u043f\u0440\u043e\u0441\u044b) \u0438 dashboards (\u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b). \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0438\u0437 \u043d\u0438\u0445 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435.<\/p>\n<ul>\n<li>\n<p><strong>Datasets (\u041d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445)<\/strong> \u2014 \u044d\u0442\u043e \u0441\u043e\u0431\u043e\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0438 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u043e\u0432.<\/p>\n<p>\u0412 Superset \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0442\u0438\u043f\u0430 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<ul>\n<li>\n<p><strong>\u0424\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (Physical datasets):<\/strong> \u044d\u0442\u043e \u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u043a \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c \u0438\u043b\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u043c (views) \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445. \u041e\u043d\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437 ClickHouse, PostgreSQL \u0438 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043c\u044b\u0445 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445).<\/p>\n<\/li>\n<li>\n<p><strong>\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 (Virtual datasets):<\/strong> \u044d\u0442\u043e \u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u0430\u0431\u043e\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u0435\u0437\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0430\u0433\u0440\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0434 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439. \u041e\u043d\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 SQL Lab, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>Charts (\u0414\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b)<\/strong> \u2014 \u044d\u0442\u043e \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445. \u041a\u0430\u0436\u0434\u0430\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0432\u044b\u0431\u043e\u0440\u0430 \u043c\u0435\u0442\u0440\u0438\u043a, \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432.<\/p>\n<\/li>\n<li>\n<p><strong>Queries (\u0417\u0430\u043f\u0440\u043e\u0441\u044b)<\/strong> \u2014 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439. \u0412 SQL Lab \u043c\u043e\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u044b. \u042d\u0442\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c. \u0417\u0430\u043f\u0440\u043e\u0441\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u0430\u043a \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c\u0438, \u0434\u043b\u044f \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u043e\u0432, \u0442\u0430\u043a \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u043c\u0438, \u0435\u0441\u043b\u0438 \u043d\u0430 \u0438\u0445 \u043e\u0441\u043d\u043e\u0432\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p><strong>Dashboards (\u0414\u0430\u0448\u0431\u043e\u0440\u0434\u044b)<\/strong> \u2014 \u044d\u0442\u043e \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u044b\u0435 \u043f\u0430\u043d\u0435\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439 \u0432 \u043e\u0434\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0447\u0435\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438. \u041d\u0430 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430\u0445 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u043c\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u0449\u0438\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u0434\u0430\u0442\u0435) \u0438\u043b\u0438 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b.<\/p>\n<\/li>\n<\/ul>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Superset \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439<\/h3>\n<h4>\u0428\u0430\u0433 1. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a ClickHouse<\/h4>\n<ul>\n<li>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Superset \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 <strong>Settings \u2192 Database Connections<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c4c\/e30\/fdd\/c4ce30fdd451428d13073c5c5015732a.jpg\" width=\"1280\" height=\"540\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/c4c\/e30\/fdd\/c4ce30fdd451428d13073c5c5015732a.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c4c\/e30\/fdd\/c4ce30fdd451428d13073c5c5015732a.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 <strong>+ Database<\/strong> \u0432 \u043f\u0440\u0430\u0432\u043e\u043c \u0432\u0435\u0440\u0445\u043d\u0435\u043c \u0443\u0433\u043b\u0443.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9cd\/977\/c7d\/9cd977c7d8aec03a5a8530e4938f4e12.png\" width=\"1837\" height=\"472\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/9cd\/977\/c7d\/9cd977c7d8aec03a5a8530e4938f4e12.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9cd\/977\/c7d\/9cd977c7d8aec03a5a8530e4938f4e12.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0412 \u0441\u043f\u0438\u0441\u043a\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 <strong>ClickHouse Connect<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/78b\/b42\/57e\/78bb4257eba56f51feed5ccf509e2b08.jpg\" width=\"513\" height=\"874\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/78b\/b42\/57e\/78bb4257eba56f51feed5ccf509e2b08.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/78b\/b42\/57e\/78bb4257eba56f51feed5ccf509e2b08.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0444\u043e\u0440\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u0443\u043a\u0430\u0437\u0430\u0432 URI \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/14e\/001\/298\/14e001298ccc00f7c13210c90056864a.png\" width=\"511\" height=\"849\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/14e\/001\/298\/14e001298ccc00f7c13210c90056864a.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/14e\/001\/298\/14e001298ccc00f7c13210c90056864a.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Test Connection<\/strong>, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9ae\/7e1\/2c9\/9ae7e12c9c8f03a1db400d50776afa1f.png\" width=\"507\" height=\"466\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/9ae\/7e1\/2c9\/9ae7e12c9c8f03a1db400d50776afa1f.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9ae\/7e1\/2c9\/9ae7e12c9c8f03a1db400d50776afa1f.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<ul>\n<li>\n<p>\u041f\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u043d\u0430\u0436\u043c\u0438\u0442\u0435 <strong>Connect<\/strong> \u0434\u043b\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438.<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/67b\/b08\/672\/67bb0867261ce3ef0045d236dae53a13.png\" width=\"1840\" height=\"429\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/67b\/b08\/672\/67bb0867261ce3ef0045d236dae53a13.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/67b\/b08\/672\/67bb0867261ce3ef0045d236dae53a13.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e Superset \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442 \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<h4>\u0428\u0430\u0433 2. \u0421\u0442\u0440\u043e\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u044b<\/h4>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 ClickHouse \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <strong>SQL Lab<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 SQL-\u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u043d\u0443\u0436\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0438\u043b\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044e.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 \u0432\u0438\u0434\u0435 <strong>\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>\u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u043b\u043e\u0436\u043d\u044b\u0445 SQL-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a\u0430\u043a \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3cd\/e81\/31d\/3cde8131d327af1b4c2f5703bf36306d.png\" width=\"1840\" height=\"855\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/3cd\/e81\/31d\/3cde8131d327af1b4c2f5703bf36306d.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3cd\/e81\/31d\/3cde8131d327af1b4c2f5703bf36306d.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h3>\u0428\u0430\u0433 3. \u0421\u0442\u0440\u043e\u0438\u043c \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b \u0438 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u044b<\/h3>\n<ul>\n<li>\n<p>\u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0438\u043b\u0438 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 <strong>\u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b (charts)<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0442\u0438\u043f\u044b \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0439: \u0433\u0440\u0430\u0444\u0438\u043a\u0438, \u0442\u0430\u0431\u043b\u0438\u0446\u044b, \u043a\u0440\u0443\u0433\u043e\u0432\u044b\u0435 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b \u0438 \u0434\u0440.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c\u044b \u043d\u0430 \u043e\u0434\u043d\u043e\u043c <strong>\u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0435 (dashboard)<\/strong>.<\/p>\n<\/li>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u2014 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e \u0434\u0430\u0442\u0435) \u0438\u043b\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0445 \u0434\u0438\u0430\u0433\u0440\u0430\u043c\u043c.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430.<\/p>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/778\/06b\/1ca\/77806b1ca989260099aeddc0b7f0cbd1.png\" width=\"1812\" height=\"873\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/778\/06b\/1ca\/77806b1ca989260099aeddc0b7f0cbd1.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/778\/06b\/1ca\/77806b1ca989260099aeddc0b7f0cbd1.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/161\/37d\/0a1\/16137d0a1aec07d196e6acccf3440a0d.png\" width=\"1807\" height=\"874\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/161\/37d\/0a1\/16137d0a1aec07d196e6acccf3440a0d.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/161\/37d\/0a1\/16137d0a1aec07d196e6acccf3440a0d.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/225\/968\/082\/225968082f57c6f1a916ab449bc4b113.png\" width=\"1804\" height=\"744\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/225\/968\/082\/225968082f57c6f1a916ab449bc4b113.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/225\/968\/082\/225968082f57c6f1a916ab449bc4b113.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a5c\/fab\/b8d\/a5cfabb8d408b916712fd6d8b57d9d29.png\" width=\"961\" height=\"624\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/a5c\/fab\/b8d\/a5cfabb8d408b916712fd6d8b57d9d29.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a5c\/fab\/b8d\/a5cfabb8d408b916712fd6d8b57d9d29.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Superset, Airflow \u0438 ClickHouse \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u0435\u0439\u0441\u0430 \u2014 \u0441\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u0440\u043e\u0431\u043e\u0442\u0430\u0445-\u043f\u044b\u043b\u0435\u0441\u043e\u0441\u0430\u0445 \u0441 \u0441\u0430\u0439\u0442\u0430 Wildberries. \u041c\u044b \u043f\u0440\u043e\u0448\u043b\u0438 \u043f\u0443\u0442\u044c \u043e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f DAG \u0438 \u0441\u043a\u0440\u0430\u043f\u0438\u043d\u0433\u0430, \u0434\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043e\u0447\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 ClickHouse \u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u043e\u0432 \u0432 Superset.<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0441\u0442\u0435\u043a \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d: \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0431\u043e\u0440 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u043a\u0435 \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0444\u043e\u0440\u043c\u0435. Airflow \u0434\u0430\u0451\u0442 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c, ClickHouse \u2014 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u0430 Superset \u2014 \u043c\u043e\u0449\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0438\u0441\u0430\u0442\u044c \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u044d\u0442\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440 \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0438 \u043f\u043e\u0441\u043b\u0443\u0436\u0438\u0442 \u0431\u0430\u0437\u043e\u0439 \u0434\u043b\u044f \u0432\u0430\u0448\u0438\u0445 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0438 \u0430\u043d\u0430\u043b\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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\/928712\/\"> https:\/\/habr.com\/ru\/articles\/928712\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0430 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0432\u044f\u0437\u043a\u0443 Superset, Airflow \u0438 ClickHouse. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u044f \u0432\u0437\u044f\u043b <a href=\"https:\/\/beget.com\/ru\/cloud\/marketplace\/supersetairflow\" rel=\"noopener noreferrer nofollow\">\u0433\u043e\u0442\u043e\u0432\u0443\u044e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u043e\u0442 Beget, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0443\u044e Superset \u0438 Airflow \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438<\/a> \u2014 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u043d\u0435 \u043d\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u044b\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u0442\u043e\u0432\u0430\u0440\u0430\u0445 \u0441 \u0441\u0430\u0439\u0442\u0430 Wildberries.<\/p>\n<p>\u0414\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Python-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <code>selenium<\/code> \u0438 <code>BeautifulSoup<\/code> \u2014 \u043e\u043d\u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0434\u043b\u044f \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c <code>re<\/code> \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.<\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 ETL<\/h2>\n<p>ETL (Extract \u2014 Transform \u2014 Load) \u2014 \u044d\u0442\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0449\u0438\u0439 \u0442\u0440\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u044d\u0442\u0430\u043f\u0430:<\/p>\n<ul>\n<li>\n<p><strong>\u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 (Extract):<\/strong> \u043d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0438\u0437 \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u0432 \u2014 \u0431\u0443\u0434\u044c \u0442\u043e \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, API, \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438\u043b\u0438 \u0444\u0430\u0439\u043b\u044b.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435 (Transform):<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0447\u0438\u0449\u0430\u044e\u0442\u0441\u044f, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442\u0441\u044f \u043a \u043d\u0443\u0436\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 (Load):<\/strong> \u0434\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u2014 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e ClickHouse, \u0433\u0434\u0435 \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Superset.<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0441 \u043e\u0441\u043d\u043e\u0432\u0430\u043c\u0438 ETL, \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u2014 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Airflow \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430. <\/p>\n<h2>\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f Airflow<\/h2>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0441\u0440\u0435\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f Python-\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u2014 \u044d\u0442\u043e Airflow, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 Docker-\u043e\u0431\u0440\u0430\u0437. \u041c\u044b \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e, \u0430 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0443\u044e \u0441\u0431\u043e\u0440\u043a\u0443 \u043d\u0430 \u0431\u0430\u0437\u0435 <code>apache\/airflow:2.8.2<\/code>.<\/p>\n<h3>\u041a\u0440\u0430\u0442\u043a\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043a\u0430 \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 Airflow 2.8.2<\/h3>\n<p>Airflow \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432:<\/p>\n<p>Airflow 2.8.2 (\u0432\u0435\u0440\u0441\u0438\u044f, \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u043d\u0430 \u043c\u043c\u043e\u0435\u043d\u0442 \u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u0441\u0442\u0430\u0442\u044c\u0435\u0439) \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0441\u0432\u043e\u0435\u0433\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 Docker \u0438\u043b\u0438 Docker Compose \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c. \u0412\u043e\u0442 \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438 \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u0435\u043b\u0430\u0435\u0442:<\/p>\n<ol>\n<li>\n<p><strong>Webserver<\/strong> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c DAG&#8217;\u0438, \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u044b, \u043b\u043e\u0433\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u043e\u043c \u0437\u0430\u0434\u0430\u0447 \u0438 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u043a\u0430\u043c\u0438. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043d\u0430 \u0431\u0430\u0437\u0435 Flask, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>Scheduler<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u044b, \u0438 \u0438\u0445 \u0440\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 DAG&#8217;\u043e\u0432 \u0438 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u043e\u0432 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Worker(s)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u0420\u0430\u0431\u043e\u0447\u0438\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 DAG&#8217;\u043e\u0432. \u0412 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u0438\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u043e\u043c. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Celery Executor, \u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e worker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Database (Postgres\/MySQL)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u0421\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445 Airflow, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 DAG&#8217;\u043e\u0432, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447, \u043b\u043e\u0433\u0438 \u0438 \u0434\u0440\u0443\u0433\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u0412 Docker Compose \u043e\u0431\u044b\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f PostgreSQL, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p><strong>Redis (\u0438\u043b\u0438 RabbitMQ)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u042f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u0440\u043e\u043a\u0435\u0440\u043e\u043c \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 \u0437\u0430\u0434\u0430\u0447 \u0434\u043b\u044f Celery Executor. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0437\u0430\u0434\u0430\u0447 \u043e\u0442 \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0430 \u043a \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: Redis \u0438\u043b\u0438 RabbitMQ \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e\u0442 \u0432 \u0440\u043e\u043b\u0438 message broker, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<\/li>\n<li>\n<p><strong>Triggerer (\u0435\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f)<\/strong> \u0417\u0430\u0434\u0430\u0447\u0430: \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0441\u0435\u043d\u0441\u043e\u0440\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, AsyncSensor), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0431\u0435\u0437 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432. \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435: \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0434\u043b\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u043e\u043b\u0433\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0439.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0421\u0431\u043e\u0440\u043a\u0430 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430<\/h3>\n<pre><code class=\"bash\">cd \/opt\/beget\/airflow<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c <code>Dockerfile<\/code>:<\/p>\n<pre><code class=\"bash\">FROM apache\/airflow:2.8.2 USER root RUN apt-get update &amp;&amp; \\    apt-get install -y wget unzip &amp;&amp; \\    wget https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    apt install -y .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    rm -f .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\    export LATEST_VERSION=$(curl -s https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE) &amp;&amp; \\    wget -O \/tmp\/chromedriver.zip \"https:\/\/chromedriver.storage.googleapis.com\/$LATEST_VERSION\/chromedriver_linux64.zip\" &amp;&amp; \\    unzip \/tmp\/chromedriver.zip -d \/usr\/local\/bin\/ &amp;&amp; \\    rm \/tmp\/chromedriver.zip &amp;&amp; \\    rm -rf \/var\/lib\/apt\/lists\/* USER airflow RUN pip3 install pandas numpy selenium webdriver-manager beautifulsoup4<\/code><\/pre>\n<p>\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0437 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e:<\/p>\n<pre><code class=\"bash\">docker build -t local\/airflow:2.8.2 .<\/code><\/pre>\n<p>\u041c\u0435\u043d\u044f\u0435\u043c <code>image:<\/code> \u0432 <code>docker-compose.yml<\/code> \u0438 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c:<\/p>\n<pre><code class=\"bash\">docker-compose down &amp;&amp; docker-compose up -d<\/code><\/pre>\n<h3>\u041f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f \u043a RUN<\/h3>\n<p>\u042d\u0442\u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0432 Dockerfile \u0432 \u0431\u043b\u043e\u043a\u0435 \u0441 \u043f\u0435\u0440\u0432\u044b\u043c RUN, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c Google Chrome \u0438 ChromeDriver \u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440. \u0412\u043e\u0442 \u0440\u0430\u0437\u0431\u043e\u0440 \u043f\u043e \u0448\u0430\u0433\u0430\u043c:<\/p>\n<ol>\n<li>\n<p><code>RUN apt-get update &amp;&amp; \\<\/code> \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435. \u042d\u0442\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u0430\u0445 \u0438\u0437 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u0439.<\/p>\n<\/li>\n<li>\n<p><code>apt-get install -y wget unzip &amp;&amp; \\<\/code> \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u044b: <code>wget<\/code>: \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u043e HTTP, HTTPS \u0438 FTP. <code>unzip<\/code>: \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438 ZIP-\u0430\u0440\u0445\u0438\u0432\u043e\u0432. \u0424\u043b\u0430\u0433 <code>-y<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 apt-get \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c &#171;\u0434\u0430&#187; \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435.<\/p>\n<\/li>\n<li>\n<p><code>wget <\/code><a href=\"https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/dl.google.com\/linux\/direct\/google-chrome-stable_current_amd64.deb<\/code><\/a><code> &amp;&amp; \\<\/code> \u0421\u043a\u0430\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u043a\u0435\u0442 .deb, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e Google Chrome \u0434\u043b\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b amd64 (64-\u0431\u0438\u0442\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f).<\/p>\n<\/li>\n<li>\n<p><code>apt install -y .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\<\/code> \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f Google Chrome \u0438\u0437 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0430\u043a\u0435\u0442\u0430 <code>.deb<\/code>. \u0424\u043b\u0430\u0433 <code>-y<\/code> \u0441\u043d\u043e\u0432\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443.<\/p>\n<\/li>\n<li>\n<p><code>rm -f .\/google-chrome-stable_current_amd64.deb &amp;&amp; \\<\/code> \u0423\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u0430\u043a\u0435\u0442\u0430 <code>.deb<\/code> \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0441\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p><code>export LATEST_VERSION=$(curl -s <\/code><a href=\"https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/chromedriver.storage.googleapis.com\/LATEST_RELEASE<\/code><\/a><code>) &amp;&amp; \\<\/code> \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f <code>LATEST_VERSION<\/code> \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0440\u0430\u0432\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 ChromeDriver. <code>curl -s<\/code> \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0431\u0435\u0437 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 (\u0444\u043b\u0430\u0433 <code>-s<\/code> \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 &#171;silent&#187; \u2014 \u0431\u0435\u0437 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430).<\/p>\n<\/li>\n<li>\n<p><code>echo \"Latest ChromeDriver version: $LATEST_VERSION\" &amp;&amp; \\<\/code> \u0412\u044b\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0441 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 ChromeDriver, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.<\/p>\n<\/li>\n<li>\n<p><code>wget -O <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> \"<\/code><a href=\"https:\/\/chromedriver.storage.googleapis.com\/%24LATEST_VERSION\/chromedriver_linux64.zip\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/chromedriver.storage.googleapis.com\/$LATEST_VERSION\/chromedriver_linux64.zip<\/code><\/a><code>\" &amp;&amp; \\<\/code> \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f ZIP-\u0430\u0440\u0445\u0438\u0432 \u0441 ChromeDriver \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <code>\/tmp\/<\/code> \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0435\u043c <a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\">chromedriver.zip<\/a>.<\/p>\n<\/li>\n<li>\n<p><code>unzip <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> -d <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/usr\/local\/bin<\/code><\/a><code>\/ &amp;&amp; \\<\/code> \u0420\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 <a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a> \u0432 \u043a\u0430\u0442\u0430\u043b\u043e\u0433 <a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/usr\/local\/bin<\/code><\/a><code>\/<\/code>, \u0447\u0442\u043e\u0431\u044b ChromeDriver \u0431\u044b\u043b \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 PATH.<\/p>\n<\/li>\n<li>\n<p><code>rm <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/tmp\/<\/code><\/a><a href=\"http:\/\/chromedriver.zip\" rel=\"noopener noreferrer nofollow\"><code>chromedriver.zip<\/code><\/a><code> &amp;&amp; \\<\/code> \u0423\u0434\u0430\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 ZIP-\u0444\u0430\u0439\u043b \u043f\u043e\u0441\u043b\u0435 \u0435\u0433\u043e \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u043c\u0435\u0441\u0442\u043e.<\/p>\n<\/li>\n<li>\n<p><code>rm -rf <\/code><a href=\"https:\/\/colab.research.google.com\/drive\/1hvWNykXagnLploMXfx6VUu0vACLazPIN#\" rel=\"noopener noreferrer nofollow\"><code>\/var\/lib\/apt\/lists<\/code><\/a><code>\/*<\/code> \u0423\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 <code>apt-get<\/code>, \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435, \u044d\u0442\u043e\u0442 \u0431\u043b\u043e\u043a \u043a\u043e\u043c\u0430\u043d\u0434 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 Google Chrome \u0438 ChromeDriver \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0447\u0438\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0447\u0442\u043e \u0432\u0430\u0436\u043d\u043e \u0434\u043b\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0438 \u043c\u0435\u0441\u0442\u0430 \u0438 \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u044f \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430.<\/p>\n<h2>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 Airflow<\/h2>\n<h3>\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b<\/h3>\n<p>\u041e\u0431 airflow \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e, \u043d\u043e \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043c\u0441\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0441\u043f\u0440\u0430\u0432\u043a\u043e\u0439, \u0430 \u0434\u0430\u043b\u0435\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0435\u0433\u043e \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u0435\u0439\u0441\u0430.<\/p>\n<p>Apache Airflow \u2014 \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043f\u043b\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430 \u0440\u0430\u0431\u043e\u0447\u0438\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 (DAGs \u2014 Directed Acyclic Graphs). \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0432 Airflow \u044d\u0442\u043e DAG. \u041e\u043d \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447 \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.<\/p>\n<p>DAG \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442:<\/p>\n<ul>\n<li>\n<p><strong>\u0417\u0430\u0434\u0430\u0447\u0438 (Tasks)<\/strong>: \u041e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b. \u0417\u0430\u0434\u0430\u0447\u0438 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0432\u0438\u0441\u0435\u0442\u044c \u0434\u0440\u0443\u0433 \u043e\u0442 \u0434\u0440\u0443\u0433\u0430, \u0438 \u044d\u0442\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u044b\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 \u0433\u0440\u0430\u0444\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u0420\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u0435 (Schedule)<\/strong>: DAG \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043f\u043e \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u043d\u0438\u044e (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0436\u0435\u0434\u043d\u0435\u0432\u043d\u043e \u0432 9 \u0443\u0442\u0440\u0430) \u0438\u043b\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<\/li>\n<li>\n<p><strong>\u0410\u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440<\/strong>: DAG \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0446\u0438\u043a\u043b\u043e\u0432, \u0442.\u0435. \u043d\u0435\u043b\u044c\u0437\u044f \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043b\u0430\u043d \u0442\u0430\u043a\u043e\u0439: \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u0448 \u043a\u0435\u0439\u0441 \u043f\u043e ETL, \u0442\u043e \u043f\u0435\u0440\u0432\u043e\u0435 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e &#8212; &#8230; \ud83e\udd41 &#8230; <code>extract<\/code>. \u0410 \u0437\u0430\u0442\u0435\u043c \u0443\u0436\u0435 <code>transform<\/code> \u0438 <code>load<\/code>.<\/p>\n<p>\u041d\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u043a\u0430\u043a\u0443\u044e \u0442\u043e \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u0430 \u043f\u0440\u044f\u043c\u043e \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430 wildberries, \u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u0441\u0435\u0445 \u0440\u043e\u0431\u043e\u0442\u043e\u0432-\u043f\u044b\u043b\u0435\u0441\u043e\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u0442\u0430\u043c \u043d\u0430\u0439\u0434\u0435\u043c, \u0441 \u0438\u0445 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 \u0438 \u0446\u0435\u043d\u0430\u043c\u0438. \u0412\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043e\u0447\u0438\u0441\u0442\u043a\u0430, \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435), \u0430 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 ClickHouse. \u0412\u0441\u0435 \u044d\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434y\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430 python \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0432 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u043d\u0430 vps.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 DAG\u0430 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438. \u041e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u043e, \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430.<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from airflow import DAG from airflow.decorators import dag, task from airflow_clickhouse_plugin.hooks.clickhouse import ClickHouseHook from datetime import datetime, timedelta import pandas as pd import numpy as np import re from time import sleep from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u043f\u0446\u0438\u0439 Chrome options = webdriver.ChromeOptions() options.add_argument('--headless')  # \u0420\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0444\u043e\u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 options.add_argument('window-size=1920x1080')  # \u0417\u0430\u0434\u0430\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u043e\u043a\u043d\u0430 options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--disable-gpu')  # \u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c GPU \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 options.add_argument('--incognito') # \u0420\u0435\u0436\u0438\u043c \u0438\u043d\u043a\u043e\u0433\u043d\u0438\u0442\u043e default_args = {    'depends_on_past': False,    'retries': 1,    'retry_delay': timedelta(minutes=5),    'start_date': datetime(2024, 10, 29)} schedule_interval = '0 9 * * *' def get_page_content(articles):    '''    \u0424\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u043e\u0434\u0435\u0436\u0438\u043c\u043e\u0433\u043e \u0442\u0435\u0433\u043e\u0432 article,    \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043e\u0434\u0435\u0436\u0438\u0442\u0441\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0438\u0437 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0442\u043e\u0432\u0430\u0440\u0430 \u043f\u0440\u0438 \u0431\u044b\u0441\u0442\u0440\u043e\u043c \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-467333","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/467333","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=467333"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/467333\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=467333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=467333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=467333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}