Backup Facebook Images using Python

So I’ve been thinking about limiting my Facebook usage, or completely shutting it down. However, having quite a few images on the site I wanted to first backup what I may not have locally.

Now Facebook offers a “Download a copy of your Facebook data“, but this did not seem to work for me when attempting (continued to get 404 HTTP responses).

So a little bit of digging and I found Facebook provides a pretty nice explorer,
but better yet I can use the explorer to help craft RESTful calls.

And here you have it, a hacky Python (2.x) script to do the deed of downloading all
your uploaded photos.

from urllib import urlopen
from json import loads
from sys import argv
import dateutil.parser as dateparser
import logging

def get_logger(label='lvm_cli', level='INFO'):
    """
    Return a generic logger.
    """
    format = '%(asctime)s - %(levelname)s - %(message)s'
    logging.basicConfig(format=format)
    logger = logging.getLogger(label)
    logger.setLevel(getattr(logging, level))
    return logger

def urlrequest(url):
    """
    Make a url request
    """
    req = urlopen(url)
    return req.read()

def get_json(url):
    """
    Make a url request and return as a JSON object
    """
    res = urlrequest(url)
    return loads(res)

def get_next(data):
    """
    Get next element from facebook JSON response,
    or return None if no next present.
    """
    try:
        return data['paging']['next']
    except KeyError:
        return None

def get_images(data):
    """
    Get all images from facebook JSON response,
    or return None if no data present.
    """
    try:
        return data['data']
    except KeyError:
        return []

def get_all_images(url):
    """
    Get all images using recursion.
    """    
    data = get_json(url)
    images = get_images(data)
    next = get_next(data)

    if not next:
        return images
    else:
        return images + get_all_images(next)

def get_url(userid, access_token):
    """
    Generates a useable facebook graph API url
    """
    root = 'https://graph.facebook.com/'
    endpoint = '%s/photos?type=uploaded&fields=source,updated_time&access_token=%s' % \
                (userid, access_token)
    return '%s%s' % (root, endpoint)

def download_file(url, filename):
    """
    Write image to a file.
    """
    data = urlrequest(url)
    f = open(filename, 'w')
    f.write(data)
    f.close()

def create_time_stamp(timestring):
    """
    Creates a pretty string from time
    """
    date = dateparser.parse(timestring)
    return date.strftime('%Y-%m-%d-%H:%M:%S')

def download(userid, access_token):
    """
    Download all images to current directory.
    """
    logger = get_logger()
    url = get_url(userid, access_token)
    logger.info('Requesting image direct link, please wait..')
    images = get_all_images(url)

    for image in images:
        logger.info('Downloading %s' % image['source'])
        filename = '%s.jpg' % create_time_stamp(image['created_time'])
        download_file(image['source'], filename)

Usage looks something like this while inside a Python shell:

In [1]: download(USER_NAME, SECRET_TOKEN)
2014-01-14 13:40:19,891 - INFO - Requesting image direct link, please wait..
2014-01-14 13:40:37,352 - INFO - Downloading https://scontent-b.xx.fbcdn.net/?????_n.jpg

I take no responsibility if the above code does not work, this was merely a bit of hacking to achieve my goal, though I figured other may benefit from some of the process I took.

I would also like to note, you can hit these RESTful APIs directly, it merely returns JSON data.
Go ahead and give it a try, remember to plugin your username and access_token (Token can be get and modified in the Explorer‘s Get Access Token button):

https://graph.facebook.com/USER_NAME/photos?type=uploaded&fields=source&access_token=ACCESS_TOKEN_HERE

 

Humble Bundle Store and Bitcoin!

Realized the other day The Humble Bundle accepts Bitcoin! This is fantastic as a couple of my other post talk about mining Bitcoins.

One of the first games I purchased with Bitcoin was DLC Quest, it only cost me about $0.75 in Bitcoin, and was well worth the time it took to mine said coins.

images

It only took me about 2 days to complete the game, but it is hilarious.

Also worked with my XBOX 360 controller!

If you are still unaware of what Bitcoin is, I suggest having a peak at Try Bitcoin, you will end up with a wallet, some coin and a good explanation.

Happy mining!

Looking in to Bitcoin Address

I’ve been looking in to Bitcoin addresses a bit, and to better understand I’m looking to create offline wallet addresses using Python.

According to the Bitcoin Wiki, the first think I needed is a Private key, the simplest and most readable way I can think to generate a random 256-bit number (Bitcoin specs) in Python is like this:

In [1]: import random

In [2]: mykey = random.getrandbits(256)

In [3]: mykey
Out[3]: 36701660163838632464800851613895428671125029112948002597567369254337753327982L

In [4]: hex(mykey)
Out[4]: '0x5124674c33955a4ed7786396e53b3b3b6151f3c8806fb49f1fac97896d2b296eL'

And that is that, a way to generate a 256-bit number and represent it as int and hex.

Here is the output from pycoin:

$ bu 36701660163838632464800851613895428671125029112948002597567369254337753327982
secret exponent: 36701660163838632464800851613895428671125029112948002597567369254337753327982
  hex:           5124674c33955a4ed7786396e53b3b3b6151f3c8806fb49f1fac97896d2b296e
WIF:             KywSWpsVmrv2oYty3zLNgoFXu6CBY2dwFmQz28CUYuUdJtuB3xJB
  uncompressed:  5JS2FThtqE82mXnkxxaqsFZHyD3zDU76x16S9s7gKZHEDJLCUf6
public pair x:   85570294591966495791808404640105384345946951133990589858581492914720116747518
public pair y:   43020695826980084121062383274092737492069066203032384259538697939351904697945
  x as hex:      bd2f12062aa4256f5e4c97636fa8494b49594c4460730014999970f87452acfe
  y as hex:      5f1cd9d7aed0b4c54d2b1f6a019ffcdcfe1e377274ba5060cfd8e795cd13be59
y parity:        odd
key pair as sec: 03bd2f12062aa4256f5e4c97636fa8494b49594c4460730014999970f87452acfe
  uncompressed:  04bd2f12062aa4256f5e4c97636fa8494b49594c4460730014999970f87452acfe\
                   5f1cd9d7aed0b4c54d2b1f6a019ffcdcfe1e377274ba5060cfd8e795cd13be59
hash160:         6e4512e09c5ac49ccb4caf3f9e534ecf57255eba
  uncompressed:  bbc11fac2f16292de330844c3f1eefd6ad88992f
Bitcoin address: 1B445RsuKVQbiqVc99AL7fbf92BTK4XhPv
  uncompressed:  1J7kiLw785VQzMBWi7rw1vjhN8QhhTafFr