Глава 6. Запуск Python из PowerShell

До сих пор наш подход к интеграции Python с PowerShell состоял в запуске сценариев PowerShell из Python в качестве некого подпроцесса {Прим. пер.: синхронного или асинхронного}. В этой главе их роли поменяются и PowerShell будет питать данными сценарии Python. Одним из ключевых элементов PowerShell выступает организация конвейера самого процесса передачи полученных результатов от одного CmdLet другому. Имея это в виду, почему бы не воспринимать Python протсо как другой элемент конвейера и исполнять сценарии Python, управляемые получаемыми от PowerShell данными?

Реверс ролей из PowerShell в Python

Для иллюстрации данного метода потребуются и сценарий PowerShell, и сценарий Python. Мы начнём с некого простого сценария PowerShell для передачикакой- то строки даных в конвейере и отображении этих данных из своего сценария Python.

Изучение сценария PoerShell

Давайте изучим подробности своего показанного на Рисунке 6-1 сценария PowerShell. Этот сценарий разбивается на четыре простые этапа:

  1. Определение локальной переменной $Python с установленным полным путём к самому выбранному вами исполняемому файлу интерпретатора Python. Для днного примера мы снова будем применять Python 3.x.

  2. Задание некой локальной переменной $Script, которая определяет значение полного пути к тому сценарию Python, который будет исполняться.

  3. Установление локальной переменной $Message, которая будет передана через устанавливаемый конвейер в наш сценарий Python.

  4. Эта строка передаёт само содержимое нашей переменной сообщения в применяемый сценарий Python. Основным ключевым элементом здесь выступает оператор амперсанда (&), который указывает PowerShell нзагрузить необходимую внешнюю программу.

 

Рисунок 6-1


Сценарий PowerShell BasicOne.ps1

Изучение соответствующего сценария Python

Рассматривая свой соответствующий сценарий Python, показанный на Рисунке 6-2, мы видим, что он также разбивается на четыре раздела:

  1. Некий блок комментария, который определяет то что выполнит данный сценарий.

  2. Импорт стандартной библиотеки Python sys. Она требуется для обработки тех данных, которые передаются через конвейер.

  3. Вывод на печать сообщений, доставляемых в Python для демонстрации исполнения данного сценария Python.

  4. Обработку каждой из доставляемых в этот сценарий через утановленный конвейер строки и вывод поллученного содержимого всех строк. Отметим, что данный сценарий передаёт лишь одну строку.

 

Рисунок 6-2


Сценарий Python BasicOne.py

Исполнение совмещённых PowerShell для сценариев Python

Рисунок 6-3 отображает получаемый в результате вывод, вырабатываемый нашим сценарием PowerShell, управляющим соответсвующим сценарием Python. Вы заметите, что в нашем выводе PowerShell появляется вывод как из сценария PoweShell (CmdLet write-host), так и из операторов Python (print).

 

Рисунок 6-3


Исполнение BasicOne.ps1 управляющего BasicOne.py

Применяя данный метод, давайте теперь изучим более интересное применение своего показаного здесь метода BasicOne.

Извлечение возможных собственных имён из текстовых документов

В этом примере наш сценарий PowerShell будет использовать CmdLet Get-ChildItem и CmdLet Get-Content для получения содержимого текстового файла и передачи всего содержимого в некий сценарий Python. Данный сценарий Python обработает переданное содержимое и снова применит метод BaseOne и попытается выделить возможные подходящие названия.

При изучении простых текстовых данных в процессе криминального расследования, часто бывает полезным выделять и ранжировать собственные имена по частоте их появлений. Язык программирования Python имеет встроенные возможности, которые превращают такое выделение в быстрое и простое.

[Замечание]Замечание

Но для начала, что такое собственные имена?

Языкознание определяет собственные имена как такие слова, которые представляют человека, место, группу, организацию или вещь, которая обычно начинается с заглавной буквы. К примеру, собственные имена в отдельном слове (такое как Дэвид, Смит, Кэрол, Вашингтон, Канада, Пентагон, Конгресс или Apple) могут предоставлять контекст и значение для расследования. В обычных текстах такие собственные имена скорее всего пишутся с заглавной буквы и их легко разобрать, выявить, пересчитать и рассортировать. Следует отметить, что не все будут следовать выделению заглавной буквой имён собственных; однако, смартфоны, прикладные программы текстовых сообщений, программы электронной почты, процессоры обработки слов и даже окно обмена сообщениями Skype автоматически выделяют их заглавными для нас. Таким образом, их выделение и ранжирование может предоставлять быстрый поиск и обеспечивать перспективу для расследования.

