Programy narzędziowe w Pythonie

W tej sekcji przyjrzymy się kilku standardowym modułom narzędziowym Pythona, które rozwiązują typowe problemy.

System plików – os, os.path, closedil

Moduły *os* i *os.path* zawierają wiele funkcji umożliwiających interakcję z systemem plików. Moduł *shutil* może kopiować pliki.

  • dokumentacja modułu systemu operacyjnego
  • filenames = os.listdir(dir) – lista nazw plików w ścieżce katalogu (z wyłączeniem . i ..). Nazwy plików to tylko nazwy w katalogu, a nie ich bezwzględne ścieżki.
  • os.path.join(dir, filename) – po nazwie pliku z powyższej listy możesz utworzyć ścieżkę
  • os.path.abspath(ścieżka) – z podaniem ścieżki zwraca postać bezwzględną, np. /home/nick/foo/bar.html
  • os.path.dirname(ścieżka), os.path.basename(ścieżka) – z danym dir/foo/bar.html zwracanymi katalogami „dir/foo” i „bar.html”
  • os.path.exists(path) – true, jeśli istnieje
  • os.mkdir(dir_path) – tworzy jeden dir, os.makedirs(dir_path) zawiera wszystkie potrzebne dir w tej ścieżce
  • sitil.copy(ścieżka-źródłowa, ścieżka-docelowa) – skopiuj plik (katalogi ścieżki powinny istnieć)
## Example pulls filenames from a dir, prints their relative and absolute paths
def printdir(dir):
  filenames = os.listdir(dir)
  for filename in filenames:
    print(filename)  ## foo.txt
    print(os.path.join(dir, filename)) ## dir/foo.txt (relative to current dir)
    print(os.path.abspath(os.path.join(dir, filename))) ## /home/nick/dir/foo.txt

Badanie modułu dobrze działa z wbudowanymi funkcjami Pythona help() i dir(). W interpreterze wykonaj polecenie „import os”, a następnie za pomocą tych poleceń sprawdź zawartość modułu: dir(os), help(os.listdir), dir(os.path), help(os.path.dirname).

Uruchamianie procesów zewnętrznych – proces podrzędny

Moduł *podprocesu* pozwala w prosty sposób uruchomić zewnętrzne polecenie i przechwycić ich dane wyjściowe.

  • dokumentacja modułu podprocesu
  • wyjściowy = subprocess.check_output(cmd, stderr=subprocess.STDOUT) – uruchamia polecenie, czeka na jego zakończenie i zwraca tekst wyjściowy. Polecenie jest uruchamiane ze standardowymi danymi wyjściowymi i błędem standardowym połączonymi w jeden tekst wyjściowy. Jeśli operacja się nie powiedzie, powoduje wystąpienie błędu CalledProcessError.
  • Jeśli chcesz mieć większą kontrolę nad uruchomieniem podprocesu, zapoznaj się z klasą subprocess.popen
  • Dostępny jest też prosty plik subprocess.call(cmd), który uruchamia polecenie, zapisuje dane wyjściowe w danych wyjściowych i zwraca kod błędu. Działa to, jeśli chcesz uruchomić polecenie, ale nie musisz przechwytywać jego danych wyjściowych w strukturach danych Pythona.
import subprocess

## Given a dir path, run an external 'ls -l' on it --
## shows how to call an external program
def listdir(dir):
  cmd = 'ls -l ' + dir
  print("Command to run:", cmd)   ## good to debug cmd before actually running it
  (status, output) = subprocess.getstatusoutput(cmd)
  if status:    ## Error case, print the command's output to stderr and exit
    sys.stderr.write(output)
    sys.exit(status)
  print(output)  ## Otherwise do something with the command's output

działania związane z wyjątkami.

Wyjątek stanowi błąd występujący w czasie wykonywania, który wstrzymuje normalne wykonywanie w określonym wierszu i przekazuje kontrolę do kodu obsługi błędów. W tej sekcji omawiamy tylko podstawowe zastosowania wyjątków. Błąd podczas działania może na przykład wynikać z tego, że zmienna używana w programie nie ma wartości (Błąd ValueError .. zapewne zdarzyło się to już kilka razy) albo błąd operacji otwierania pliku, ponieważ plik nie istnieje (IOError). Więcej informacji znajdziesz w samouczku wyjątków i całej liście wyjątków.

