От редакции
Эта статья — часть цикла «Python с абсолютного нуля», где мы рассказываем об азах Python в нашем фирменном нескучном стиле. Ты можешь читать их по порядку или выбирать какие‑то области, которые хотел бы подтянуть.
- Урок 1: Переменные, типы данных, условия и циклы
- Урок 2: Строки, файлы, исключения и работа с интернетом
Первые два урока доступны целиком без платной подписки. Этот — почти целиком: за исключением последнего примера и домашнего задания.
Работаем с файлами
Начнем, как всегда, с несложных вещей. В Python есть модуль с лаконичным названием os
, который (ты не поверишь!) предназначен для взаимодействия программы с операционной системой, в том числе для управления файлами.
Первым делом, конечно, нужно импортировать его в начале нашего скрипта:
import os
И теперь нам открываются разные интересные возможности. К примеру, мы можем получить путь к текущей папке. Сначала она совпадает с той, в которой ты был при запуске скрипта (даже если сам скрипт находится где‑то в другом месте), но по ходу исполнения программы мы можем менять это значение при помощи функции os.
.
# Возвращает путь к текущей рабочей папкеpth=os.getcwd()print(pth)# Устанавливает путь к текущей рабочей папке, в данном случае это диск D:/os.chdir(r'D:/')
info
Если ты работаешь в Windows, то в пути к файлу или папке перед открывающей кавычкой указывай букву r
(что означает raw) или вместо одной косой черты в пути ставь две.
Попробуем получить список файлов с расширением .py, находящихся в текущей директории. Для этого используем модули os и fnmatch.
import osimport fnmatch# В цикле, с помощью os.listdir('.') получим список файлов# в текущей директории (точка в скобках как раз ее и обозначает)for fname in os.listdir('.'): # Если у текущего имени файла расширение .py, то печатаем его if fnmatch.fnmatch(fname, '*.py'): print(fname)
Модуль fnmatch позволяет искать в строках определенный текст, подходящий по маске к заданному шаблону:
-
*
заменяет любое количество любых символов; -
?
заменяет один любой символ; -
[
заменяет любые символы из последовательности в квадратных скобках;seq] -
[!
заменяет любые символы, кроме тех, что присутствуют в квадратных скобках.seq]
Давай безжалостно удалим какой‑нибудь файл:
import os(os.remove(r'D:\allmypasswords.txt'))
Переименуем файл:
import osos.rename('lamer.txt','xakep.txt')
А теперь создадим папку по указанному пути и сразу же удалим ее. Для этого пригодится модуль shutil, где есть функция rmtree(
, которая удаляет папку вместе с содержимым.
import osimport shutilos.makedirs(r'D:\secret\beer\photo') # Создает все папки по указанному путиshutil.rmtree(r'D:\secret\beer\photo') # Удаляет папку вместе с ее содержимым
Допустим, ты хочешь получить список всех файлов, содержащихся в папках по указанному пути (учитывая вложенные папки тоже), чтобы найти что‑то интересное. Скрипт будет выглядеть следующим образом:
warning
Будь осторожен — скрипт в таком виде обшарит весь диск D. Если он у тебя есть и там много хлама, то процесс может затянуться.
import osfor root, dirs, files in os.walk(r'D:'): for name in files: fullname = os.path.join(root, name) print(fullname) if('pass' in fullname): print('Бинго!!!')
Функция walk() модуля os принимает один обязательный аргумент — имя каталога. Она последовательно проходит все вложенные каталоги и возвращает объект‑генератор, из которого получают:
- адрес очередного каталога в виде строки;
- список имен подкаталогов первого уровня вложенности для данного каталога;
- список имен файлов данного каталога.
info
Генератор — это объект, который сразу при создании не вычисляет значения всех своих элементов. Этим генераторы отличаются от списков — те хранят в памяти все свои элементы, и удалить их можно только программно. Вычисления с помощью генераторов называются ленивыми, они экономят память. Подробнее мы рассмотрим генераторы в следующих уроках.
Сейчас покажу, как узнать размер любого файла, а также дату его модификации.
import os.path# Модуль для преобразования даты в приемлемый форматfrom datetime import datetimepath = r'C:\Windows\notepad.exe'# Получим размер файла в байтахsize = os.path.getsize(path)# А теперь в килобайтах# Две косые черты — это целочисленное делениеksize = size // 1024atime = os.path.getatime(path)# Дата последнего доступа в секундах с начала эпохиmtime = os.path.getmtime(path)# Дата последней модификации в секундах с начала эпохиprint ('Размер: ', ksize, ' KB')print ('Дата последнего использования: ', datetime.fromtimestamp(atime))print ('Дата последнего редактирования: ', datetime.fromtimestamp(mtime))
info
Для операционных систем Unix 1 января 1970, 00:00:00 (UTC) — точка отсчета времени, или «начало эпохи». Чаще всего время в компьютере вычисляется в виде прошедших с этого момента секунд и лишь затем переводится в удобный для человека вид.
Давай пошутим над юзером: создадим какой‑нибудь файл и будем постоянно его открывать с помощью той программы, которой этот файл обычно открывается в системе:
import os# Модуль time понадобится для паузы, чтобы не слишком часто открывалосьimport time# Создаем текстовый файлf=open('beer.txt','w',encoding='UTF-8')f.write('СРОЧНО НАЛЕЙТЕ ХАКЕРУ ПИВА, ИНАЧЕ ЭТО НЕ ЗАКОНЧИТСЯ!!')f.close()while True: # Открываем файл программой по умолчанию os.startfile('beer.txt') # Делаем паузу в одну секунду time.sleep(1)
Ниже приведен список еще некоторых полезных команд:
-
os.
— возвращает название файла или папки в конце пути;path. basename( 'путь') -
os.
— возвращает родительский путь к объекту пути;path. dirname( 'путь') -
os.
— разделяет путь на путь и расширение файла;path. splitext( 'путь') -
os.
— существует ли путь до файла или папки;path. exists( 'путь') -
os.
— является ли объект пути файлом (существующим);path. isfile( 'путь') -
os.
— является ли объект пути папкой (существующей).path. isdir( 'путь')
Регулярные выражения
Регулярные выражения — это специальные шаблоны для поиска и замены строк в тексте. Вообще говоря, их вполне можно считать самостоятельным языком, и его изучение выходит за рамки этого цикла. Мы пройдемся по самым основам и по использованию регулярок в Python.
www
Подробнее о регэкспах ты можешь почитать в документации Python, в Википедии или в книге Джеффри Фридла, которая так и называется — «Регулярные выражения».
Мы не раз писали о полезных сайтах, которые помогают работать с регулярными выражениями: CyberChef, RegExr, txt2re. Помимо этого, можешь обратить внимание на сервис regex101.com и сайт RegexOne с интерактивным тренажером.
За работу с регулярными выражениями в Python отвечает модуль re. Первым делом импортируем его.
import re
В качестве простейшего паттерна мы можем использовать какое‑нибудь слово. Пусть по традиции это будет «пиво»:
import repattern = r"пиво"string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян."result = re.search(pattern, string)print(result.group(0))
Команда re.
ищет в тексте string
первое вхождение шаблона pattern
и возвращает группу строк, доступ к которым можно получить через метод .
. Но команда search
ищет только первое вхождение шаблона. Поэтому в нашем случае вернется всего один результат — слово «пиво», несмотря на то что в нашем тексте оно присутствует дважды.
Чтобы вернуть все вхождения шаблона в текст, используется команда re.
. Эта команда вернет список строк, которые присутствуют в тексте и совпадают с шаблоном.
import repattern = r"пиво"string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян."result = re.findall(pattern, string)print(result)
info
Обрати внимание, что шаблоны в регулярных выражениях имеют буковку r перед началом строки. Это так называемые сырые строки, в которых не работает символ экранирования с помощью обратного слеша \
. При этом «сырая» строка не может заканчиваться этим символом.
В предыдущих двух примерах программ в качестве шаблона pattern
для поиска строк ты использовал просто какое‑то слово. Но мощь регулярных выражений не в этом. Ты можешь заменять части шаблона специальными символами, чтобы под шаблон подходили не только конкретные слова, но и самые разные строки.
Давай, например, попробуем найти в тексте все слова, которые начинаются с «пи». Для этого используем специальный символ \
— он означает «начало слова». Сразу после него указываем, с чего должно начинаться слово, и напишем специальный символ w
, который означает, что дальше в шаблоне должны идти какие‑то буквы (плюс означает, что их может быть одна или больше) до тех пор, пока не встретится небуквенный символ (например, пробел или знак препинания). Шаблон будет выглядеть так: r"\
.
import repattern = r"\bпи\w+"string = "Хакер знает, что пиво играет во взломе решающую роль. Свежее пиво — ключ к сисадмину. Пока сисадмин ходит писать, можно сесть за его комп и внедрить троян."result = re.findall(pattern, string)print(result)
Давай попробуем выполнить чуть более сложную задачу. Найдем в тексте все email с доменом mail.
, если они там есть.
import repattern = r"\b\w+@mail\.ru"string = "Если вы хотите связаться с админом, пишите на почту admin@mail.ru. По другим вопросам обращайтесь на support@mail.ru."result = re.findall(pattern, string)print(result)
Как видишь, мы использовали тот же трюк, что и в прошлый раз, — написали специальный символ \
, чтобы обозначить начало слова, потом \
, что значит «одна или больше букв», а затем @mail.
, заэкранировав точку, поскольку иначе она будет означать «любой символ».
Часто бывает нужно найти какой‑то элемент строки, окруженный двумя другими элементами. Например, это может быть URL. Чтобы выделить ту часть шаблона, которую нужно вернуть, используются скобки. Приведу пример, в котором ты получишь все адреса ссылок из какого‑то кусочка кода на HTML.
import restring = 'Вы можете посмотреть карту сайта <a href="map.php">тут</a>. Посетите также <a href="best.php"раздел</a>'pattern = r'href="(.+?)"'result = re.findall(pattern,string)print(result)
В коде выше использовался паттерн r'href="(.
— в этом шаблоне искомая строка начинается с href="
и заканчивается еще одной двойной кавычкой. Скобки нужны для того, чтобы указать, какую часть подходящей под шаблон строки ты хочешь получить в переменную result
. Точка и плюс внутри скобок указывают, что внутри кавычек могут быть любые символы (кроме символа новой строки). Знак вопроса означает, что нужно остановиться перед первой же встреченной кавычкой.
info
Знак вопроса в регулярных выражениях используется в двух немного разных смыслах. Если он идет после одного символа, это значит, что символ может присутствовать или не присутствовать в строке. Если же вопросительный знак идет после группы символов, это означает «нежадный» (non-greedy) режим: такая регулярка будет стараться захватить как можно меньше символов.
Мы можем не только искать строки, но и заменять их чем‑то другим. Например, давай попробуем удалить из HTML-кода все теги. Для этого используется команда re.
.
import restring = 'Вы можете посмотреть карту сайта <a href="map.php">тут</a>. Посетите также <a href="best.php"раздел</a>'pattern = r'<(.+?)>'result = re.sub(pattern,'',string)print(result)
Программа напечатает строку уже без тегов, так как мы заменили их пустой строкой.
Регулярные выражения — очень мощная штука. Освоив их, ты сможешь делать со строками почти все, что угодно, а в сочетании с кодом на Python — буквально что угодно. Для начала же можешь поэкспериментировать и изменить какие‑то из приведенных рецептов.
Функции
Пришла пора подробнее поговорить о функциях. Мы уже неоднократно вызывали разные функции — как встроенные в Python (например, print(
), так и из подключаемых модулей (например, urllib.
). Но что такое функция изнутри и как их делать самостоятельно?
Представь, что у тебя есть какой‑то набор команд, которые нужно выполнять несколько раз, изменяя лишь входные данные. Такие блоки команд обычно выносят в отдельные кусочки программы.
info
В объектно ориентированном программировании функции являются методами какого‑либо класса и пишутся через точку от его названия.
s='Hello, xakep!'print(s) # Функцияs.lower() # Метод
У функции могут быть входные параметры — это одна или несколько переменных, которые пишутся в скобках после имени функции. При вызове функции ты можешь передать ей аргументы для этих параметров. Некоторые из параметров могут быть необязательными или иметь значение по умолчанию — на случай, если его не передадут.
Объявление функции начинается с ключевого слова def
, далее следует имя функции, параметры в скобках и программный код, отделенный четырьмя пробелами. Функция может возвращать одно или несколько значений с помощью ключевого слова return
. Оно, кстати, прекращает работу функции, и, если за ним идут какие‑то команды, они будут пропущены.
Для примера разберем простейшую функцию, которая будет принимать в качестве аргументов два любых числа и перемножать их, возвращая результат умножения. Назовем ее umn.
def umn(a, b): c = a * b return c
Теперь, когда ты описал функцию, далее в этой же программе можно ее вызывать.
a = int(input('Введите первое число: '))b = int(input('Введите второе число: '))с = umn(a, b)print(c)
Иногда надо задать один из параметров как необязательный, установив для него значение по умолчанию.
def umn(a, b=10): c = a * b return c
Теперь если ты вызовешь функцию и не передашь ей второй аргумент, то она просто будет считать его равным десяти, то есть будет умножать любое переданное число на десять.
с=umn(5)print(c)
Несмотря на то что параметр b
в данном случае равен по умолчанию 10 и необязателен для передачи в качестве второго аргумента, ты по‑прежнему можешь передавать второй аргумент, если это будет нужно, и тогда в качестве b
будет использовано не 10
, а переданное значение.
с=umn(5, b=20)print(c)
Внутри программы мы можем вызывать созданную нами функцию сколько угодно раз.
Давай создадим программу, которая будет считать прибавку к зарплате за каждую уязвимость, которую хакер нашел на работе. У каждого хакера будет своя зарплата, в зависимости от его ранга, но начисление прибавки для всех работает по принципу «+2% к базовой зарплате за уязвимость, если таких уязвимостей найдено больше чем три».
Сделаем функцию, которая принимает в качестве аргументов размер зарплаты сотрудника и количество найденных уязвимостей. Для округления результата используем функцию round(
, которая округлит прибавку до целого числа.
def pribavka(zarplata, bugs): k = 0 if bugs > 3: k = round((bugs - 3) * 0.02 * zarplata) return ka = int(input('Введите зарплату сотрудника: '))b = int(input('Введите количество найденных им уязвимостей за месяц: '))c = pribavka(a, b)print('В этом месяце прибавка к зарплате составит: ' + str(c))
Если функция должна возвращать больше одного значения, то можно перечислить их через запятую.
def myfunc(x): a = x + 1 b = x * 2 return a, b
Функция будет возвращать список, но мы можем сразу присвоить возвращаемые значения каким‑нибудь переменным:
plusone, sum = myfunc(5)
Внутри функций вполне можно использовать переменные, которые встречались в коде программы до вызова функции. Но если внутри кода функции задать переменную с таким же именем, то эта переменная автоматически станет локальной и все дальнейшие изменения будут происходить с ней только в пределах функции.
Поясню на примере:
def boom(a, b): z = 15 c = a * b * z return cz = 1c = boom(15, 20)print(z)
В результате выполнения программы ты увидишь единицу. Почему? Внутри кода функции мы присвоили переменной z
значение 15, и она стала локальной, и все изменения с ней будут происходить внутри функции, тогда как в основной программе ее значение будет по‑прежнему равно единице.
Это немного трудно понять, но на самом деле удобно. Если ты пишешь несколько похожих функций, то вполне можешь использовать внутри них одинаковые названия локальных переменных, не опасаясь, что они будут влиять как‑то друг на друга.
Переменные, объявленные вне функций, называются глобальными. Если ты хочешь изнутри функции изменить одну из них, то объяви ее внутри функции, использовав ключевое слово global
.
def addfive(num): global a a += numa = 5addfive(3)print(a)
Эта программа напечатает 8. Обрати внимание, что мы ничего не возвращали через return
, а просто изменили глобальную переменную. Кстати, функция, в которой ничего не возвращается через return
, будет возвращать значение None
.
a = 5print(addfive(3))
На экране выведется слово None
. Это бывает полезно, если функция возвращает что‑то только при выполнении каких‑то условий, а если они не выполнены, то выполнение не доходит до return
. Тогда можно проверить, не вернула ли она None
.
def isoneortwo(num): if(num==1): return 'Один' if(num==2): return 'Два'print(isoneortwo(1))print(isoneortwo(2))print(isoneortwo(3))
Эта функция проверяет, равно ли значение единице или двойке, и если не равно, то вернет None
. Это можно дальше проверить при помощи if
:
if isoneortwo(3) is None: print("Не 1 и не 2!")
Итак, мы научились создавать функции, вызывать их и возвращать из них параметры, а также использовать внутри функций глобальные переменные. С этого момента мы уже можем браться за относительно сложные примеры!
Практика: проверка SQL-уязвимостей
На этот раз мы создадим скрипт, который будет искать SQL-уязвимости по разным URL. Заранее создадим файл urls.
, в каждой строчке которого будут адреса сайтов, содержащие GET-параметры. Например:
http://www.taanilinna.com/index.php?id=325https://www.925jewellery.co.uk/productlist.php?Group=3&pr=0http://www.isbtweb.org/index.php?id=1493
Напишем скрипт, который получает список подобных URL из нашего файла и добавляет в каждый из GET-параметров знак кавычки, пытаясь вызвать ошибки SQL баз данных.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»