Seite 1 von 1

Hugelol client für die Hugelol API

Verfasst: Montag 16. Juni 2014, 16:23
von pl3ktrum
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


Re: Hugelol client für die Hugelol API

Verfasst: Montag 16. Juni 2014, 18:04
von EyDu
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.

Re: Hugelol client für die Hugelol API

Verfasst: Montag 16. Juni 2014, 18:12
von Sirius3
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.

Re: Hugelol client für die Hugelol API

Verfasst: Montag 16. Juni 2014, 22:05
von pl3ktrum
Danke EyDu und Sirius3.
Ich wusste da kann noch viel verbessert bzw. "pythonischer" umgesetzt werden ;)
Werde mich die Tage nochmal dran geben,