Изучение сценария PoerShell

Рисунок 6-4 показывает соответствующий сценарий PowerShell, который доставит всё содержимое этих файлов в более сложный сценарий Python, который и выполнит необходимые извлечение и ранжирование всех возможных собственных имён. Отметим, что для этого примера был добавлен новый элемент для возможности обработки множества файлов.

 

Рисунок 6-4


Сценарий ProperNames PowerShell

Данный сценарий был разбит на шесть этапов. Определим здесь каждый из них:

  1. Определение локальной переменной $Python с установленным полным путём к самому выбранному вами исполняемому файлу интерпретатора Python. Для днного примера мы снова будем применять Python 3.x.

  2. Задание некой локальной переменной $Script, которая определяет значение полного пути к тому сценарию Python, который будет исполняться.

  3. Установление локальной переменной $targetPath, которая указывает имя пути назначения и типов обрабатываеемых файлов.

  4. Применяем CmdLet Get-ChildItem для получения всех имён из тех файлов, которые соответствуют предоставленному значению расширения.

  5. Записываем информацию в тот хост, который содержит перечень файлов, обследованных CmdLet Get-ChildItem.

  6. Применяя цикл ForEach, обрабатываем все перечисленные в своей локальной переменной $files файлы. Внутри этого цикла наш сценарий выводит на печать все имена из каждого файла, затем выделяет полученное сырое содержимое данного файла и отправляет конвейером полученное в результате содержимое в наш сценарий Python.

Изучение соответствующего сценария ProperNames Python

Наш сценарий Python приводится в Листинге 6-1, разбитом на шесть основных разделов озаглавленных так:

  1. Импорт библиотек

  2. Определение списка стоп- слов

  3. Определение псевдоконстант

  4. Выделение собственных имён

  5. Основной элемент программы

  6. Вывод на печать получаемых в результате возможных имён собственных

Импорт библиотек: Как и подразумевает его название, это именно то место, в котором загружаются необходимые библиотеки Python. Они включают:

  • sys: Как было показано в BasicOne, эта библиотека позволяет нам орбрабатывать ввод командной строки, доставляемый со стороны PowerShell.

  • re: Это библиотека регулярных выражений Python, которая применяется в данном сценарии для снятия внешних символов из получаемого текста для упрощения поиска собственных имён.

  • datetime: Как и подразумевает её название, данная библиотека предоставляет методы для отображения и вычисления деталей времени и даты.

Определение списка стоп- слов: Данный раздел создаёт некий список стоп- слов, которые применяются внутри нашего сценария для устранения слов, которые не производят возможных значений при получении собственных имён. Они представляют собой на самом деле те слова, которые обычно начинают последовательности, которые могут выделяться заглавными. Тем самым, исключение данных слов из наших результатов произведёт улучшение получаемого результата.

Определение псевдоконстант: В языке программирования нет констант в обычном понимании, тем не менее, выделение этих переменных заглавными буквами предупреждает читающего программу о том, что данные переменные не следует изменять. В данном случае значения переменных MIN_SIZE и MAX_SIZE определяют велечины пределов возможных собственных имён. Изменяя эти величины вы можете расширять или сужать значение диапазона возможных собственных имён.

Выделение собственных имён: Именно это составляет функцию ядра нашего сценария, которая обрабатывает передаваемое в конвейере содержимое из нашего сценария PowerShell. Данная функция будет вызываться для каждой обрабатываемой из стандартного ввода строки. Эта функция выделяет возможные собственные имена из строки на входе и добавляет их в свой словарь. Если это имя уже имеется в данном словаре, наша функция обновляет значение этого словаря, которое содержит частоту вхождения для такого конкретного возможносго собственного имени.

Основной элемент программы: Наша основная программа вначале выводит на печать несколько сообщений заголовка. Затем создаёт некий пустой properNamesDictionary. После этого, как и в примере BasicOne.py, этот сценарий обрабатывет все строки из стандартного системного входа, предоставляемого нашим сценарием PowerShell. Каждая строка затем преобразовывается при помощи заданного регулярного выражения для исключения не- альфа символов. Все преобразованные строки передаются в нашу функцию ExtractProperNames совместно с текущим properNamesDictionary. Этот процесс повторяется после этого для каждой предоставляемой нашим сценарием строки.

