I've been thinking for a while now about how nice it would be to automatically create a Markdown post of the Instagram photos I take. I looked around at Instagram's API's and, while I could probably set up a way to pull directly from the API, I wanted to throw something together quickly. I'm on my way to Maui for vacation, so I spent an evening making this handy little Python script that'll enable me to easily post my Instagram photos to jayhickey.com.

importosimportsysimportrefromglobimportglobimporturllibfromtimeimportlocaltime,strftimedefread_file(file):# Check for filename for Instagram file from IFTTTf=open("%s"%(files),mode="r")fileLines=f.readlines()fileDict={}# Create a dictionary with the Instagram infoforlineinfileLines:x=re.search(r'([\w\@\.]+)\s*:\s*(.*)',line)ifx!=None:fileDict[x.group(1)]=x.group(2)f.close()returnfileDictdefcreate_draft(fileDict,draftLoc,imgLink):# Replace non alphanumerics with dashes for filenamec=re.sub(r'[\t !"#$:;%&\'()*\-/<=>?@\[\\\]^_`{|},.]+',"-",fileDict['Caption'])whilec.endswith('-'):c=c[:-1]c=c.lower()# Embed the photo with Markdowndraft=open(draftLoc+"/%s.md"%(c),mode="w")draft.write(fileDict['Caption']+'\n')draft.write("=====================\n")draft.write("Link: %s"%(fileDict['URL'])+"\n")draft.write("publish-not-yet\n\n")printimgLinkdraft.write("![%(1)s](%(2)s)\n\n"%{"1":fileDict['Caption'],"2":imgLink})draft.write("(Via [Instagram](http://instagram.com))")draft.close()if__name__=='__main__':# These might not be used, so make them emptyLocal_Image_URL_Path=''Website=''# Read input argumentsIFTTT_Read_Path=sys.argv[1]Draft_Write_Path=sys.argv[2]# These parameters are optionaliflen(sys.argv)>=4:Local_Image_URL_Path=sys.argv[3]iflen(sys.argv)==5:Website=sys.argv[4]# Make sure the file is a text file from InstagramfileList=glob(IFTTT_Read_Path+'*instagr.am*.txt')forfilesinfileList:# Read the Instagram datafileDict=read_file(files)ifLocal_Image_URL_Path!=''orWebsite!='':# Make a local copy of the image and date itimage=urllib.URLopener()eventTime=strftime("%Y-%m-%d_%H%M%S",localtime())fileName,fileExtension=os.path.splitext(fileDict['Source'])imgLinkPath=IFTTT_Read_Path+eventTime+fileExtensionimage.retrieve(fileDict['Source'],imgLinkPath)imgURL=Website+Local_Image_URL_Path+eventTime+fileExtensionelse:# Use the image hosted by InstagramimgURL=fileDict['Source']# Create a Markdown draftcreate_draft(fileDict,Draft_Write_Path,imgURL)# Delete the Instagram text file from IFTTTos.remove(files)

The downside to using IFTTT, according to their hilariously titled /wtf page, is that it only polls for new data every 15 minutes. So this won't happen instantly.2

GitHub is where you can find all the instructions for setting up the IFTTT recipe, running Markdownify Instagram, and even how to use a shell script and iNotify to automate the process. I won't go into any of that here. However, I do think it's interesting to dive a little deeper and see how the script works.

Implementation

Here's a look at a few of the design decisions I made while writing Markdownify Instagram.

Reading the text file from IFTTT

When triggered, the IFTTT recipe will create a plain text file with a name like http-instagr.ampoefb-ihvv0.txt and these contents:

Python's built in glob module is first used to create a list of all the Instagram text files in your {{IFTTT_Read_Path}}:

fileList=glob.glob(IFTTT_Read_Path+'*instagr.am*.txt')

The * will match zero or more characters, so a file that starts with anything, contains instagr.am, ends with .txt, and has anything in between will be appended to fileList. This is the first time I've actually used the glob module. Although less powerful than regular expressions, it's perfect for finding specific files in directories.

Creating a dictionary from the data

After getting a list of files, read_file uses readlines() to split the text into individual lines. The regex pattern r'([\w\@\.]+)\s*:\s*(.*)' creates matchobject's out of those lines, then lastly the dictionary fileDict is created from the matches:

Regex is hard to look at, but it's not too bad once you grasp the syntax. Google's regular expressions tutorial is a good place to start, and you can learn a whole lot more just by searching the web. Each set of parenthesis corresponds to a group match that will be stored in an additional element of x. Because there are two sets of parenthesis, x will have a group size of 3. x.group(0) will contain all of line, x.group(1) will contain the text before the : (the first set of parenthesis in the regex), and x.group(2) will have everything after (the second set). Like this:

Saving the local image file

If the last two parameters are entered, your photo will be saved locally—so you aren't relying on Instagram's S3 hosting. If Facebook ever decides to shut down Instagram, you won't have a broken embedded image. To accomplish this, I used the Python urllib module to save an image from a URL by setting image = urllib.URLopener(), then

image.retrieve(fileDict['Source'],imgLinkPath)

where fileDict['Source'] is the link to the Instagram image and imgLinkPath is where the image saved will be saved, e.g., /home/blog/secondcrack/www/media/instagram/2012-11-30_062759.jpg

The local image is named using strftime("%Y-%m-%d_%H%M%S", localtime()). This creates a string formatted something like 2012-11-30_062759, where:

%Y is the year with century as a decimal (use %y for without century, resulting in 12 instead of 2012)

I've been using this convention ever since Gabe over at MacDrifter recommended it. It's very easy to both sort and quickly look at. I love it so much I even set this as a TextExpander shortcut for naming files and photos on my Mac:

Creating the Markdown draft

The final function, create_draft, is pretty straightforward and will create a Markdown file that looks like this:

Conclusion

Overall, I'm pretty happy with the way Markdownify Instagram turned out. It produces a nice, simple post that's easy to modify.3 There's also tons of possiblities for new features and expansions. I find it extremely useful, so get prepared for an influx of pictures posted here. Aloha!

Update: I've made a small addition to the script's create_draft function. Non-alphanumerics are now replaced with dashes in the filename. So any spaces or special characters will be turned into dashes for the .md slug.

I used a little more regex magic to make the substitution. Here's what it looks like: