Small script to access my Groupees profile and automatically download all my games and music written in python 3.

It depends on my CookieCon module, so just download both files, call python groupees.py and give it your username and password. It will automatically start downloading all of your games.

If you only want some games, have a look at the source code. Editing it for your needs should be easy!

Groupees.py

#!/usr/bin/env python
#Python3 bindings for groupees.com
#to easiely access your profile,
#keys and downloads
__author__ = "Frederik Lauber"
__copyright__ = "Copyright 2014"
__license__ = "GPL3"
__version__ = "0.5"
__maintainer__ = "Frederik Lauber"
__status__ = "Development"
__contact__ = "https://flambda.de/impressum.html"

import os
import re
from string import Template
import urllib.error
from bs4 import BeautifulSoup
import CookieCon
URL_LOGIN = "https://groupees.com/login"
URL_LOGIN_AUTH = "https://groupees.com/auth/email_password"
URL_PROFILE_PAGE = Template("https://groupees.com/users/${user_id}/purchases?page=${page}")
URL_BASE = Template("https://groupees.com${relative}")
REGEX_USER_ID = r"""(?<=users/)\d+(?=/purchases)"""
REGEX_COVER_URL = r"""(?<=^background-image:url\(').*(?='\))"""

class _groupees_base_exception(Exception):
    pass

class _url_exception(_groupees_base_exception):
    def __init__(self, url):
        self.url = url
    def __str__(self):
        return "".join([str(self.__class__.__name__), "\nUrl: ", self.url])
class MultipleExceptions(_groupees_base_exception):
    def __init__(self, exceptions):
        self.exceptions = exceptions
    def __str__(self):
        tmp = []
        for e in self.exceptions:
            tmp.append(str(e))
        return "\n".join(tmp)
class NeitherLinkNoKey(_groupees_base_exception):
    def __str__(self):
        return str(self.__class__.__name__)
class ToSmallFile(_url_exception):
    pass
class LinkNotReachable(_url_exception):
    pass
class NoCover(_groupees_base_exception):
    pass
class NoLink(_groupees_base_exception):
    pass
class NoKey(_groupees_base_exception):
    pass
class LoginFailed(_groupees_base_exception):
    pass

class product(object):
    @property
    def name(self):
        #Get the name of the product
        if not hasattr(self, "_name"):
            self._name = self._soup.find("div", {"class" : "product"}).get_text().strip()
        return self._name

    @property
    def cover_url(self):
        #Get the relative path of the cover. Raises NoCover if no cover exists.
        if not hasattr(self, "_cover_url"):
            cover_url_style = self._soup.find("div", {"class" : "cover"}).get("style")
            cover_url_regexed = re.search(REGEX_COVER_URL, str(cover_url_style))
            self._cover_url = None if cover_url_regexed is None else cover_url_regexed.group(0)
        if self._cover_url is None:
            raise NoCover
        else:
            return self._cover_url

    def download_cover(self, folder, filename = None):
        self._con.urlretrieve(URL_BASE.substitute(relative = self.cover_url), folder, filename)

    @property
    def link_urls(self):
        if not hasattr(self, "_link_urls"):
            tmp = dict()
            for a in self._soup.find_all('a'):
                tmp[a.string] = a.get('href')
            self._link_urls = tmp if len(tmp) else None
        if self._link_urls is None:
            raise NoLink
        else:
            return self._link_urls

    def download_file(self, platform, folder, filename = None):
        self._con.urlretrieve(URL_BASE.substitute(relative = self.link_urls[platform]), folder, filename)

    @property
    def keys(self):
        if not hasattr(self, "_keys"):
            tmp = dict()
            for li in self._soup.find_all('li'):
                platform, key = li.find_all('span')
                tmp[platform.string] = key.string
            self._keys = tmp if len(tmp) else None
        if self._keys is None:
            raise NoKey
        else:
            return self._keys

    def __init__(self, con, soup):
        self._soup = soup
        self._con = con

    def auto_download(self, folder):
        path = os.path.join(folder, self.name.replace(":", ""))
        if not os.path.exists(path): os.makedirs(path)
        try:
            self.download_cover(path)
        except Exception:
            pass
        for platform in self.link_urls:
            try:
                self.download_file(platform, path)
            except Exception as E:
                print(E)

    def test(self, filesize_limit = 180):
        #Groupees has still a lot of defect profiles.
        #This functions tries to detect whose. 
        #The first criteria is:
        #   1. Every product needs at least one key or one link_url
        #If a link_url exists, headers are checked. If the file is smaller then
        #filesize_limit in kb, it will be regarded as defect.
        num = 0
        try:
            num += len(self.keys)
        except NoKey:
            pass
        try:
            num += len(self.link_urls)
        except NoLink:
            pass
        if not num:
            raise NeitherLinkNoKey
        #check if links have a bigger size than 180kb if they exist
        exception_list = []
        try:
            for platform in self.link_urls:
                url = URL_BASE.substitute(relative = self.link_urls[platform])
                try:
                    (filename, filesize) = self._con.urlgetfileinfo(url)
                    if filesize <= filesize_limit:
                        exception_list.append(ToSmallFile(url))
                except urllib.error.HTTPError:
                    exception_list.append(LinkNotReachable(url))
        except NoLink:
            pass
        else:
            if len(exception_list) == 1:
                raise exception_list[0]
            elif len(exception_list) > 1:
                raise MultipleExceptions(exception_list)

def _get_auth_and_userid(username, password):
    connector = CookieCon.CookieCon(userAgent="Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0")
    #as of 15.01.2014, groupees refuses auth if no userAgent is given
    data_for_user_id = connector.request(URL_LOGIN_AUTH, {'identifier': username, 'password': password})
    try:
        userid = re.search(REGEX_USER_ID, data_for_user_id).group(0)
    except AttributeError:
        raise LoginFailed
    return (connector, userid)

def collect_products(username, password):
    #Takes your user name and password and will return a list of groupees_products. Each groupees_product represents one 
    #item on your profile like a cd or a game. It can have multiple keys for different platforms and can have multiple downloadable
    #file. Please keep in mind that it does not need to have any at all. A NoKey, NoLink or NoCover Exception will be raised once
    #you try to access it.
    (connector, userid) = _get_auth_and_userid(username, password)
    products = []
    i = 0
    last_request = ""
    while not "No purchased Groupees yet" in last_request:
        last_request = connector.request(URL_PROFILE_PAGE.substitute(user_id = userid, page = str(i)))
        soup = BeautifulSoup(last_request)
        for div in soup.findAll("div", {"class" : "product-box"}):
            products.append(product(connector, div))
        i += 1
    return products

def download_all_by_platform(product_list, platform, folder):
    for prod in product_list:
        try:
            prod.download_file(platform, folder)
        except groupees_product.NoLink:
            pass

def download_all_inverted(product_list, platform_list, folder):
    for prod in product_list:
        try:
            for platform in prod.link_urls:
                if platform in platform_list:
                    pass
                else:
                    try:
                        prod.download_file(platform, folder)
                    except KeyError:
                        pass
        except NoLink:
            pass

def create_report(product_list):
    line_list = []
    for i in product_list:
        try:
            i.test()
        except Exception as E:
            line_list.append(i.name)
            line_list.append(str(E))
    return "\n".join(line_list)

if __name__ == "__main__":
    import groupees
    print("Start")
    un = input("Please, type your email address\n")
    pw = input("Please, type your password\n")
    print("Now trying to accumulate all your products on groupees")
    try:
        p = groupees.collect_products(un, pw)
    except groupees.LoginFailed:
        print("Login Failed")
        print("Check email and password")
        exit(1)
    print("All products found")
    download_folder = input("Give folder for auto download (default is the current folder)\n")
    if download_folder == "":
        download_folder = "./"
    print("Starting to download")
    for i in p:
        print("Product:")
        print(i.name)
        try:
            i.auto_download(download_folder)
        except Exception as E:
            print(i.name)
            print(E)
    print("Stop")

Published

Category

snippets

Tags