Python, urllib und CERTIFICATE_VERIFY_FAILED

Da denkt man sich einfach "mal kurz mein Blogging-Container neu bauen" und schon funktioniert nichts mehr. Es fing schon damit an das mein Ansible Playbook Probleme hatte Files herrunterzuladen. urllib brach immer mit urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] ab. Wie aus dem nichts. Vor ein paar Wochen gab es keine Probleme mit der urllib. Es gab sogar ein StackOverflow Posting welches mir riet einfach mal die ca-certificates zu installieren. Das Paket war aber schon installiert. Nach viel googeln un verzweifeln bin ich auf eine obskure Seite gestoßen von der ich kaum etwas verstand. Es gab wohl die Möglichkeit urllib über eine Environment-Variabel den Ort der CA-Zertifikate mitzuteilen. Dies scheint auch in der PEP 476 so beschrieben. requests scheint dies per Default zu machen. Ein export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt fixed die Situation in Alpine Linux. Ich frage mich nur was sich in ein paar Wochen so verändert hat das dieses Problem auftritt.

Ansible Playbooks und variable Hosts

Ich benutze Ansible für viele Sachen. Unter anderem habe ich ein Set an Playbooks und Roles um meine Server so anzupassen das ich mich auf ihnen wohl fühle. Bis jetzt habe ich immer Ansible auch auf den Servern installiert und es dann lokal ausgeführt. Aber dabei nutze ich nicht das eigentlich Ansible Feature: das Ganze Remote über SSH auszurollen. Problem: In den Playbooks muss es die Zeile - hosts: geben. Aber eigentlich will ich das alles Variabel ausführen können. Zum Beispiel nutze ich die gleichen Files auch um meine Vagrant Container einzurichten. Wieso also beim Aufruf die Hosts dem Playbook nicht einfach übergeben? Die Lösung ist dann doch wieder einmal einfacher als man denkt. Man benutzt die Möglichkeit in Playbooks und Roles Variabeln einzusetzen.

---
- hosts: "{{ hosts }}"
  roles:
    - git
    - tmux
    - vim
    - zsh

Diese Variabel übergeben wir beim Aufruf von Ansible.

ansible-playbook base.yml --extra-vars="hosts=myservers"

Nochmal Serien Plots, XKCD Style

Immer noch ziemlich nutzlos aber diesmal mit XKCD-Eyecandy. Bokeh ist ja ganz schön, braucht aber eine ziemlich große JS-Komponente die immer geladen werden muss. Mit matplotlib kann man statische Grafen zeichnen und mit %matplotlib notebook vorher einen Auschnitt wählen.

Ich wollte mal schauen wie sich die Ratingverläufe der Staffeln einer Serie entfalten. Eventuell kann man ja ein daramaturgischen Verlauf erkennen. Ich als jemand der selber keine Ratings abgibt. Alles befindet sich doch ziemlich auf einem Level.

In [1]:
import pandas as pd
import matplotlib
import numpy as np
from trakt.tv import TVShow

Nach dem import der Libs führen wir folgendes Jupyter-Magic aus um die erzeugten Plots im Notebook noch leicht anpassen zu könne.

In [2]:
%matplotlib notebook

Wir aktivieren die XKCD-Style-Plots:

In [3]:
matplotlib.pyplot.xkcd()
Out[3]:
<matplotlib.rc_context at 0xaf0ef58c>

Nun kommt die Funktion die alle Daten einsammelt und ausgibt:

In [4]:
def season_ratings(name):
    tv_show = TVShow(name)
    
    data = {}
    for season in tv_show.seasons:
    
        if season.season == 0:
            continue
    
        ratings = []
        for episode in season.episodes:
            ratings.append(episode.rating)
    
        data[season.season] = pd.Series(ratings)

    df = pd.DataFrame(data)
    df.plot(colormap=matplotlib.cm.Accent)

Beispiele

In [5]:
season_ratings('The IT-Crowd')
In [6]:
season_ratings('Roswell')
In [7]:
season_ratings('Veronica Mars')
In [8]:
season_ratings('Breaking Bad')
In [9]:
season_ratings('Community')

Ratingverläufe unterschiedlicher Serien

Nun lassen wir mal ein paar Serien gegeneinander antreten. Dabei betrachten wir immer alle Episoden aller Staffeln.