Вывод на печать получаемых в результате возможных имён собственных: Данный окончательный раздел сортирует полученный в результате словарь по числу обнаруженных вхождений (более высокие значения первыми), а потом выводит на печать каждое собственное имя и связанное с ним значение счётчика.

 

Листинг 6-1. Сценарий Python ProperNames.py


'''
Copyright (c) 2019 Python Forensics and Chet Hosmer

Настоящим предоставляется бесплатное разрешение любому получающему копию этого программного обеспечения и связанных с ним файлов документации лицу, (далее - «Программное обеспечение»), осуществлять операции с Программным обеспечением без ограничений, включая, помимо прочего, права на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование и/ или продаважу копии Программного обеспечения и разрешает лицам, которым предоставляется Программное обеспечение, делать это при соблюдении следующих условий:

Вышеуказанное уведомление об авторском праве и данное уведомление о разрешении должны быть включены во все копии или существенные части Программного обеспечения.

 ProperNames Demonstration
 Version 1.3
 January 2019
 Requirement: Python 3.x

 usage:
 stdin | python properNames.py

 Сценарий обработает поставляемые конвейером данные

'''

''' Раздел импорта библиотек '''

# импорт стандартного модуля sys
import sys

# импорт библиотеки регулярных выражений
# для фильтрации нежелательных символов
import re

# импорт метода datetime из стандартной библиотеки
from datetime import datetime

''' Раздел определения стоп- слов '''

# Общий списо стоп- слов
# Что такое стоп- слова: Слова, которые 
# обычно отфильтровываются прочь
# при обработке данных естественного языка (текста)
# вы вольны добавить в этот список дополнительные слова

STOP_WORDS = [
"able","about","above","accordance","according",
"accordingly","across","actually","added","affected",
"affecting","affects","after","afterwards","again",
"against","almost","alone","along","already","also",
"although","always","among","amongst","announce",
"another","anybody","anyhow","anymore","anyone",
"anything","anyway","anyways","anywhere","apparently",
"approximately","arent","arise","around","aside",
"asking","auth","available","away","awfully","back",
"became","because","become","becomes","becoming",
"been","before","beforehand","begin","beginning",
"beginnings","begins","behind","being",
"believe","below","beside","besides","between",
"beyond","both","brief","briefly","came","cannot",
"cause","causes","certain","certainly","come",
"comes","contain","containing","contains","could",
"couldnt","date","different","does","doing","done",
"down","downwards","during","each","effect","eight",
"eighty","either","else","elsewhere","end",
"ending","enough","especially","even","ever",
"every","everybody","everyone","everything",
"everywhere","except","fifth","first","five",
"followed","following","follows","former","formerly",
"forth","found","four","from","further",
"furthermore","gave","gets","getting",
"give","given","gives","giving","goes",
"gone","gotten","happens","hardly","has","have",
"having","hence","here","hereafter","hereby",
"herein","heres","hereupon","hers","herself",
"himself","hither","home","howbeit","however",
"hundred","immediate","immediately","importance",
"important","indeed","index","information",
"instead","into","invention","inward","itself",
"just","keep","keeps","kept","know","known",
"knows","largely","last","lately","later","latter",
"latterly","least","less","lest","lets","like",
"liked","likely","line","little","look","looking",
"looks","made","mainly","make","makes","many",
"maybe","mean","means","meantime","meanwhile",
"merely","might","million","miss","more","moreover",
"most","mostly","much","must","myself","name",
"namely","near","nearly","necessarily","necessary",
"need","needs","neither","never","nevertheless",
"next","nine","ninety","nobody","none","nonetheless",
"noone","normally","noted","nothing","nowhere",
"obtain","obtained","obviously","often","okay",
"omitted","once","ones","only","onto","other",
"others","otherwise","ought","ours","ourselves",
"outside","over","overall","owing","page","pages",
"part","particular","particularly","past","perhaps",
"placed","please","plus","poorly","possible","possibly",
"potentially","predominantly","present","previously",
"primarily","probably","promptly","proud","provides",
"quickly","quite","rather","readily","really","recent",
"recently","refs","regarding","regardless",
"regards","related","relatively","research",
"respectively","resulted","resulting","results","right",
"run","said","same","saying","says","section","see",
"seeing","seem","seemed","seeming","seems","seen",
"self","selves","sent","seven","several","shall",
"shed","shes","should","show","showed","shown",
"showns","shows","significant","significantly",
"similar","similarly","since","slightly","some",
"somebody","somehow","someone","somethan",
"something","sometime","sometimes","somewhat",
"somewhere","soon","sorry","specifically","specified",
"specify","specifying","still","stop","strongly",
"substantially","successfully","such","sufficiently",
"suggest","sure","take","taken","taking","tell",
"tends","than","thank","thanks","thanx","that",
"thats","their","theirs","them","themselves","then",
"thence","there","thereafter","thereby","thered",
"therefore","therein","thereof","therere",
"theres","thereto","thereupon","there've","these",
"they","think","this","those","thou","though","thought",
"thousand","through","throughout","thru","thus",
"together","took","toward","towards","tried","tries",
"truly","trying","twice","under","unfortunately",
"unless","unlike","unlikely","until","unto","upon",
"used","useful","usefully","usefulness","uses","using",
"usually","value","various","very","want","wants",
"was","wasnt","welcome","went","were","what","whatever",
"when","whence","whenever","where","whereafter","whereas",
"whereby","wherein","wheres","whereupon","wherever",
"whether","which","while","whim","whither","whod",
"whoever","whole","whom","whomever","whos","whose",
"widely","will","willing","wish","with","within","without",
"wont","words","world","would","wouldnt",
"your","youre","yours","yourself","yourselves"]