Bez kodu obsługi błędów (jak do tej pory) wyjątek czasu działania wstrzymuje działanie programu i wyświetla komunikat o błędzie. To jest dobre działanie domyślne. Widziałeś(-aś) to już wiele razy. Możesz dodać do kodu strukturę „try/except”, aby obsługiwać wyjątki, na przykład:

  try:
    ## Either of these two lines could throw an IOError, say
    ## if the file does not exist or the read() encounters a low level error.
    f = open(filename, 'rb')
    data = f.read()
    f.close()
  except IOError:
    ## Control jumps directly to here if any of the above lines throws IOError.
    sys.stderr.write('problem reading:' + filename)
  ## In any case, the code then continues with the line after the try/except

Sekcja try: zawiera kod, który może zgłosić wyjątek. Sekcja z wyjątkiem: zawiera kod uruchamiany w razie wystąpienia wyjątku. Jeśli nie ma wyjątku, sekcja wyjątkiem: zostanie pominięta (kod służy wyłącznie do obsługi błędów, a nie „zwykła” wielkość liter w kodzie). Wskaźnik do samego obiektu wyjątku możesz uzyskać przy użyciu składni „oprócz IOError jako e: ..” (wskazuje on obiekt wyjątku).

HTTP – urllib i urlparse,

Moduł *urllib.request* umożliwia pobieranie adresów URL, dzięki czemu adres URL wygląda jak plik, z którego możesz odczytać dane. Moduł *urlparse* może rozdzielać i łączyć adresy URL.

  • dokumentacja modułu urllib.request
  • ufile = urllib.request.urlopen(url) – zwraca plik podobny do obiektu dla tego adresu URL.
  • text = ufile.read() – może odczytywać z niego dane, podobnie jak plik (readlines() itp. również działa).
  • info = ufile.info() -- metainformacje o tym żądaniu. info.gettype() to typ MIME, np. „text/html”
  • baseurl = ufile.geturl() – pobiera dla żądania adres URL typu „base”, który może różnić się od adresu pierwotnego z powodu przekierowań
  • urllib.request.urlretrieve(url, nazwa pliku) – pobiera dane adresu URL na podaną ścieżkę pliku
  • urllib.parse.urljoin(baseurl, url) – adres URL, który może, ale nie musi być pełny, oraz bazowy adres URL strony, z której pochodzi, zwraca pełny adres URL. Użyj metody geturl() powyżej, aby podać podstawowy adres URL.

Wszystkie wyjątki znajdują się w pliku urllib.error.

from urllib.request import urlopen

## Given a url, try to retrieve it. If it's text/html,
## print its base url and its text.
def wget(url):
  ufile = urlopen(url)  ## get file-like object for url
  info = ufile.info()   ## meta-info about the url content
  if info.get_content_type() == 'text/html':
    print('base url:' + ufile.geturl())
    text = ufile.read()  ## read all its text
    print(text)

Powyższy kod działa prawidłowo, ale nie uwzględnia obsługi błędów, jeśli adres URL z jakiegoś powodu nie działa. Oto wersja funkcji, która dodaje logikę try/exception, aby wyświetlać komunikat o błędzie w przypadku niepowodzenia operacji na adresie URL.

Jeśli serwer urlopen() się zawiesza, może to oznaczać, że system nie zezwala na bezpośredni dostęp do niektórych adresów HTTP. Możesz to sprawdzić, próbując pobrać ten sam adres URL za pomocą polecenia wget lub curl. Jeśli te programy również się nie udają, treści HTTP trzeba będzie pobrać przez serwer proxy. Ten samouczek nie obejmuje konfigurowania dostępu przez serwer proxy.

## Version that uses try/except to print an error message if the
## urlopen() fails.
def wget2(url):
  try:
    ufile = urlopen(url)
    if ufile.info().get_content_type() == 'text/html':
      print(ufile.read())
  except IOError:
    print('problem reading url:', url)

Ćwiczenie

Aby przećwiczyć działanie systemu plików i poleceń zewnętrznych, patrz Kopiowanie ćwiczenia specjalne. Aby poćwiczyć materiał z urllib, zobacz Zapisz ćwiczenie z łamigłówką.