In [12]:
def series_ratings(series):
    data = {}
    
    for serie in series:
        
        show = TVShow(serie)
        
        ratings = []
        for season in show.seasons:
            
            if season.season == 0:
                continue
            
            for episode in season.episodes:
                ratings.append(episode.rating)
        
        data[serie] = pd.Series(ratings)
        
    df = pd.DataFrame(data)
    df.plot(colormap=matplotlib.cm.Accent)

Beispiel

In [13]:
series_ratings(['Gotham', 'Flash', 'Arrow', 'Supergirl'])

Mir ist gerade nichts Besseres eingefallen. Es müssen ja in irgendeinen Sinn vergleichbare Serien sein. Da habe ich mir mal die vier aktuellen DC-Serien angeschaut. Da Arrow schon einige Episoden mehr hat, habe ich den Plot angepasst und hinten abgeschnitten. Glaubt man den Ratings, hätte ich Supergirl doch mehr Chancen als zwei Folgen geben sollen. War in den ersten Folgen einfach zu glatt gebügelt.

Fernsehserien, Trakt, Jupyter und Bokeh

Datenvisualisierung hat mich schon immer fasziniert. Leider hatte ich nie einen wirklichen Grund dafür es einzusetzen. Und doch wollte ich es mal ausprobieren. Ich habe mich sehr von Jemus inspirieren lassen. Da passiert natürlich viel viel mehr und ausführlicher. Ich habe erstmal das gemacht was ich auch verstehe :). Und zwar die Episoden Ratings darzustellen.

Als erstes importiere ich ein paar Sachen. Erstmal choice um per Zufall Farben aus einer Palette auszuwählen. Dann pandas für die DataFrames. Um die Daten aus der Trakt-API zu bekommen benutze ich trakt.py. Wieso alles selber bauen wenn es dieses schöne Modul gibt? Dann benutze ich Bokeh um das ganze darzustellen.

In [1]:
from random import choice

import pandas as pd
from trakt.tv import TVShow
from bokeh.plotting import figure, ColumnDataSource, output_notebook, show
from bokeh.models import HoverTool
from bokeh.palettes import Spectral9

Wir starten die Bokeh Ausgabe in einem jupyter-Notebook.

In [2]:
output_notebook()
BokehJS successfully loaded.

Hier ist die Funktion die die Daten besorgt, verarbeitet und darstellt.

In [3]:
def ratings(show_name):
    
    # Es wird die Show definiert
    tv_show = TVShow(show_name)
    
    # Um die Staffeln ausseinander zu halten, soll jede Episode eine unterschiedliche 
    # Staffelfarbe haben. Bokeh hat ein paar Farbpaletten dabei. Ich benutze Spectral9.
    # Dazu brauchen wir noch eine liste um alle Episoden-Staffel-Farben zu speichern
    colormap = Spectral9
    colors = []
    
    # Wir brauchen eine Episoden Liste
    episodes = []
    
    # Damit die Episoden hintereinander auf der X-Achse dargestellt werden, brauchen sie
    # eine fortlaufende Nummer
    episode_number = 1
    
    # Dann arbeiten wir uns mal durch die Staffeln
    for season in tv_show.seasons:
        
        # Season 0 beinhaltet die Specials. Die werden ingnoriert
        if season.season == 0:
            continue
        
        # Eine Staffel-Farbe wird per Zufall ausgewaehlt. Wir schmeissen immer die letzte Farbe
        # aus einer Kopie der Farbenliste damit sie nicht nochmal gewaehlt wird
        if colors:
            new_colormap = colormap[:]
            new_colormap.remove(colors[-1])
            color = choice(new_colormap)
            
        else:
            color = choice(colormap)
        
        # Die Episoden 
        for episode in season.episodes:
            
            # Wenn es eine Ausstrahlungsdatum gibt, wird der Zeit-Teil weggeschnitten
            if episode.first_aired:
                aired = episode.first_aired.split('T')[0]
            else:
                aired = None
            
            # Nun werden die gesammelten Daten als Tupel der Episodenliste angehangen
            episodes.append((episode_number,
                             episode.season,
                             '{}.{}'.format(episode.season,
                                            episode.number), 
                              episode.title, 
                              aired, 
                              episode.rating))
            
            # Die Episoden-Staffel-Farbe wird der Farbenliste angehangen
            colors.append(color)
            
            # Die Episodennummer wird erhoeht
            episode_number += 1
                
    # Es wird ein DataFrame erstellt
    df = pd.DataFrame(
        episodes, 
        columns=['episode_number', 'season', 'episode', 'title', 'aired', 'rating'])

    # Es wird ein source-Objekt gebraucht damit die tooltips funktionieren.
    # Der DataFrame genuegt nicht um die Daten fuer die tooltips zu benutzen
    source = ColumnDataSource(data=df)
    
    # Wir erstellen in figure und belabeln die Achsen
    fig = figure(title=show_name)
    fig.xaxis.axis_label = 'Episodes'
    fig.yaxis.axis_label = 'Rating'
    
    # Hier bauen wir den Scatter-Plot. Wir uebergeben X und Y plus unser source-Objekt
    # und Style Geschichten. Unter anderem unsere Episoden-Farbenliste
    fig.scatter(df['episode_number'], df['rating'], 
                source=source, name='main', 
                fill_alpha=0.8, line_color='#000000', size=10, color=colors)
    
    # Ein HoverTool-Object in dem wir das Layout fuer die Tooltips festlegen
    hover = HoverTool(tooltips=[('Episode', '@episode'),
                                ('Title', '@title'),
                                ('Rating', '@rating'),
                                ('Aired', '@aired')])
    
    # Das Tool wird hinzugefuegt
    fig.add_tools(hover)
    
    # Und nun angezeigt
    show(fig)