''' Раздел определения псевод- констант '''

# ПСЕВДО КОНСТАНТЫ,
# Вы вольны изменять минимальное и максимальное 
# значения длины имени
MIN_SIZE = 3      # Минимальная длина сосбтвенного имени
MAX_SIZE = 20     # Максимальная длина сосбтвенного имени

''' Раздел выделения сосбтвенных имён '''

def ExtractProperNames(theString, dictionary):
    ''' Введите строку для поиска,
        Словарь вывода собственных имён
    '''

    # Выделение всех последующих строк символов

    wordList = theString.split()

    # Теперь давайте определим какие слова являются возможными
    # собственными именами и создадим их список.

    '''
    Для данного примера те слова, которые рассматриваются возможными
    собственными именами это:
    1) Название (начинающееся с заглавной буквы слово)
    2) Соотвествующие критерию минимальной и максимальной длины
    3) То слово, которое НЕ содержится в списке стоп- слов

    Встроенный метод Python string.istitle()
    применяется для выявления названий
    '''

    for eachWord in wordList:

        if eachWord.istitle() and len(eachWord) >=
              MIN_SIZE and len(eachWord) <= MAX_SIZE and
              eachWord.lower() not in STOP_WORDS:
            '''
            если данное слово соответствует предписанным условиям
              оно добавляется в имеющийся  properNamesDictionary
            '''
            try:
                # если это слово имеется в данном словаре
                  # тогда в значение частот прибавляется 1
                cnt = properNamesDictionary[eachWord]
                properNamesDictionary[eachWord] =
                   cnt + 1
            except:
                # Если это слово пока не в 
                  # словаре
                # добавим его и установим число 
                # вхождений равным 1
                properNamesDictionary[eachWord] = 1
        else:
            # в противном случае идём циклом для своего следующего возможного слова
            continue

    # данная функция возвращает созданный
    #   properNamesDictionary

    return properNamesDictionary

# Конец функции выделения собственных имён

''' Раздел входа в главную программу '''

'''
Основная программа для выделения собственных имён
'''
if __name__ == "__main__":

    ''' Точка входа в основную программу '''
    print("\nPython Proper Name Extraction ")
    print("Python Forensics, Inc. \n")
    print("Сценарий запущен", str(datetime.now()))
    print()

    # Создание пустого словаря
    properNamesDictionary = {}

    for eachLine in sys.stdin:

        txt = re.sub("[^A-Za-z']", ' ', eachLine)
        '''
        Вызов даннойфункции ExtractProperNames
        которая возвращает некий словарь Python возможных
        собственных имён совместно с общим числом вхождений
        этого имени.

        Данная функция выполняет всю тяжесть
        выделения всех возможных собственных имён
        '''
        properNamesDictionary =
           ExtractProperNames(txt,
           properNamesDictionary)

    # После считывания всех строк на входе
    # получаемое значение является числом вхождений
    # данного собственного имени

    # Данный подход выведет на печать все возможные
    # собственные имена при том,
    # что первыми выводятся наиболее часто встречающиеся

    '''
    Раздел вывода на печать возможных
    собственных имён '''

    print()

    for eachName in sorted(properNamesDictionary,
        key=properNamesDictionary.get, reverse=True):
        print('%4d'  %
             properNamesDictionary[eachName],end="")
        print( '%20s' % eachName)
    print("\n\nScript Ended", str(datetime.now()))
    print()

