Пишем инструменты командной строки на Python с помощью Click
Интерфейсы командной строки — эффективная вещь, так как они позволяют автоматизировать практически всё что угодно. Сегодня мы расскажем, как написать такой интерфейс на Python с помощью Click.
Python — невероятно гибкий язык программирования, который хорошо интегрируется с существующими программами. Немало Python-кода написано в виде скриптов и интерфейсов командной строки (CLI).
Инструменты и интерфейсы командной строки — эффективная вещь, так как они позволяют автоматизировать практически всё что угодно. Как следствие, эти интерфейсы с течением времени могут стать довольно сложными.
Обычно всё начинается с простого скрипта на Python, который что-то делает. Например, получает доступ к веб-API и выводит результат в консоль:
# print_user_agent.py import requests json = requests.get('http://httpbin.org/user-agent').json() print(json['user-agent'])
Вы можете запустить этот скрипт с помощью команды python3 print_user_agent.py , и он выведет имя user-agent, использованного для вызова API.
Как и было сказано, довольно простой скрипт.
Но что делать, когда подобная программа растёт и становится всё более сложной?
Решением этого вопроса мы сегодня и займёмся. Вы узнаете об основах написания интерфейсов командной строки на Python и о том, как click позволяет упростить этот процесс.
Используя эти знания, мы шаг за шагом перейдём от простого скрипта к интерфейсу командной строки с аргументами, опциями и полезными инструкциями по использованию. Всё это мы сделаем с помощью библиотеки click.
К концу этой статьи вы будете знать:
- Почему click — лучшая альтернатива argparse и optparse;
- Как с его помощью создать простой CLI;
- Как добавить обязательные аргументы командной строки в ваши скрипты;
- Как парсить флаги и опции командной строки;
- Как сделать ваши консольные приложения более удобными, добавив справочный текст.
Вы увидите, как сделать всё это с минимальным количеством шаблонного кода.
Примечание переводчика Код в данной статье написан на Python 3.6, работоспособность на более ранних версиях не гарантируется.
Зачем вам писать скрипты и инструменты для командной строки на Python?
Код выше — всего лишь пример, не очень полезный в реальной жизни. На самом деле скрипты бывают куда более сложные. Возможно, вы имели опыт с ними и знаете, что они могут быть важной частью нашей повседневной работы: некоторые скрипты остаются на протяжении всего времени жизни проекта, для которого они были написаны. Некоторые начинают приносить пользу другим командам или проектам. У них даже может расширяться функционал.
В этих случаях важно сделать скрипты более гибкими и настраиваемыми с помощью параметров командной строки. Они позволяют указать имя сервера, учётные данные или любую другую информацию скрипту.
Здесь приходят на выручку такие модули, как optparse и argparse, которые делают нашу жизнь на порядок проще. Но прежде чем мы с ними познакомимся, давайте разберёмся с терминологией.
Основы интерфейса командной строки
Интерфейс командной строки (CLI) начинается с имени исполняемого файла. Вы вводите имя в консоль и получаете доступ к главной точке входа скрипта, такого как pip.
В зависимости от сложности CLI обычно есть определённые параметры, которые вы можете передавать скрипту:
- Аргумент, который является обязательным параметром. Если его не передать, то CLI вернёт ошибку. Например, в следующей команде click является аргументом: pip install click .
- Опция — необязательный параметр, который объединяет имя и значение, например —cache-dir ./my-cache . Вы говорите CLI, что значение ./my-cache должно использоваться как директория для кэша.
- Флаг, который включает или выключает определённый сценарий. Вероятно, самым частым является —help . Вы только указываете имя, а CLI самостоятельно интерпретирует значение.
С более сложными CLI, такими как pip или Heroku CLI, вы получаете доступ к набору функций, которые собраны под главной точкой входа. Они обычно называются командами или подкомандами.
Возможно, вы уже использовали CLI, когда устанавливали Python-библиотеку с помощью команды pip install . Команда install говорит CLI, что вы хотите использовать функцию установки пакета, и даёт вам доступ к параметрам, характерным для этой функции.
Пакеты для работы с командной строкой, доступные в стандартной библиотеке Python 3.x
Добавление команд и параметров в ваши скрипты может сделать их значительно лучше, но парсить командную строку не так просто, как может показаться. Однако вместо того, чтобы пытаться самостоятельно решить эту проблему, лучше воспользоваться одним из многих пакетов, которые сделали это за вас.
Два наиболее известных пакета для этого — optparse и argparse. Они являются частью стандартной библиотеки Python и добавлены туда по принципу «всё включено».
По большей части они делают одно и то же и работают схожим образом. Главное отличие заключается в том, что optparse не используется начиная с Python 3.2, и argparse считается стандартом для создания CLI в Python.
Вы можете узнать о них больше в документации Python, но, чтобы иметь представление, как выглядит скрипт с argparse, посмотрите на пример ниже:
import argparse parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('integers', metavar='N', type=int, nargs='+', help='an integer for the accumulator') parser.add_argument('--sum', dest='accumulate', action='store_const', const=sum, default=max, help='sum the integers (default: find the max)') args = parser.parse_args() print(args.accumulate(args.integers))
click против argparse: лучшая альтернатива?
Вероятно, вы смотрите на этот код и думаете: «Что это всё значит?» И это является одной из проблем argparse: код с ним неинтуитивен и сложночитаем.
Поэтому вам может понравиться click.
Click решает ту же проблему, что и optparse и argparse, но немного иначе. Он использует декораторы, поэтому ваши команды должны быть функциями, которые можно обернуть этими декораторами.
С click легко создавать многофункциональный CLI с небольшим количеством кода. И этот код будет легко читаться, даже когда ваш CLI вырастет и станет более сложным.
Пишем простой CLI на Python с помощью click
Вдоволь поговорив о CLI и библиотеках, давайте взглянем на пример, чтобы понять, как написать простой CLI с click. Как и в первом примере, мы создаём простой CLI, который выводит результат в консоль. Это несложно:
# cli.py import click @click.command() def main(): print("I'm a beautiful CLI ✨") if __name__ == "__main__": main()
Не пугайтесь последних двух строк: это то, как Python запускает функцию main при исполнении файла как скрипта.
Как вы видите, всё, что нам нужно сделать — создать функцию и добавить к ней декоратор @click.command() . Он превращает функцию в команду, которая является главной точкой входа нашего скрипта. Теперь вы можете запустить скрипт через командную строку и увидеть что-то вроде этого:
$ python3 cli.py I'm a beautiful CLI ✨
Что в click здорово, так это то, что мы получаем некоторые дополнительные возможности просто так. Мы не реализовывали справочную функцию, однако вы можете добавить флаг —help и увидеть базовое сообщение:
$ python3 cli.py --help Usage: cli.py [OPTIONS] Options: --help Show this message and exit.
Более реалистичный пример CLI на Python с использованием click
Теперь, когда вы знаете, как click упрощает написание CLI, давайте взглянем на более реалистичный пример. Мы напишем программу, которая позволяет нам взаимодействовать с веб-API.
API, который мы дальше будем использовать, — OpenWeatherMap API. Он предоставляет информацию о текущей погоде, а также прогноз на пять дней для определённого местоположения. Мы начнём с тестового API, который возвращает текущую погоду для места.
Прежде чем мы начнём писать код, давайте познакомимся с API. Для этого можно использовать сервис HTTPie, включая онлайн-терминал.
Давайте посмотрим, что случится, когда мы обратимся к API с Лондоном в качестве местоположения:
$ http --body GET http://samples.openweathermap.org/data/2.5/weather \ q==London \ appid==b1b15e88fa797225412429c1c50c122a1 < "base": "stations", "clouds": < "all": 90 >, "cod": 200, "coord": < "lat": 51.51, "lon": -0.13 >, "dt": 1485789600, "id": 2643743, "main": < "humidity": 81, "pressure": 1012, "temp": 280.32, "temp_max": 281.15, "temp_min": 279.15 >, "name": "London", "sys": < "country": "GB", "id": 5091, "message": 0.0103, "sunrise": 1485762037, "sunset": 1485794875, "type": 1 >, "visibility": 10000, "weather": [ < "description": "light intensity drizzle", "icon": "09d", "id": 300, "main": "Drizzle" >], "wind": < "deg": 80, "speed": 4.1 >>
Если вы смущены наличием API-ключа в примере сверху, не переживайте, это тестовый API-ключ, предоставляемый сервисом.
Более важное наблюдение заключается в том, что мы отправляем два параметра (обозначаемые == при использовании HTTPie), чтобы узнать текущую погоду:
- q — место, в котором мы хотим узнать погоду;
- appid — наш API-ключ.
Это позволяет нам создать простую реализацию на Python с использованием библиотеки requests (опустим обработку ошибок и неудачных запросов для простоты):
import requests SAMPLE_API_KEY = 'b1b15e88fa797225412429c1c50c122a1' def current_weather(location, api_key=SAMPLE_API_KEY): url = 'http://samples.openweathermap.org/data/2.5/weather' query_params = < 'q': location, 'appid': api_key, >response = requests.get(url, params=query_params) return response.json()['weather'][0]['description']
Эта функция делает простой запрос к API, используя два параметра. В качестве обязательного аргумента она принимает location (местоположение), которое должно быть строкой. Также мы можем указать API-ключ, передавая параметр api_key при вызове функции. Это необязательно, так как по умолчанию используется тестовый ключ.
И вот мы видим текущую погоду в Python REPL:
>>> current_weather('London') 'light intensity drizzle' # впрочем, ничего нового ?
Парсим обязательные параметры с click
Простая функция current_weather позволяет нам создать CLI с местоположением, указанным пользователем. Это должно работать примерно так:
$ python3 cli.py London The weather in London right now: light intensity drizzle.
Как вы, возможно, догадались, местоположение — это аргумент, поскольку оно является обязательным параметром для нашего погодного CLI.
Как нам сделать это при помощи click? Всё довольно просто, мы используем декоратор под названием argument . Кто бы мог подумать?
Давайте возьмём наш предыдущий пример и слегка изменим его, добавив аргумент location :
@click.command() @click.argument('location') def main(location): weather = current_weather(location) print(f"The weather in right now: .")
Если этот print выглядит для вас странно, не волнуйтесь — это новый способ форматирования строк в Python 3.6+, который называется f-форматированием.
Как вы видите, всё, что нам нужно сделать, это добавить дополнительный декоратор к нашей функции main и дать ему имя. Click использует имя в качестве имени аргумента, переданного обёрнутой функции.
Примечание переводчика Имя аргумента, переданное click, должно совпадать с именем аргумента в объявлении функции.
В нашем случае значение аргумента командной строки location будет передано функции main в качестве аргумента location . Логично, не так ли?
Также вы можете использовать тире в именах, например api-key , которые click переведёт в snake case для имени аргумента в функции, например main(api_key) .
Реализация main просто использует нашу функцию current_weather для получения погоды в указанном месте. И затем мы с помощью print выводим полученную информацию.
Парсим опциональные параметры с click
Как вы, возможно, догадались, тестовый API ограничивает нас в возможностях. Поэтому, прежде чем мы продолжим, зарегистрируйтесь и получите настоящий API-ключ.
Первое, что нам нужно изменить, — URL, откуда берутся данные о текущей погоде. Это можно сделать, изменив значение переменной url в функции current_weather на URL, указанный в документации OpenWeatherMap:
def current_weather(location, api_key=SAMPLE_API_KEY): url = 'https://api.openweathermap.org/data/2.5/weather' # дальше всё остаётся как было .
Это изменение приведёт к неработоспособности нашего CLI, так как указанный API-ключ не работает с реальным API. Поэтому давайте добавим новый параметр в наш CLI, который позволит нам указывать API-ключ. Но сначала мы должны решить, будет ли этот параметр аргументом или опцией. Мы сделаем его опцией, так как добавление параметра вроде —api-key делает его более явным и говорящим за себя.
Мы хотим, чтобы наша программа запускалась таким образом:
$ python3 cli.py --api-key London The weather in London right now: light intensity drizzle.
Проще простого. Посмотрим, как добавить опцию к нашей существующей команде:
@click.command() @click.argument('location') @click.option('--api-key', '-a') def main(location, api_key): weather = current_weather(location, api_key) print(f"The weather in right now: .")
И снова мы добавляем декоратор к нашей функции main . В этот раз мы используем декоратор с говорящим именем @click.option и указываем имя для нашей опции, начинающееся с двух тире. Как вы видите, мы также можем указать сокращение для нашей опции с одним тире, чтобы сэкономить пользователю немного времени.
Как было сказано ранее, click создаёт аргумент для передачи в функцию main из длинного варианта имени. В случае с опцией он убирает впередистоящие тире и переводит её в snake case. Таким образом, —api-key становится api_key .
Чтобы всё заработало, осталось лишь передать API-ключ в функцию current_weather .
Мы добавили возможность указывать свой собственный ключ и проверять погоду в любом месте:
$ python3 cli.py --api-key Canmore The weather in Canmore right now: broken clouds.
Добавляем автоматически генерируемые инструкции по использованию
Можете себя похвалить, вы создали отличный небольшой CLI почти без шаблонного кода. Однако прежде чем вы решите отдохнуть, давайте убедимся, что новый пользователь будет знать, как пользоваться нашим CLI, путём добавления документации. Не бойтесь, всё будет просто.
Сначала давайте проверим, что выведет флаг —help после всех сделанных изменений. Довольно неплохо, учитывая что мы не приложили к этому никаких усилий:
$ python3 cli.py --help Usage: cli.py [OPTIONS] LOCATION Options: -a, --api-key TEXT --help Show this message and exit.
Первое, что нужно исправить, это добавить описание для нашей опции с API-ключом. Всё, что нам для этого нужно сделать, — добавить справочный текст в декоратор @click.option :
@click.command() @click.argument('location') @click.option( '--api-key', '-a', help='your API key for the OpenWeatherMap API', ) def main(location, api_key): .
Второе (и последнее), что мы сделаем, — добавим документацию для всей click-команды. Самый простой и самый питонический способ сделать это — добавить строку документации в нашу функцию main . Да, нам в любом случае нужно сделать это, поэтому это не лишняя работа:
. def main(location, api_key): """ A little weather tool that shows you the current weather in a LOCATION of your choice. Provide the city name and optionally a two-digit country code. Here are two examples: 1. London,UK 2. Canmore You need a valid API key from OpenWeatherMap for the tool to work. You can sign up for a free account at https://openweathermap.org/appid. """ .
Сложив всё вместе, мы получаем хороший вывод для нашего инструмента:
$ python3 cli.py --help Usage: cli.py [OPTIONS] LOCATION A little weather tool that shows you the current weather in a LOCATION of your choice. Provide the city name and optionally a two-digit country code. Here are two examples: 1. London,UK 2. Canmore You need a valid API key from OpenWeatherMap for the tool to work. You can sign up for a free account at https://openweathermap.org/appid. Options: -a, --api-key TEXT your API key for the OpenWeatherMap API --help Show this message and exit.
Подводим итоги
Итак, в этом уроке мы рассмотрели много всего. Можете гордиться собой, вы написали свой собственный CLI, и всё это с минимальным количеством шаблонного кода! Исходный код ниже доказывает это. Не стесняйтесь использовать его для собственных экспериментов:
import click import requests SAMPLE_API_KEY = 'b1b15e88fa797225412429c1c50c122a1' def current_weather(location, api_key=SAMPLE_API_KEY): url = 'https://api.openweathermap.org/data/2.5/weather' query_params = < 'q': location, 'appid': api_key, >response = requests.get(url, params=query_params) return response.json()['weather'][0]['description'] @click.command() @click.argument('location') @click.option( '--api-key', '-a', help='your API key for the OpenWeatherMap API', ) def main(location, api_key): """ A little weather tool that shows you the current weather in a LOCATION of your choice. Provide the city name and optionally a two-digit country code. Here are two examples: 1. London,UK 2. Canmore You need a valid API key from OpenWeatherMap for the tool to work. You can sign up for a free account at https://openweathermap.org/appid. """ weather = current_weather(location, api_key) print(f"The weather in right now: .") if __name__ == "__main__": main()
Введение в создание приложений на Python
Python — это один из самых популярных языков программирования в мире. Он прост в изучении и использовании, и может быть использован для создания различных приложений, от консольных до веб-приложений и мобильных приложений.
Для начала работы с Python нужно установить интерпретатор Python и выбрать текстовый редактор или интегрированную среду разработки (IDE). Для создания приложений на Python необходимы также различные библиотеки и фреймворки, такие как Flask, Django, Tkinter, Kivy и др.
Приходите на наш бесплатный вебинар по разработке без кода, чтобы задать вопросы, узнать больше про зерокодинг и попробовать себя в новой профессии!
Создание консольного приложения на Python: простые шаги и лучшие практики
Консольное приложение — это приложение, которое запускается и выполняется в командной строке. Для создания консольного приложения на Python нужно создать файл с расширением .py и написать код в текстовом редакторе. В Python есть множество встроенных функций для работы с консолью, например, функции input() и print().
Для лучшей практики в разработке консольных приложений нужно следовать принципам DRY (Don’t Repeat Yourself) и KISS (Keep It Simple, Stupid). Это означает, что код должен быть читабельным и легко поддерживаемым.
Создание графического интерфейса пользователя с помощью библиотеки Tkinter
Tkinter — это стандартная библиотека для создания графического интерфейса пользователя (GUI) в Python. Она предоставляет набор виджетов и инструментов для создания интерактивных окон и диалоговых окон.