Ich gebe eigentlich nicht viel auf die Ratings. Ich selber habe über Trakt noch nie etwas bewertet. Trotzdem handelt es sich um gute Daten um ein paar Sachen auszuprobieren.

Hier ein paar Ratings von Serien die mit entweder viel bedeuten oder die ich gerade schaue.

Roswell

In [4]:
ratings('Roswell')

Die erste Serie bei der ich von einem persönlichen Fandom sprechen kann. Noch bevor ich wusste das es das Wort gibt. Ich habe damals sogar alle Bücher gelesen. Kurz hatte ich nachgedacht sie wegzuschmeissen. Leicht peinlich berührt meiner Serienliebe von damals. Habe mich doch dagegen entschieden. Die erste Staffel war grossartig. Der perfekte Mix aus Highschool und SciFi/Mystery. Danach nahm es für mich drastisch ab. Die dritte Staffel habe ich noch nicht einmal schauen können. Zu gross war die Angst vor dem Zerstören meiner Liebe zur Serie.

Veronica Mars

In [5]:
ratings('Veronica Mars')

Vielleicht meine absolute Lieblingsserie. Veronica Mars habe ich vielleicht 4 oder 5 mal durchgeschaut. Und ich glaube dabei wird es nicht bleiben. Es so eine Serie die immer herhalten muss wenn es mir mal nicht so gut geht. Ein paar Folgen Veronica Mars sind immer eine gute Ablenkung. Alle drei Staffeln haben ungefähr die gleiche Bewertung. Dies deckt sich auch mit meiner Meinung. Drei gleich starke Staffeln. Wieso nur wurde die Serie abgesetzt? Das College-Szenario hätte noch soviel zu bieten gehabt.

Akte X

In [6]:
ratings('The X-Files')

Mit einem Knall wurde auf einmal eine zehnte Staffel angekündigt und gestern lief davon die dritte Folge. Auch wenn ich nur ein wenig mit Akte X vertraut bin, die drei Episoden haben mir bis jetzt sehr gefallen. Anscheinend alle Staffeln mit gleichen Höhen und Tiefen.

Downton Abbey

In [7]:
ratings('Downton Abbey')

Ich hätte nicht gedacht das mich diese Geschichte so packt und emotional macht. Doch Downton Abbey wurde für mich zu einer Herzensangelegenheit. Jeden Staffeln mit viel Tränen. Den Abfall der Ratings nach hinten decken sich nicht mit meiner Meinung. Ich fand alle 6 ziemlich stark.

Californication

In [8]:
ratings('Californication')

Eher so mal reingeschnuppert weil auf Netflix. Teilweise sehr unterhaltsam. Sonst kann ich nicht viel darüber sagen. Bin auch erst bei Staffel 4. Danach war ich wieder gesund und hatte keine Zeit mehr ;-).

Modern Family

In [9]:
ratings('Modern Family')