# Конец Основной функции
 	   

Исполнение совмещённых PowerShell для сценариев ProperNames Python

После этого мы выполнили свой сценарий PowerShell в небольшом каталоге текстовых файлов. Эти файлы были сохранены в папке C:\PS\Text для упрощения доступа. Вы можете изменить имя папки назначения в переменной $targetPath. Результаты на Рисунок 6-5.

 

Рисунок 6-5


Результат вывода сочетания PowerShell/Python (для краткости вывод усечён)

Наш вывод разбит на три раздела:

  • Расздел 1: Это вырабатываемый CmdLet Write-Host вывод внутри нашего сценарий PowerShell.

  • Расзделы 2-3: Это результаты, вырабатываемые сценарием Python обрабатывающим BookOne.txt. Этот вывод повторяется обработкой BookTwo.txt, поскольку PowerShell обходит в цикле все те текстовые файлы, которые он находит в предписанном каталоге.

После изучения полученного вывода сочетания сценариев PowerShell/ Python даже в таком усечённом виде, вы скорее всего способны определить значение текста, их которого выделены эти возможние собственные имена. Это всего лишь одна из возможностей обработки содержимого файлов, получаемых PowerShell с последующей доставкой его вывода в Python для последующей обработки.

Подобное сочетание составляет некую модель базового уровня, которая затем может дублироваться для дополнительных результатов. Кроме того, добавляя в вашем сценарии PowerShell последующие Invoke-Command, вы можете собирать файлы и содержимое файлов по всей своей корпорации. Теперь давайте рассмотрим некий иной подход, который передаёт какой- то список названий файлов в сам сценарий Python вместо того чтобы передавать собственно содержимое этих файлов.

Выделение данных EXIF из фотографий

В этом примере наш сценарий PowerShell будет оставаться небольшим, а вся тяжёлая работа будет отдана в наш сценарий Python, где мы воспользуемся ключевыми библиотеками для выделения данных EXIF, содержащих информацию о геолокации, заключённую в соответствующих заголовках EXIF образов JPEG.

Сценарий PoerShell

Наш сценарий PowerShell на Рисунке 6-6 разбит на четыре общих элемента с небольшим переплетением.

  1. Определение локальной переменной $Python с установленным полным путём к самому выбранному вами исполняемому файлу интерпретатора Python. Для днного примера мы снова будем применять Python 3.x.

  2. Задание некой локальной переменной $Script, которая определяет значение полного пути к тому сценарию Python, который будет исполняться.

  3. Установление локальной переменной $fles, которая хранит набор файлов, соответствующих критерию поиска *.jpg. Значение локальной переменной $jpegList вычленяет полный путь к каждому из файлов и исключает заголовки, оставляя лишь список файлов, который мы намерены обработать.

  4. Эта строка передаёт само содержимое нашей локальной переменной $jpegList в применяемый сценарий Python. Основным ключевым элементом здесь выступает оператор амперсанда (&), который указывает PowerShell нзагрузить необходимую внешнюю программу. Наш сценарий Python получит все получаемые сценарием PowerShell полные названия путей, причём по одному в каждой строке, передаваемые через stdin.

 

Рисунок 6-6


Сценарий PowerShell PhotoMap.ps1

Сценарий Python pyGeo.py

Наш сценарий Python приводится в Листинге 6-2, разбитом на восемь основных разделов озаглавленных так:

  1. Импорт библиотек

  2. Определение псевдоконстант

  3. Выделение словаря GPS

  4. Выделение широты и долготы

  5. Преобразование координат GPS в градусы

  6. Основной элемент программы

  7. Выработка таблицы результатов

  8. Выработка файла CSV

Импорт библиотек: Как и подразумевает его название, это именно то место, в котором загружаются необходимые библиотеки Python. Они включают:

  • sys: Как было показано в BasicOne, эта библиотека позволяет нам орбрабатывать ввод командной строки, доставляемый со стороны PowerShell.

  • re: Это библиотека регулярных выражений Python, которая применяется в данном сценарии для снятия внешних символов из получаемого текста для упрощения поиска собственных имён.

  • datetime: Как и подразумевает её название, данная библиотека предоставляет методы для отображения и вычисления деталей времени и даты.

  • PIL: Библиотека изображений Python стороннего разработчика, предоставляющая методы для доступа и извдечения данных EXIF, содержащих информацию геолокации.

  • prettytable: Библиотека Python стороннего разработчика, предоставляющая возможность табличного вывода данныъ внутри простой табличной структуры на основе текста.