Для создания GUI на Tkinter нужно создать экземпляр класса Tk() и добавлять к нему различные виджеты, такие как кнопки, поля ввода и др. Также можно настраивать внешний вид и поведение виджетов с помощью атрибутов и методов.
Создание простого приложения на Python
Создание простого приложения на Python может быть легким и интересным заданием для начинающих разработчиков. Вот как можно создать простое приложение на Python:
Шаг 1: Создание нового файла Python
Начнем с создания нового файла Python. Откройте текстовый редактор или интегрированную среду разработки (IDE), такую как PyCharm или Visual Studio Code, и создайте новый файл с расширением «.py». Давайте назовем его «calculator.py».
Шаг 2: Написание кода приложения
Откройте файл «calculator.py» в выбранной вами IDE и добавьте следующий код:
num1 = float(input(«Введите первое число: «))
num2 = float(input(«Введите второе число: «))
В этом коде мы создаем функцию add_numbers , которая принимает два аргумента a и b и возвращает их сумму. Затем мы запрашиваем у пользователя ввод двух чисел, преобразуем их в числовой формат (используя float() ) и сохраняем их в переменные num1 и num2 . Далее мы вызываем функцию add_numbers с этими числами в качестве аргументов и сохраняем результат в переменной result . Наконец, мы выводим результат на экран.
Шаг 3: Запуск приложения
Сохраните файл «calculator.py» и запустите его. Вы можете выполнить это в своей IDE, нажав на кнопку «Запуск» или используя команду в терминале:
python calculator.py
Шаг 4: Тестирование приложения
После запуска приложение будет запрашивать у вас ввод двух чисел. Введите числа и нажмите Enter. После этого приложение выведет на экран сумму введенных чисел.
Вот пример работы приложения:
| Введите первое число: 2.5 Введите второе число: 4.3 Сумма чисел равна: 6.8 |
Это пример работы приложения, где были введены числа 2.5 и 4.3, и программа вывела сумму 6.8.
Создание простого приложения на Python может быть занимательным и познавательным опытом для тех, кто хочет начать свой путь в программировании. Хорошее знание основных конструкций языка и навыки тестирования и отладки помогут вам создать качественное приложение на Python.
На Zerocoder.ru есть курс «Создание мобильных приложений без программирования», который позволяет создать мобильное приложение без необходимости знания Python или других языков программирования. В этом курсе вы узнаете, как создать простое мобильное приложение на платформах glide и adalo.
Как создавать и публиковать консольные приложения на Python
Подробное руководство по созданию и публикации консольных приложений на Python
Published in
NOP::Nuances of Programming
11 min read
Jul 25, 2020
Консольные приложения — это те, которые вы запускаете в терминале. Скорее всего, вы уже пытались их создать. Или, по крайней мере, думали об их создании.
Но создание консольного приложения — это одно, а публикация его в репозиторий с открытым кодом (например, PyPI) — совсем другое. Хотя ни первое, ни второе не является чем-то запредельно трудным.
В этой статье я подробно расскажу, как можно создать простой CLI на Python и опубликовать его в PyPI.
Начало
Не так давно я занялся изучением уязвимостей open-source кода и понял, что хочу иметь в арсенале инструмент командной строки, который мог бы находить уязвимости напрямую из терминала. Уязвимости open-source кода обычно публикуются в открытых базах данных. Их можно найти на таких сайтах, как CVE, NVD, WhiteSource Vuln и т.д.
В этой статье мы создадим примитивный скрейпер для поиска и просмотра уязвимостей с сайта CVE, обернем его в простое консольное приложение и опубликуем на PyPI.
Для начала нужно настроить среду разработки и установить необходимые модули. Я предлагаю установить виртуальную среду. Это простое решение, которое позволит избежать конфликтов между версиями модулей.
Для создания виртуальной среды можно воспользоваться командой python -m venv (для Python 3) либо установить virtualenvwrapper с помощью pip install virtualenvwrapper и создать виртуальную среду virtualenv через mkvirtualenv -p /path/topython .
После того, как виртуальная среда настроена и активирована, вы можете создать папку проекта и установить необходимые модули:
mkvirtualenv -p /usr/bin/python cvecli-env
mkdir cvecli && cd cvecli
mkdir cver && touch setup.py && touch README.md && touch cver/__init__.py && touch .gitignore
pip install requests beautifulsoup4 lxml twine click
pip freeze > requirements.txt
Как только все успешно запустилось, откройте свой проект в любом редакторе кода. Вы увидите похожую структуру:
Создание веб-скрейпера
Для того, чтобы искать и просматривать уязвимости на сайте CVE, потребуется веб-скрейпер. Он поможет нам собирать информацию об уязвимостях. Мы создаем скрейпер в Requests и BeautifulSoup. Вот что будет делать наш скрейпер:
1. искать уязвимости;
2. получать информацию об уязвимости по ее названию на CVE.
Теперь откроем папку cver и создадим в ней файл под названием cve_scraper . Затем пропишем его базовые настройки:
import requests
from bs4 from BeautifulSoupSEARCH_URL = "https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword="CVE_URL = "https://cve.mitre.org/cgi-bin/cvename.cgi?name="def get_html(url):
request = requests.get(url)
if request.status_code == 200:
return request.content
else:
raise Exception("Bad request")def search(s):
passdef lookup_cve(name):
pass
Поиск уязвимостей
Для поиска уязвимостей на CVE используется URL в следующем формате: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword= . Такой формат позволяет извлекать список уязвимостей, соответствующих ключевому слову.
Например, через URL можно получить список всех уязвимостей, связанных с Python:
Для извлечения данных открываем инструменты разработчика ( Developer Console ) и исследуем DOM-элемент с нужным представлением. Для этого кликните правой кнопкой по любому месту на странице и выберите “исследовать элемент” ( inspect element ) либо нажмите Ctrl + F12 .
Если вы присмотритесь к DOM-структуре выше, то увидите, что результаты представлены в виде таблицы, а каждое значение указано в отдельной строке под таблицей. Такие данные можно запросто извлечь:
def search(s):
url = f""
results=[]
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#TableWithRules table tr") for row in result_rows:
_row = <>
name = row.select_one("td a")
description = row.select_one("td:nth-child(2)") if all([name, description]): _row["name"] = name.text
_row["url"] = name.get("href")
_row["description"] = description.text results.append(_row)
return results
1. отправляем запрос в SEARCH_URL с помощью Requests и получаем DOM-содержимое;
2. преобразуем DOM-содержимое в объекты BeautifulSoup . Это позволит нам выделять DOM-элементы с помощью CSS-селекторов, XPATH и других методов;
3. выделяем все tr под таблицей #TableWithRules . Выделяем первый столбец строки в качестве названия, а в качестве описания берем второй. Затем извлекаем текст.
Просмотр информации об уязвимостях
Чтобы просмотреть информацию об уязвимости, нужно взять ее CVE-ID и передать по этому адресу: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-ID.
Откройте инструменты разработчика и исследуйте DOM-структуру.
Такая структура чуть сложнее, поскольку в строках таблицы отсутствует ID или название класса. Поэтому нам нужно пройтись циклом по каждой строке и проверить, не является ли она подзаголовком. Если да, то следующий элемент берется в качестве содержимого-потомка. Каждый подзаголовок отображается в th , а его содержимое — в td .
def lookup_cve(name):
url = f""
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#GeneratedTable table tr")subtitle = ""
description = ""raw_results = <>for row in result_rows:
head = row.select_one("th")
if head:
subtitle = head.text
else:
body = row.select_one("td")
description = body.text.strip().strip("\n")
raw_results[subtitle.lower()] = description
return raw_results
Готово! Мы успешно создали веб-скрейпер с CVE. Теперь добавим в него две функции ( search и lookup_sve ), которые будут искать уязвимости и получать информацию по ним через CVE-ID .
import requests
from bs4 import BeautifulSoup
SEARCH_URL = "https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword="
CVE_URL = "https://cve.mitre.org/cgi-bin/cvename.cgi?name="
def get_html(url):
request = requests.get(url)
if request.status_code == 200:
return request.content
else:
raise Exception("Bad request")
def search(s):
url = f""
results=[]
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#TableWithRules table tr")
for row in result_rows:
_row = <>
name = row.select_one("td a")
description = row.select_one("td:nth-child(2)")
if all([name, description]):
_row["name"] = name.text
_row["url"] = name.get("href")
_row["description"] = description.text
results.append(_row)
return results
def lookup_cve(name):
url = f""
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#GeneratedTable table tr")
subtitle = ""
description = ""
raw_results = <>
for row in result_rows:
head = row.select_one("th")
if head:
subtitle = head.text
else:
body = row.select_one("td")
description = body.text.strip().strip("\n")
raw_results[subtitle.lower()] = description
return raw_results
Создание консольного приложения
Наш следующий шаг — структурирование и создание консольного приложения через библиотеку Click.
Click — это Python-пакет для создания красивых интерфейсов командной строки с минимальным количеством кода и возможностью компоновки. Это один из лучших Python-пакетов для создания CLI, и с ним очень удобно работать.
Click позволяет создавать интерфейсы командной строки любого уровня — от самых простых до навороченных (например, Heroku).
В нашем CLI мы реализуем две команды:
1. поиск уязвимости;
2. просмотр уязвимости.
В папке cver создаем файл под названием __main__.py и прописываем его базовые настройки:
import sys
import click@click.group()
@click.version_option("1.0.0")
def main():
"""A CVE Search and Lookup CLI"""
print("Hye")
pass@main.command()
@click.argument('keyword', required=False)
def search(**kwargs):
"""Search through CVE Database for vulnerabilities"""
click.echo(kwargs)
pass@main.command()
@click.argument('name', required=False)
def look_up(**kwargs):
"""Get vulnerability details using its CVE-ID on CVE Database"""
click.echo(kwargs)
passif __name__ == '__main__':
args = sys.argv
if "--help" in args or len(args) == 1:
print("CVE")
main()
Поиск уязвимостей
Здесь мы будем импортировать функцию поиска search из веб-скрейпера и передавать ей аргумент keyword из командной строки. Таким образом, приложение будет искать уязвимости, совпадающие с ключевым словом:
from scraper import search as cve_search, lookup_cve@main.command()
@click.argument('keyword', required=False)
def search(**kwargs):
"""Search through CVE Database for vulnerabilities"""
results = cve_search(kwargs.get("keyword"))
for res in results:
click.echo(f' - \n')
Для запуска этой команды:
python cver/__main__.py search python
Просмотр уязвимости
Принцип тот же: используем lookup_cve из веб-скрейпера и передаем туда аргумент name из команды look_up .
@main.command()
@click.argument('name', required=False)
def look_up(**kwargs):
"""Get vulnerability details using its CVE-ID on CVE Database"""
details = lookup_cve(kwargs.get("name"))
click.echo(f'CVE-ID \n\n\n')
click.echo(f'Description \n\n\n')
click.echo(f'References \n\n\n')
click.echo(f'Assigning CNA \n\n\n')
click.echo(f'Date Entry \n\n')
Для запуска этой команды:
python cver/__main__.py look-up CVE-2013–4238
Готово! Мы успешно создали инструмент командной строки по поиску с CVE.
Публикация консольного приложения на PyPI
После того, как мы создали консольное приложение и убедились в его работоспособности, можно опубликовать его на PyPI в публичном доступе.
PyPI — это хранилище приложений для пакетов Python. Там можно найти практически все пакеты, которые устанавливаются через pip . Для публикации пакета потребуется аккаунт на PyPI. Если он у вас уже есть, то смело читайте дальше.
Настройка пакета
Следующий шаг — это настройка Python-пакета с помощью setup.py . Если вы хотите, чтобы ваш пакет попал на PyPI, то нужно снабдить его базовым описанием. Эта информация указывается в файле setup.py .
Откройте setup.py в основной директории проекта и поместите в начало файла следующий код:
from setuptools import setup, find_packages
from io import open
from os import pathimport pathlib
# Директория, в которой содержится этот файл
HERE = pathlib.Path(__file__).parent# Текст README-файла
README = (HERE / "README.md").read_text()# Автоматически собирает в requirements.txt все модули для install_requires, а также настраивает ссылки на зависимости
with open(path.join(HERE, 'requirements.txt'), encoding='utf-8') as f:
all_reqs = f.read().split('\n')install_requires = [x.strip() for x in all_reqs if ('git+' not in x) and (
not x.startswith('#')) and (not x.startswith('-'))]
dependency_links = [x.strip().replace('git+', '') for x in all_reqs \
if 'git+' not in x]
В примере выше мы преобразовали содержимое файла README.md в одну строку для дальнейшего использования. Кроме того, мы перечислили все необходимые модули из requirements.txt и сгенерировали ссылки на их зависимости.
Ваш файл requirements.txt выглядит примерно так:
click
requests
beautifulsoup4
lxml
twine
Теперь давайте рассмотрим параметры настроек:
setup (
name = 'cver',
description = 'A simple commandline app for searching and looking up opensource vulnerabilities',
version = '1.0.0',
packages = find_packages(), # list of all packages
install_requires = install_requires,
python_requires='>=2.7', # any python greater than 2.7
entry_points='''
[console_scripts]
cver=cver.__main__:main
''',
author="Oyetoke Toby",
keyword="cve, vuln, vulnerabilities, security, nvd",
long_description=README,
long_description_content_type="text/markdown",
license='MIT',
url='https://github.com/CITGuru/cver', download_url='https://github.com/CITGuru/cver/archive/1.0.0.tar.gz',
dependency_links=dependency_links,
author_email='oyetoketoby80@gmail.com',
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
]
)
В коде выше мы добавили множество строк. Но подробнее обсудим только те из них, которые нужны для установки.
1. name — название пакета, которое появится на PyPI;
2. version — текущая версия пакета;
3. packages — пакеты и подпакеты с исходным кодом. В ходе установки мы пользуемся модулем find_packages . Он автоматически находит все подпакеты;
4. install_requires — используется для перечисления всех зависимостей или сторонних библиотек пакета. В cver мы пользуемся Requests, Beautifulsoup 4 и Click. Их нужно включить в требования к установке install_requires . Нам не нужно добавлять эту информацию вручную, поскольку она присутствует в requirements.txt ;
5. entry_points — используется для создания скриптов, которые вызывают функцию внутри пакета. В данном случае мы создаем новый скрипт cver , который вызывает main() внутри файла cver/__main__.py . Наш основной элемент — это __main__.py , который вызывает функцию main() для запуска Click.
До того, как опубликовать пакет на PyPI или выложить в открытый доступ, необходимо снабдить его документацией. То, как будет выглядеть документация, целиком и полностью зависит от самого проекта. Это может быть как простой файл README.md , так и Readme.rst .
Пример хорошо оформленного README.md :
# CVERA simple commandline app for searching and looking up opensource vulnerabilities# Установка## Через Pip```bash
$ pip install cver
```## Вручную```bash
$ git clone https://github.com/citguru/cevr
$ cd cver
$ python setup.py install
```# Использование```bash
$ cver
```## Поиск
`search ````bash
$ cver search python
```
## Просмотр`search ````bash
$ cver look-up CVE-2020-2121
```
Кроме того, не забудьте создать файл .gitignore :
# Байтовая компиляция / оптимизация / DLL-файлы
__pycache__/
*.py[cod]
*$py.class
# C-расширения
*.so
# Сборка дистрибутива / пакета
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Обычно такие файлы пишутся Python-скриптом по шаблону
# до того, как PyInstaller создаст exe. Это нужно для добавления в файл даты и прочей информации.
*.manifest
*.spec
# Логи установщика
pip-log.txt
pip-delete-this-directory.txt
# Модульные тесты / отчеты по покрытию
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Переводы
*.mo
*.pot
# Всякое на Django:
*.log
local_settings.py
db.sqlite3
# Всякое на Flask:
instance/
.webassets-cache
# Всякое на Scrapy:
.scrapy
# Sphinx-документация
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# Schedule-файл Celery Beat
celerybeat-schedule
# Проанализированные файлы SageMath
*.sage.py
# Окружения
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Настройки Spyder Project
.spyderproject
.spyproject
# Настройки Rope Project
.ropeproject
# Документация mkdocs
/site
# mypy
.mypy_cache/
from setuptools import setup, find_packages
from io import open
from os import path
import pathlib
# Директория, в которой содержится этот файл
HERE = pathlib.Path(__file__).parent
# Текст README-файла
README = (HERE / "README.md").read_text()
# Автоматически собирает в requirements.txt все модули для install_requires
with open(path.join(HERE, 'requirements.txt'), encoding='utf-8') as f:
all_reqs = f.read().split('\n')
install_requires = [x.strip() for x in all_reqs if ('git+' not in x) and (
not x.startswith('#')) and (not x.startswith('-'))]
dependency_links = [x.strip().replace('git+', '') for x in all_reqs \
if 'git+' not in x]
setup (
name = 'cver',
description = 'A simple commandline app for searching and looking up opensource vulnerabilities',
version = '1.0.0',
packages = find_packages(), # list of all packages
install_requires = install_requires,
python_requires='>=2.7', # any python greater than 2.7
entry_points='''
[console_scripts]
cver=cver.__main__:main
''',
author="Oyetoke Toby",
keyword="cve, vuln, vulnerabilities, security, nvd",
long_description=README,
long_description_content_type="text/markdown",
license='MIT',
url='https://github.com/CITGuru/cver',
download_url='https://github.com/CITGuru/cver/archive/1.0.0.tar.gz',
dependency_links=dependency_links,
author_email='oyetoketoby80@gmail.com',
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
]
)
Публикация на PyPI
Теперь ваш пакет готов к публикации на PyPI. Еще раз проверьте, есть ли у вас уже аккаунт на PyPI. Добавьте к нему тестовый аккаунт на тестовом сервере PyPI. Этот аккаунт нужен для тестирования пакетов перед публикацией их на рабочем сервере.
Загружать Python-пакеты на PyPI мы будем с помощью специального инструмента — Twine. По идее, вы уже установили его на одном из предыдущих этапов. Если нет, то это можно сделать через pip install twine .
Создание и локальное тестирование пакета на тестовом сервере
Python-пакеты, опубликованные на PyPI, не распространяются в виде «голого» кода. Они оборачиваются в дистрибутивы. Самыми распространенными форматами дистрибутивов в Python являются Wheels и Source Archives.
Wheels — это zip-архив с кодом и готовыми расширениями. Source Archives содержит исходный код и вспомогательные файлы, упакованные в tar-архив.
Для локального тестирования пакета выполните:
python setup.py install
Теперь мы можем использовать его как:
cver search python
Для проверки пакета на тестовом сервере PyPI нужно сгенерировать сборку для локального тестирования. При создании этой сборки сгенерируются архивы Wheels и Source Archives.
python setup.py sdist bdist_wheel
Код ниже сгенерирует два файла в директории dist :
cvecli/
│
└── dist/
├── cver-1.0.0-py3-none-any.whl
└── cver-1.0.0.tar.gz
Затем воспользуемся Twine. Теперь мы можем загрузить пакет на тестовый сервер PyPI:
twine upload — repository-url https://test.pypi.org/legacy/ dist/*
Затем у вас спросят логин и пароль.
Если пакет загружается на тестовый сервер без ошибок, то он готов к публикации на рабочем сервере. Проверить можно здесь.
Для установки из TestPyPI выполните следующую команду:
pip install -i https://test.pypi.org/simple/ cver==1.0.0
В тестовой среде вы можете проверить все команды и убедиться в их правильности перед публикацией пакета на рабочем сервере.
Протестировав все команды локально, переходите к публикации пакета на рабочем сервере:
twine upload dist/*
В процессе загрузки укажите свой логин и пароль. Вот и все!
Теперь пакет можно установить через:
pip install cver
Поздравляю! Ваш пакет был опубликован на PyPI. Просмотреть его можно здесь!
Заключение
В этой статье я пошагово объяснил процесс создания и публикации консольного приложения на Python.
- Проверка типов в Python
- Attr — одна из лучших практик объектно-ориентированного Python
- 15 Python пакетов, которые нужно попробовать
Практические вопросы по создания консольного приложения на Python
это обычные поля ввода данных. А как сделать в самом начале выбор языка, по типу «введите (1), если хотите начать на русском. Enter (2) if you want to continue in English». В самом начале ставить везде условный оператор if ? или есть другой способ?
Отслеживать
задан 9 мар 2022 в 18:18
33 6 6 бронзовых знаков
можно сделать вызов консольки с параметрами.
9 мар 2022 в 18:32
а можно поточнее рассказать об этом, по типу Python для самых маленьких 🙂
9 мар 2022 в 18:54
с примерами jenyay.net/Programming/Argparse
9 мар 2022 в 19:16
«интерфейс взаимодействия пользователя и ПО и как он называется?» ну вашему коду я бы назвал интерактивным интерфейсом, прога задает вопрос пользователь отвечает. Можно сделать типа сценария пишишь текстовой файл(мини ЯП) и скармливаешь своей проге. Если мне паметь не изменяет VMware может работать из командной строки тут даже батник можно замутить.
9 мар 2022 в 19:33
0
Сортировка: Сброс на вариант по умолчанию
Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.
- python
- ui
- vmware