#!/usr/bin/env python2.5# browse XKCD contentsimport urllibclass XkcdImg(object): base_site = r'http://%sxkcd.com/' def __init__(self, base=None): """ XKCD Images informations fetcher works by downloading pages and parsing data base is: None (default) for random '' for Latest <int> for the corresponding episode Note: an empty-string base will fetch the latest Image """ # Most of the magic here try to get a clean name and a correct uri if base is None: # Dynamic Random image base = 'comic/random' prefix = 'dynamic.' self.name = 'Random Episode' self._number = None else: # str(int) or empty string if isinstance(base, int): base = str(base) # We deal with strings self._number = base else: self._number = None prefix = '' self.name = 'Episode %s'%base if base else 'Latest' uri = self.base_site%prefix + base if uri[-1] != '/': uri += '/' # Ensure trailing slash self.uri = uri self._pic_uri = None def __repr__(self): return '<%s %s>'%(self.name, self.__class__.__name__) def _get_data(self): """ Get the corresponding website data """ site = urllib.urlopen(self.uri) return site.read() def _find_prefixed_uri(self, prefix, lookup_size=500): """ Try to find an uri next prefix lookup_size: Size of the superstring used for lookup prefix: "Magic" pattern that should be at the left of the uri """ data = self._get_data() idx = data.index(prefix) + len(prefix) # Find start of interesting data substring = data[idx:idx+lookup_size] # average matching # return the stripped-down version (supposed interesting content) return substring[substring.index('http'):substring.index('</')] def get_number(self): """ Get the effective number of this comic """ if self._number is None: full_uri = self._find_prefixed_uri('Permanent link to this comic: ') for subpart in reversed(full_uri.split('/')): if subpart.isdigit(): self._number = int(subpart) break assert self._number is not None return self._number def get_uri(self, force_refresh=False): if self._pic_uri is None or force_refresh: self._pic_uri = self._find_prefixed_uri('Image URL (for hotlinking/embedding):') return self._pic_uriclass XkcdBrowser(object): """ Class showing XkcdImg use """ _Max = None # Maximum episode at time of instanciation def __init__(self): if self._Max is None: latest = XkcdImg('') XkcdBrowser._Max = latest.get_number() def browse(self): """ Return a list of all pictures from XKCD The first element is a magic Random picture """ ret = [XkcdImg()] ret.extend(XkcdImg(str(n)) for n in xrange(1, self._Max+1)) return retdef test(): def pprint(i, **kw): """ Eyecandy get_uri """ print i, '=>', i.get_uri(**kw) xkcd = XkcdBrowser() x = xkcd.browse()[1] pprint(x) pprint(xkcd.browse()[-1]) rand_img = xkcd.browse()[0] print "No reload: all the same.." assert reduce(lambda x, y: x if x == y else None, (rand_img.get_uri() for x in xrange(10))) is not None print "assert OK!" for n in xrange(10): pprint(rand_img) print "reload: mostly differents.." # They are all different (allow 1 dup for unlucky ppl) assert len(set((rand_img.get_uri(force_refresh=True) for x in xrange(10)))) >= 9 print "assert OK!" for n in xrange(10): pprint(rand_img, force_refresh=True) print "Done."if __name__ == '__main__': test()