Выделение словаря GPS: В эту функцию передаётся некое имя файла для его обработки и выполняется проверка что этот файл является допустимым изображением, причём содержащим информацию о геолокации. Если это так, собирается данная информация геолокации, а возвращаются основные данные EXIF в виде словаря GPS.

Выделение широты и долготы: Данная функция выделяет из предоставленного словаря GPS значения GPSLatitude и GPSLongitude, а также связанные ссылки. Эти значения не хранятся в виде градусов, как на то рассчитывает большая часть программ. Следовательно, они преобразовываются в градусы при помощи функции ConvertToDegress. Затем надлежащим образом устанавливается их ориентация. Например, если долгота относится к Южной, тогда значение долготы в градусах должно устанавливаться в отрицательное значение.

Преобразование в градусы: Эта функция и выполняет преобразование хранимых в полученных данных EXIF координат GPS в градусы.

Основной элемент программы: Наша основная программа вначале выводит несколько сообщений заголовка. Затем создаёт некий пустой список картинок. После этого, как и в примере BasicOne.py, данный сценарий обрабатывает все строки из стандартного системного ввода, предоставляемого соответствующим сценарием Powershell. Каждая строка содержит значение полного пути файлов, выявленных сценарием PowerShell. Все названия файлов затем дописываются в конеци нашего перечня картинок.

После этого создаётся пустой latLonList для сохранения результатов выделения значений GPS из каждой картинки. Каждый файл после этого проверяется на наличие, а затем вызывается словарь выделенных GPS. Если получаемый словарь GPS содержит данные, вызывается функция выделения широты и долготы. Если находятся допустимые данные широты/ долготы, в конец списка latLonList добавляются название базового имени файла, а также данные значений широты/ долготы.

Выработка таблицы результатов: Раздел выработки резулттирующей таблицы производит некую удобную таблицу результатов из latLonList. После создания этой таблицы она выводится на печать с тем, чтобы все выделенные результаты могли быть отображены в PowerShell.

Выработка файла CSV: Наконец, наш сценарий вырабатывает некий файл LatLon.csv разделяемых запятой значений (comma separated value). Он форматируется таким образом, чтобы его можно было импортировать в инструменты разметки на основе веб.

 

Листинг 6-2. Сценарий Python pyGeo.py


'''
EXIF Data Acquistion
January 2019
Version 1.1
'''

'''
Copyright (c) 2019 Chet Hosmer, Python Forensics

Настоящим предоставляется бесплатное разрешение любому получающему копию этого программного обеспечения и связанных с ним файлов документации лицу, (далее - «Программное обеспечение»), осуществлять операции с Программным обеспечением без ограничений, включая, помимо прочего, права на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование и/ или продаважу копии Программного обеспечения и разрешает лицам, которым предоставляется Программное обеспечение, делать это при соблюдении следующих условий:

Вышеуказанное уведомление об авторском праве и данное уведомление о разрешении должны быть включены во все копии или существенные части Программного обеспечения.

'''
# Пример использования:
# fileList | python pyExif.py
#
# Requirement: Python 3.x
#
# Requirement: Применяемая библиотека  
#              стороннего производителя : PILLOW
#              для установки PILLOW воспользуйтесь такой CMD
#              из своей командной строки
#
#              pip install PILLOW
#
# Данный сценарий будет выделять данные EXIF/GEO из файлов jpeg
# передаваемых через конвейер в данный сценарий и выработает табличный список 
# выделенных EXIF и данных геолокации совместно с 
# созданием некого файла CSV с данными LAT/LON
#

''' Оаздел импорта библиотек '''
# Стандартный Python: Методы операционной системы
import os

# Стандартный Python : Методы системы
import sys

# Стандартный Standard метод datetime из Стандартной библиотеки
from datetime import datetime

# импорт Python Image Library
# совместно с  TAGS и относящимся к TAGS GPS
# Отметим, что вам следует установить модуль PILLOW
# pip install PILLOW

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS

# Импорт библиотеки PrettyTable для выработки
# табличных результатов

from prettytable import PrettyTable

