Содержание статьи
От редакции
Недавно мы провели опрос среди читателей и выяснили, что многие хотели бы изучить Python, причем начать с самого начала. В качестве эксперимента мы опубликовали статью «Python с абсолютного нуля. Учимся кодить без скучных книжек», где рассказали об азах Python: переменных, условиях, циклах и списках. Отклики были позитивными, и мы решили продолжить знакомить читателей с Python в нашем фирменном нескучном стиле.
Эта статья, как и предыдущая, доступна без платной подписки, так что смело делись этими ссылками с друзьями, которые мечтают выучить Python!
Начнем со строк. Чтобы решить вставшую перед друзьями проблему, Чебурашка использовал функцию replace(
, которая заменяет в строке одну подстроку другой.
Сначала он объявил переменную s
и поместил туда строку, которую прислал ему Гена.
s = 'Все лето мы пили пиво. Вот как-то открываю дверь, а на пороге Чебурашка, весь такой пьяный-пьяный, и бутылка из кармана торчит.'
Дальше Чебурашка определил словарь из слов, которые требовалось заменить.
slova = {'пили':'читали', 'пиво':'книги', 'пьяный':'начитанный', 'бутылка':'энциклопедия'}
И теперь при помощи цикла for
Чебурашка перебрал словарь, чтобы заменить каждое из слов (key
) на соответствующее значение из словаря (slova[
):
for key in slova: s = s.replace(key, slova[key])print(s)
info
Словари во многом похожи на списки, но значения в них записаны парами: ключ и значение. По ключу можно узнать значение. Можно считать, что в списках ключи — это индексы (0, 1, 2...), а в словарях — строки.
Функцию replace(
удобно использовать, чтобы начисто удалить какие‑то слова из строки. Для этого будем заменять их пустой строкой (если открыть и закрыть кавычку, то получится пустая строка):
s = '''Я не люблю пить пиво.Оно невкусное и неполезное!'''s = s.replace('не','')print(s)
info
Чтобы записать в переменную несколько строк, можно обернуть их в три одинарные кавычки и делать переносы прямо в коде.
Чтобы получить количество символов в строке, используется функция len().
s = 'Если очень вам неймется, код пишите как придется!'n = len(s)print(n)
И, как я уже рассказывал в прошлой статье, от строк можно брать срезы как от массивов, если указать начало и конец подстроки в квадратных скобках после переменной. Позиция начинается с нуля.
s = 'Меня зовут Бонд, Джеймс Бонд'a = s[11:15]print('Фамилия: ' + a)
Если нужно сделать срез с начала строки, первую цифру можно не писать.
Предположим, тебе нужно найти в списке строки, которые начинаются на https. Перебираем их с помощью for
, для каждой проверяем, совпадают ли первые пять знаков со строкой https
, и если да, то выводим строку:
mas = [ 'Это просто строка', 'https://xakep.ru', 'Еще одна строка', 'https://habr.ru' ]for x in mas: if x[:5] == 'https': print(x)
Чтобы посчитать количество вхождений подстроки в строку, можно использовать метод .
:
s = 'Прикинь, короче, я такой, короче, ему бах эксплоитом по порту, а он, короче, упал сразу!'n = s.count('короче')print(n)
Иногда в начале или в конце строки могут быть лишние пробелы или переносы строк. Давай удалим их специальной командой .
:
s = ' Пива много не бывает! \n's = s.strip()print(s)
info
Переносы строк можно добавить с помощью символов \
(используется во всех ОС) либо \
(в Windows). Есть и другие спецсимволы. Например, \
— знак табуляции.
Чтобы определить наличие подстроки в строке s, можно использовать метод .
:
n = s.find('строка, которую ищем')
Если искомая подстрока найдена, то в переменную n
попадет ее позиция в строке, а если не найдена, n
станет равной -1
.
Давай попробуем определить, есть ли в строке адрес электронной почты с Xakep.ru, то есть будем искать подстроку @xakep.
.
Но сначала нам понадобится еще один строковый метод — .
. Он позволяет разделить строку на части, указав в качестве аргумента строку‑разделитель. Например, s.
разделит текст на абзацы по символу переноса строки. Если же оставить скобки пустыми, то будет использован разделитель по умолчанию — пробел.
s = 'Это обычная строка, а в ней адрес почты vasya@xakep.ru'words = s.split()for w in words: n = w.find('@xakep.ru') if n != -1: print('Найден e-mail: ' + str(w) + ' в позиции ' + str(n))
Метод .
позволяет, наоборот, склеивать строки. Он принимает список и возвращает строку, где каждый элемент списка соединен с другим через строку, у которой ты вызвал этот метод.
s = 'вирус внедряется 'list1 = ['раз, ', 'два, ', 'три...']print(s + s.join(list1))
Форматируем строки
Мы не раз печатали разные вещи, соединяя строки простым сложением. Это не всегда удобно, особенно учитывая, что если попадутся числа, то их придется переводить в строки функцией str(
. Есть более красивый и удобный способ подставлять значения переменных внутрь строк. Точнее, два немного разных способа.
Способ 1 — c помощью метода .format()
Мы можем вставить в строку парные фигурные скобки, а затем вызвать строковый метод .
и передать ему нужные значения в порядке их подстановки в строку.
name = 'Вася Пупкин'age = 20address = 'улица Пушкина, дом Колотушкина'info = 'Имя: {}. Возраст: {}. Адрес: {}'.format(name, age, address)print(info)
Можно передать информацию списком через звездочку:
data = ['Вася Пупкин', 20, 'улица Пушкина, дом Колотушкина']info = 'Имя: {}. Возраст: {}. Адрес: {}'.format(*data)print(info)
Способ 2 — через f-строки
Другой вариант — написать букву f
перед строкой и затем в фигурных скобках указывать непосредственно переменные.
name = 'Вася Пупкин'age = 20address = 'улица Пушкина, дом Колотушкина'info = f'Имя: {name.upper()}. Возраст: {age}. Адрес: {address}'print(info)
Главное преимущество этого способа в том, что ты можешь вставить значение в строку несколько раз. К тому же можно менять значения прямо в фигурных скобках: сперва Python выполнит все действия в них, а затем подставит полученный результат в строку. Так, метод .
в примере выше делает все буквы заглавными.
Файлы
Перечисленных методов достаточно, чтобы ты мог делать со строками что угодно. Но откуда эти строки возьмутся? Чаще всего они записаны в файлах, поэтому сейчас я расскажу, как в Python с ними управляться.
Чтобы работать с файлом, его нужно открыть. Для этого служит функция open(
, а работает она вот так:
f = open('имя файла с путем и расширением', 'режим работы с файлом', encoding='Кодировка текста')
Режимов работы с файлами несколько, но тебя интересует в основном:
-
r
— открыть файл для чтения из него информации; -
w
— открыть файл для записи в него информации (создает новый файл); -
a
— открыть файл для дозаписи информации в конец файла (дописывает информацию в конец существующего файла); -
a+
— дозапись и чтение.
Чтобы избежать проблем с путями в Windows, используй в них двойной слеш \
, а также перед открывающей кавычкой пути файла ставь букву u, указывающую на то, что строка в кодировке Unicode:
f = open(u'D:\\test.txt', 'r', encoding='UTF-8')
Читать строки из файла можно методом .
:
f = open('test.txt', 'r', encoding='UTF-8')s = f.read()print(s)
Как вариант — можно последовательно читать из файла отдельные строки с помощью цикла for
:
f = open('test.txt', 'r', encoding='UTF-8')for x in f: print(x)
После того как работа с файлом закончена, нужно закрыть его.
f.close()
info
Для работы с бинарными файлами при открытии файла добавь к режиму букву b
:
f = open('myfile.bin', 'rb')d = f.read()print("d = ", d)
Подробнее о бинарных данных мы поговорим в одной из следующих статей.
Давай теперь попробуем создать новый текстовый файл в одном каталоге с нашим скриптом и записать в него значения каких‑то переменных.
s1 = 'Раз, два, три, четыре, пять\n's2 = 'Я иду сервак ломать...\n'f = open('poems.txt', 'w', encoding='UTF-8')f.write(s1)f.write(s2)f.close()
Обрати внимание, что в конце каждой строки стоит символ \
— переход на новую строку.
Допустим, ты хочешь дописать третью строчку в конец этого файла. Тут‑то и пригодится режим дозаписи!
s3 = 'Ох, устанут поднимать!\n'f = open('poems.txt', 'a', encoding='UTF-8')f.write(s3)f.close()
Для открытия файлов также очень удобно использовать конструкцию with
, потому что благодаря слову with файл закроется автоматически и тебе не придется думать об этом.
s = 'Если вы закроете этот файл, ваш диск будет отформатирован!\nШутка\n'with open('test.txt', 'w', encoding='UTF-8') as f: f.write(s)
Работа с вебом
Давай научимся получать информацию с веб‑страниц. Для начала нужно установить несколько модулей. Пишем в командной строке:
pip install requestspip install html2text
Модуль requests позволяет делать GET- и POST-запросы к веб‑страницам. Модуль html2text служит для преобразования HTML-кода веб‑страниц в обычный текст, то есть чистит его от тегов HTML.
Импортируем наши новые модули в начале программы и попробуем получить какую‑нибудь страницу из интернета.
import requests# Делаем GET-запросs = requests.get('http://xakep.ru')# Печатаем код ответа сервераprint(s.status_code)# Печатаем HTML-кодprint(s.text)
Программа напечатает много HTML-кода, из которого состоит главная страница журнала. Но что, если тебе нужен только текст сайта, а не мешанина из тегов? Здесь поможет html2text. Он выделит из кода текст, заголовки и картинки и отдаст их уже без HTML-тегов.
import requestsimport html2text# Делаем GET-запросs = requests.get('http://xakep.ru')# Код ответа сервераprint(s.status_code)# Создается экземпляр парсераd = html2text.HTML2Text()# Параметр, влияющий на то, как парсятся ссылкиd.ignore_links = True# Текст без HTML-теговc=d.handle(s.text)print(c)
Кроме GET-запросов, существуют так называемые POST-запросы, которые применяются для отсылки на сервер больших текстов или каких‑то файлов. Если видишь на сайте форму, особенно с загрузкой файла, значит, скорее всего, при нажатии на кнопку «Отправить» будет сделан POST-запрос.
Библиотека requests тоже позволяет делать POST-запросы. Тебе это может пригодиться для имитации действий пользователя — например, если нужно автоматизировать работу с сайтом. Можешь даже использовать это в качестве самописного аналога Burp!
Давай посмотрим, как послать обычный POST-запрос. Предположим, на сайте site.
существует скрипт guest.
, который POST-запросом принимает от формы имя пользователя name
и сообщение message
, а затем постит их в гостевую книгу.
import requests# Переменные, которые нужно отправить POST-запросомuser = 'coolhacker'message = 'You have beeh pwned!!!'# Делаем POST-запрос и передаем словарь из полейr = requests.post("http://site.ru/guest.php", data={'user': user, 'message': message})print(r.status_code)
Теперь давай отправим запрос с файлом payload.
во вложении и теми же двумя полями формы, что и в предыдущем запросе. Файл придет на сервер под именем misc.
.
import requestsuser = 'kitty2007'message = '(* ^ ω ^)'# Открываем файл в бинарном режимеwith open('payload.php', 'rb') as f: # POST-запрос с отправкой файла r = requests.post('http://site.ru/upload.php', files={'misc.php': f}, data={'user': user, 'message': message})
Осталось научиться скачивать файлы. Это во многом похоже на запрос страниц, но делать это лучше в потоковом режиме (stream=True
). Также нам понадобится модуль shutil, в котором есть удобная функция copyfileobj
. Она позволяет копировать содержимое двоичных файлов — в нашем случае из интернета к нам на диск.
import requestsimport shutilimport os# Файл, который надо скачатьs = 'https://xakep.ru/robots.txt'# С помощью функции os.path.split(s) вытаскиваем из строки путь к файлу и его имяdirname, filename = os.path.split(s)# GET-запрос в режиме stream=True для скачивания файлаr = requests.get(s, stream=True)# Если ответ сервера удачен (200)if r.status_code == 200: # Создаем файл и открываем его в бинарном режиме для записи with open(filename, 'wb') as f: # Декодируем поток данных на основе заголовка content-encoding r.raw.decode_content = True # Копируем поток данных из интернета в файл с помощью модуля shutil shutil.copyfileobj(r.raw, f)
info
Коды ответа сервера помогают понять, как прошел запрос. Код 200 означает, что сервер успешно обработал запрос и отдал нам ответ, код 404 — страница не была найдена, 500 — внутренняя ошибка сервера, 503 — сервер недоступен и так далее. Полный список кодов ты найдешь в Википедии.
Обработка ошибок
Прежде чем разбирать более реальный пример, я должен показать тебе еще одну языковую конструкцию, которая незаменима при работе с файлами и сетью. Это обработка исключительных ситуаций, то есть ошибок.
Часто при работе программы компьютер сталкивается с разными проблемами. Например, файл не найден, сеть недоступна, кончилось место на диске. Если программист об этом не позаботился, то интерпретатор Python просто завершит работу с ошибкой. Но есть способ предусмотреть неурядицы прямо в коде и продолжать работу — конструкция try...
.
Выглядит она вот так:
try: # Тут какие-то команды, # которые могут привести к ошибкеexcept: # Наши действия, если ошибка произошла
Можно ловить конкретные типы ошибок, если после слова except
указать название типа. К примеру, KeyboardInterrupt
срабатывает, если пользователь пытается завершить программу, нажав Ctrl-C. В нашей власти запретить это делать!
Да что там, мы можем даже разрешить делить на ноль, если отловим ошибку ZeroDivisionError
. Вот как это будет выглядеть:
try: k = 1 / 0except ZeroDivisionError: k = 'over 9000'print(k)
Пишем сканер портов
А теперь мы напишем собственный сканер портов! Он будет простеньким, но вполне рабочим. Поможет нам в этом модуль socket, где реализована работа с сокетами.
info
Cокет — это интерфейс обмена данными между процессами. Существуют клиентские и серверные сокеты. Серверный сокет слушает определенный порт в ожидании подключения клиентов, а клиентский подключается к серверу. После того как было установлено соединение, начинается обмен данными.
Вот как будет выглядеть код.
import socket# Список портов для сканированияports = [20, 21, 22, 23, 25, 42, 43, 53, 67, 69, 80, 110, 115, 123, 137, 138, 139, 143, 161, 179, 443, 445, 514, 515, 993, 995, 1080, 1194, 1433, 1702, 1723, 3128, 3268, 3306, 3389, 5432, 5060, 5900, 5938, 8080, 10000, 20000]host = input('Введи имя сайта без http/https или IP-адрес: ')print ("Ожидай, идет сканирование портов!")# В цикле перебираем порты из спискаfor port in ports: # Создаем сокет s = socket.socket() # Ставим тайм-аут в одну cекунду s.settimeout(1) # Ловим ошибки try: # Пробуем соединиться, хост и порт передаем как список s.connect((host, port)) # Если соединение вызвало ошибку except socket.error: # тогда ничего не делаем pass else: print(f"{host}: {port} порт активен") # Закрываем соединение s.closeprint ("Сканирование завершено!")
Как видишь, ничего сложного!
Домашнее задание
Сделай, чтобы сканер портов получал список IP из одного файла, а результаты сканирования записывал в другой.
В прошлой статье ты научился работать с буфером обмена. Напиши программу, которая постоянно запущена и периодически получает содержимое буфера обмена. Если оно изменилось, то дописывает его в конец файла
monitoring.
Попробуй записывать в лог только те перехваченные строки, в которых есть латинские буквы и цифры, так более вероятно поймать пароли.txt. -
Напиши программу, которая читает файл такого вида:
Иван Иванов|ivanov@mail.ru|Password123Дима Лапушок|superman1993@xakep.ru|1993supermanВася Пупкин|pupok@yandex.ru|qwerty12345Фродо Бэггинс|Frodo@mail.ru|MoRdOr100500Кевин Митник|kevin@xakep.ru|dontcrackitpleaseЮзер Юзерович|uswer@yandex.ru|aaaa321Программа должна сортировать строки по доменам из email, для каждого домена создавать файл и в каждый файл помещать список почтовых адресов.
Напиши программу, которая проходит сайты по списку, скачивает файлы robots.txt и sitemap.xml и сохраняет на диск. В случае если файл не найден, выводится сообщение об этом.
На сегодня всё. Из следующей статьи ты узнаешь, как работать с файловой системой ОС, разберешься с функциями, познаешь силу регулярных выражений и напишешь простой сканер SQL-уязвимостей. Не пропусти!