From Q to compiler in less than 30 seconds (See the newer version here:  CodeReview question markdown downloader) 



As an adjunct to From new Q to compiler in 30 seconds, I've created a Python script to automatically download the markdown from any question on Code Review and save it to a local file using Unix-style line endings.

For instance, to fetch the markdown for this question, one could write:

python fetchQ.py 124479 fetchquestion.md


I'm interested in a general review including style, error handling or any other thing that could be improved.

fetchQ.py

""" Code Review question fetcher.  Given the number of the question, uses
the StackExchange API version 2.2 to fetch the markdown of the question and
write it to a local file with the name given as the second argument. """
import sys
import urllib
import StringIO
import gzip
import json
import HTMLParser

def make_URL(qnumber):
    return 'https://api.stackexchange.com/2.2/questions/'+str(qnumber)+'/?order=desc&amp;sort=activity&amp;site=codereview&amp;filter=!)5IYc5cM9scVj-ftqnOnMD(3TmXe'

def fetch_compressed_data(url):
    compressed = urllib.urlopen(url).read()
    stream = StringIO.StringIO(compressed)
    data = gzip.GzipFile(fileobj=stream).read()
    return data    

def fetch_question_markdown(qnumber):        
    url = make_URL(qnumber)
    try: 
        data = fetch_compressed_data(url)
    except IOError as (err):
        print "Error: {0}: while fetching data from {1}".format(err, url)
        sys.exit()
    try:
        m = json.loads(data)
    except ValueError as (err):
        print "Error: {0}".format(err)
        sys.exit()
    try:
        body = m['items'][0]['body_markdown']
    except KeyError:
        print "Error: item list was empty; bad question number?"
        sys.exit()
    except IndexError:
        print "Error: response does not contain markdown; bad question number?"
        sys.exit()
    h = HTMLParser.HTMLParser()
    md = h.unescape(body)
    return md

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print('Usage: fetchQ questionnumber mdfilename')
        sys.exit()
    qnumber, qname = sys.argv[1:3]

    md = fetch_question_markdown(qnumber)
    with open(qname, 'wb') as f:
            f.write(md.encode('utf-8').replace('\r\n','\n'))


Note: This code and its companion C++ project are now available in a github repo.
I have a couple of suggestions which are not specifically about the code, but rather about the user experience.

The first thing I did was copy-paste the script to a file, make it executable and tried to run it.

$ chmod +x fetchQ.py
$ ./fetchQ.py 124307 autoproject
./fetchQ.py: line 5: $' Code Review question fetcher.  Given the number of the question, uses\nthe StackExchange API version 2.2 to fetch the markdown of the question and\nwrite it to a local file with the name given as the second argument. ': command not found
... etc ...


As you understand, I just assumed the script had a shebang line and instead of using python, Bash tried to interpret the script itself. I think other users might have the same expectations and that you should therefore begin your script with the line #!/usr/bin/env python.

Now, you might have noticed, I accidentally left out the .md extension for the file name. Because the primary use-case for this program is to set up a file for AutoProject, which requires files to have that extension, I think it should automatically be added if no other extension is provided.

Finally, the other argument to the program is the question id. Instead of making users extract the id themselves from the url, I think the user should have the option to pass the full link directly because just pressing CtrlL; CtrlC is easier than by hand selecting the id portion.