''' Раздел выделения словаря GPS '''

#
# Выделяем данные EXIF 
#
# Вход: Полное имя пути к соответствующему файлу образа
#
# Вывод: gсловарь ps и выделенный список exifData
#
def ExtractGPSDictionary(fileName):

    try:
        pilImage = Image.open(fileName)
        exifData = pilImage._getexif()

    except Exception:
        # If exception occurs from PIL processing
        # Report the
        return None, None

    # Выполняем итерации по exifData
    # Выискивая Tags  GPS

    imageTimeStamp = "NA"
    cameraModel = "NA"
    cameraMake = "NA"
    gpsData = False

    gpsDictionary = {}

    if exifData:

        for tag, theValue in exifData.items():

            # поллучаем значение tag
            tagValue = TAGS.get(tag, tag)

            # Собираем базовые данные образа, если он доступен

            if tagValue == 'DateTimeOriginal':
                imageTimeStamp =
                              exifData.get(tag).strip()

            if tagValue == "Make":
                cameraMake = exifData.get(tag).strip()

            if tagValue == 'Model':
                cameraModel = exifData.get(tag).strip()

            # проверяем наличие тега GPS
            if tagValue == "GPSInfo":

                gpsData = True;

                # Нашли !
                # Применяем Словарь для сбора данных GPS
                # Обходим в цикле всю информацию GPS 
                for curTag in theValue:
                    gpsTag = GPSTAGS.get(curTag, curTag)
                    gpsDictionary[gpsTag] =
                                     theValue[curTag]

        basicExifData = [imageTimeStamp,
                         cameraMake, cameraModel]

        return gpsDictionary, basicExifData

    else:
        return None, None

# Конец ExtractGPSDictionary ============================

''' Раздел выделения широты и долготы '''

#
# Выделяем значения широты и долготы
# Из полученного gpsDictionary
#

def ExtractLatLon(gps):
    # для выполнения вычислений нампо крайней мере требуются
    # lat, lon, latRef и lonRef

    try:
        latitude     = gps["GPSLatitude"]
        latitudeRef  = gps["GPSLatitudeRef"]
        longitude    = gps["GPSLongitude"]
        longitudeRef = gps["GPSLongitudeRef"]

        lat = ConvertToDegrees(latitude)
        lon = ConvertToDegrees(longitude)

        # проверяем ссылку широты
        # если Южнее экватора, тогда
             lat value is negative

        if latitudeRef == "S":
            lat = 0 - lat

        # проверяем ссылку долготы
        # Если восточнее основного меридиана в
        # Гринвиче тогда значение долготы отрицательное

        if longitudeRef == "W":
            lon = 0- lon

        gpsCoor = {"Lat": lat,
                   "LatRef":latitudeRef,
                   "Lon": lon,
                   "LonRef": longitudeRef}

        return gpsCoor

    except:
        return None

# Конец выделения Lat Lon =======================================

''' Преобразование координат GPS в градусы '''

#
# Преобразовываем GPSCoordinates в градусы
#
# входное значение gpsCoordinates в формате EXIF
#

def ConvertToDegrees(gpsCoordinate):

    d0 = gpsCoordinate[0][0]
    d1 = gpsCoordinate[0][1]
    try:
        degrees = float(d0) / float(d1)
    except:
        degrees = 0.0
    m0 = gpsCoordinate[1][0]
    m1 = gpsCoordinate[1][1]
    try:
        minutes = float(m0) / float(m1)
    except:
        minutes=0.0

    s0 = gpsCoordinate[2][0]
    s1 = gpsCoordinate[2][1]
    try:
        seconds = float(s0) / float(s1)
    except:
        seconds = 0.0

    floatCoordinate = float (degrees + (minutes / 60.0) + (seconds / 3600.0))

    return floatCoordinate

''' Раздел входа в основную программу '''

