Hugelol client für die Hugelol API

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
pl3ktrum
User
Beiträge: 6
Registriert: Dienstag 18. Juni 2013, 09:27

Hallo,

habe eine kleine Schnittstelle zur Hugelol (so eine Art Imageboard) API geschrieben.
Bin eigentlich ganz überzeugt von meinem Konzept, vor allem da alles wie gewünscht funktioniert.
Würde aber mal gerne wissen was die Experten zum Design sagen.

Code: Alles auswählen

import urllib.request
import json

class HugelolApi(object):
    def __init__(self):
        # create the opener
        self.opener = urllib.request.build_opener()
        self.opener.addheaders = [("User-agent", "Mozilla/5.0")]

        #api urls
        self.apis = {
            "front" : "http://hugelol.com/api/front.php?after={}",
            "rising" : "http://hugelol.com/api/rising.php?after={}",
            "fresh" : "http://hugelol.com/api/fresh.php?after={}",
            "top" : "http://hugelol.com/api/top.php?after={}",
            "lol" : "http://hugelol.com/api/lol.php?id={}"
        }

        # last id from the page being processed
        self.lastID = None
        # last page visited
        self.lastPage = None
        self.hasNext = False

    def getPosts(self, api, after=""):
        """uses the following apis: front, rising, fresh, top"""
        if not api in self.apis:
            raise IndexError

        url = self.apis[api].format(after)
        # proceed request, read and decode
        data = self.opener.open(url).read().decode("utf-8", errors="ignore")
        # Array is twice json encoded, so it must be decoded twice
        jsondata = json.loads(json.loads(data))

        posts = list()
        for entry in jsondata:
            posts.append({
                "lol_id" : entry[0],
                "lol_time" : entry[1],
                "lol_uid" : entry[2],
                "lol_username" : entry[3],
                "lol_type" : entry[4],
                "lol_url" : entry[5],
                "lol_title" : entry[6],
                "lol_score" : entry[7],
                "lol_comments" : entry[8],
                "lol_badge" : entry[9]
            })
        # ID from the last post in "posts" list
        self.lastID = posts[-1]["lol_id"]
        
        self.lastPage = api
        self.hasNext = True
        
        return posts

    def getNext(self):
        if self.hasNext:
            return self.getPosts(self.lastPage, self.lastID)

    def getPost(self, id):
        """uses the "lol" api for receiving data about one single post"""
        url = self.apis["lol"].format(id)
        # proceed request, read and decode
        data = self.opener.open(url).read().decode("utf-8", errors="ignore")
        # decode 
        jsondata = json.loads(data)

        post = {
            "lol_id" : [0],
            "lol_time" : jsondata[1],
            "lol_uid" : jsondata[2],
            "lol_username" : jsondata[3],
            "lol_type" : jsondata[4],
            "lol_url" : jsondata[5],
            "lol_title" : jsondata[6],
            "lol_score" : jsondata[7],
            "lol_comments" : jsondata[8],
            "lol_badge" : jsondata[9],
            "lol_likes" : jsondata[10],
            "lol_dislikes" : jsondata[11],
            "lol_repost_of" : jsondata[12],
            "lol_next" : jsondata[13],
            "lol_prev" : jsondata[14],
            "lol_link" : jsondata[15],
            "lol_source" : jsondata[16],
            "lol_deleted" : jsondata[17]
        }
        return post

EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ein paar Dinge könntest du schon verbessern. Ich fange einfach mal von oben nach unten an.

Als erstes solltest du "self.apis" aus der init-Methode herausziehen. Der Werte ist für jede Instanz immer gleich und ändert sich nie, dann gehört er zur Klasse HugelolApi. Füge das einfach als Konstante hinzu:

Code: Alles auswählen

class HugelolApi(object):
APIS = {
            "front" : "http://hugelol.com/api/front.php?after={}",
            "rising" : "http://hugelol.com/api/rising.php?after={}",
            "fresh" : "http://hugelol.com/api/fresh.php?after={}",
            "top" : "http://hugelol.com/api/top.php?after={}",
            "lol" : "http://hugelol.com/api/lol.php?id={}"
        }
...
Dann solltest du dir noch PEP 8 anschauen, damit deine Namen auch Python-Standardkonform sind. Es sollte also besser "last_id", "last_page", etc. heißen.

Zeile 27 kannst du besser als ``api not in HugelolApi.APIS`` ausdrücken, dann ist es ein wenig leserlicher.

Zeilen 39 bis 48 kannst du viel leicher ausdrücken:

Code: Alles auswählen

posts.append(dict(zip(["id", "time", "uid", ...], entry)))
Die ganzen "lol"-Prefixe solltest du einfach weglassen, die bringen keinen extra mehrwert. Alternativ bietet sich hier ein NamedTuple an, dann wird das ganze noch schöner:

Code: Alles auswählen

Post = collections.namedtuple("Post", "id", "time", "uid", ...)
Und die Erzeugung dann mit:

Code: Alles auswählen

posts.append(Post(entry[:9]))
Statt "getNext", solltest du vielleicht besser das __iter__-Interface implementieren oder aber das __next__-Interface.

Zeilen 70 bis 88 kannst du wieder nach dem oben genanntem Schema vereinfachen.
Das Leben ist wie ein Tennisball.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Hier meine Anmerkungen, zusätzlich zu EyDus: Ich sehe den Sinn einer HugololApi-Klasse nicht, da sie nicht wirklich einen Zustand speichert. Die getPosts/getNext-Methoden ließen sich schöner mittels Generator schreiben. Statt dessen würde ich eher eine Post-Klasse schreiben, die bei Bedarf mehr Informationen nachlädt.
pl3ktrum
User
Beiträge: 6
Registriert: Dienstag 18. Juni 2013, 09:27

Danke EyDu und Sirius3.
Ich wusste da kann noch viel verbessert bzw. "pythonischer" umgesetzt werden ;)
Werde mich die Tage nochmal dran geben,
Antworten