if __name__ == "__main__":
    '''
    Точка входа в основную программу pyExif 
    '''
    print("\nExtract EXIF Data from JPEG Files")
    print("Python Forensics, Inc. \n")

    print("Сценарий запущен", str(datetime.now()))
    print()

    ''' Раздел обработки данныйх, передаваемых конвейером из POWERSHELL '''

    pictureList = []

    # Обработка данных из стандартного ввода в качестве перечня файлов

    for eachLine in sys.stdin:
        entry = eachLine.strip()
        if entry:
            pictureList.append(entry)

    print("Обработка фотор=графий ...")
    print()

    # CDH
    # Создаём соответствующий обхект

    ''' Раздел обработки каждого файла JPEG '''

    latLonList = []

    for targetFile in pictureList:

        if os.path.isfile(targetFile):

            gpsDictionary, exifList =
                    ExtractGPSDictionary(targetFile)

            if exifList:
                TS = exifList[0]
                MAKE = exifList[1]
                MODEL = exifList[2]
            else:
                TS = 'NA'
                MAKE = 'NA'
                MODEL = 'NA'

            if (gpsDictionary != None):

                # Получение значений Lat Lon из
                # соответствующего gpsDictionary
                #   Преобразование в градусы
                # Возвращаемым значением является некий словарь
                #   пар ключ значение

                dCoor = ExtractLatLon(gpsDictionary)

                if dCoor:
                    lat = dCoor.get("Lat")
                    latRef = dCoor.get("LatRef")
                    lon = dCoor.get("Lon")
                    lonRef = dCoor.get("LonRef")
                    if ( lat and lon and
                         latRef and lonRef):
                        latLonList.append(
                          [os.path.basename(targetFile),
                          '{:4.4f}'.format(lat),
                          '{:4.4f}'.format(lon),
                          TS, MAKE, MODEL])

                    else:
                        print("ПРЕДОСТЕРЕЖЕНИЕ",
                              "нет данных EXIF GPS для ",
                              targetFile)
                else:
                    continue
            else:
                continue
        else:
            print("ПРЕДОСТЕРЕЖЕНИЕ", " недопустимый файл", targetFile)

    # Создание результирующей таблицы отображения с помощью PrettyTable

    ''' Раздел выработки результирующей таблицы '''

    ''' Заголовок таблицы результатов '''
    resultTable = PrettyTable(['Название-файла',
                      'Шир','Дол',
                      'ВремШтамп',
                      'Make', 'Model'])

    for loc in latLonList:
        resultTable.add_row( [loc[0], loc[1],
                              loc[2], loc[3],
                              loc[4], loc[5] ])

    resultTable.align = "l"
    print(resultTable.get_string(sortby="File-Name"))

    ''' Раздел выработки файла CSV '''

    # Создаём простой файл результатов CSV
    with open("LatLon.csv", "w") as outFile:

        # Запись заголовка
        outFile.write("Name, Lat, Long\n")

        # Обрабатываем все записи и записываем их
        # все строки разделены запятыми

        for loc in latLonList:
            outFile.write(loc[0]+","+
                          loc[1]+","+
                          loc[2]+"\n")

    print("Файл LatLon.csv успешно создан")

    print("\nКонец сценария", str(datetime.now()))
    print()
 	   

Исполнение совмещённых PowerShell для сценариев exifxtract Python

Заключительным этапом является исполнение нашего сценария PowerShell, оторый передаст выявленные имена файлов в наш сценарий Python. Папка C:\PS\Photos содержит набор фотографий JPEG для исследования. Изменяя значение переменной $files в соответствующем сценарии PowerShell вы можете определить альтернативный каталог для опытов. Рассмотрите Рисунок 6-7 .

 

Рисунок 6-6


Выполнение PhotoMap.ps1

Данный сценарий обрабатывает каталог примера с девятью файлами изображений JPEG. Получаемые результаты влючены в таблицу имён файлов со связанными с ними выявленными значениями Широты/ Долготы. Также был создан файл LatLon.csv. Получаемые в итоге результаты могут далее импортироваться в веб ресурсы, ткие как Google Maps для графического представления результатов.

Выводы

Данная глава была сосредоточена на разработке некой модели исполенния сценариев Python из PowerShell. Такая модель применяет стандартную модель конвейеров PowerShell для выборки искомых данных и предоставления соотвествующего вывода в определённый сценарий Python с примененеим метода конвейера PowerShell.

Эти примеры были сфокусированы на небольших сценариях PowerShell, которые выполняли дискретную выборку, а заетм в конце концов применяли богатые возможности Python для выполнения тяжёлой нагрузки выработки искомых результатов.

Такая модель предоставляет богатый базовый уровень для экспериментов, выборки и сочетания PowerShell и Python. В некоторых случаях данная модель выглядит более простой чем метод подпроцессов, применяемых для исполнения сценариев PowerShell из Python. Обе, само собой, имеют свои места, будь то контроль и автоматизация имеющихся сценариев PowerShell или управление выводом из PowerShell в Python.