Back to Home

We are writing a module for authorization in VK API

vkontakte api · vkontakte api · python

We are writing a module for authorization in VK API

The other day there was a need to save all the photos from your VKontakte photo album to your hard drive. Naturally, the option of saving photos one at a time did not suit me. Then I remembered that VKontakte has an API . Five minutes of reading manuals, and all the necessary functions are found. The only problem is that there is no normal way to access the API. The documentation says the following:
The application authorization process consists of 3 steps:
  1. Opening a browser window for user authentication on the VKontakte website.
  2. User permission to access their data.
  3. Passing the access_token key to the application to access the API.


At first glance, writing a simple portable script will fail. Although, what prevents us from pretending to be a browser?

To achieve our goals, we will use only standard Python modules:
  1. urllib2
  2. cookielib
  3. HTMLParser

Create opener

In order to pretend to be a real browser, just loading the necessary pages is not enough. At a minimum, you need to correctly handle cookies and redirects. To do this, create an opener that will do all the work for us:
opener = urllib2.build_opener(
        urllib2.HTTPCookieProcessor(cookielib.CookieJar()),
        urllib2.HTTPRedirectHandler())

We turn to the authorization page

response = opener.open(
        "http://oauth.vk.com/oauth/authorize?" + \
        "redirect_uri=http://oauth.vk.com/blank.html&response_type=token&" + \
        "client_id=%s&scope=%s&display=wap" % (client_id, ",".join(scope))
        )

Details about the treatment parameters can be found in the documentation . It should be noted here that we will use a parameter displaywith a value wap, because there is practically no javascript in this version of the page. scopeis a list of rights names that we want to access.

Parsim answer

Let's look at the authorization page code. We will be most interested in the following section:
Телефон или e-mail:

Пароль:

For the subsequent submission of the form, it is necessary to parse all the input-s (including hidden), as well as the url to which the form is submitted. We write a simple parser based on HTMLParser:
class FormParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.url = None
        self.params = {}
        self.in_form = False
        self.form_parsed = False
        self.method = "GET"
    def handle_starttag(self, tag, attrs):
        tag = tag.lower()
        if tag == "form":
            if self.form_parsed:
                raise RuntimeError("Second form on page")
            if self.in_form:
                raise RuntimeError("Already in form")
            self.in_form = True 
        if not self.in_form:
            return
        attrs = dict((name.lower(), value) for name, value in attrs)
        if tag == "form":
            self.url = attrs["action"] 
            if "method" in attrs:
                self.method = attrs["method"]
        elif tag == "input" and "type" in attrs and "name" in attrs:
            if attrs["type"] in ["hidden", "text", "password"]:
                self.params[attrs["name"]] = attrs["value"] if "value" in attrs else ""
    def handle_endtag(self, tag):
        tag = tag.lower()
        if tag == "form":
            if not self.in_form:
                raise RuntimeError("Unexpected end of 
") self.in_form = False self.form_parsed = True

Log in

Substitute the email and password of the user in the request parameters and submit the form:
parser.params["email"] = email
parser.params["pass"] = password
response = opener.open(parser.url, urllib.urlencode(parser.params))

Allow access

The next step, if the user has not done this yet, we need to give the application the rights that we requested in the scope parameter. To do this, we will be offered a page with a form on which there will be Allow and Deny buttons. Parsim and submit the form using the method described above.

Get token and user_id

If we did everything right, then we will eventually be redirected to a page with a url of the form
http://oauth.vk.com/blank.html#access_token= 533bacf01e11f55b536a565b57531ad114461ae8736d6506a3 & expires_in = 86400 & user_id = 8492

where with simple manipulations we can get the ones we need access_tokenand user_id.

Module usage example

Actually, for the sake of which everything was started - a script for downloading the album:
import vk_auth
import json
import urllib2
from urllib import urlencode
import json
import os
import os.path
import getpass
import sys
def call_api(method, params, token):
    if isinstance(params, list):
        params_list = [kv for kv in params]
    elif isinstance(params, dict):
        params_list = params.items()
    else:
        params_list = [params]
    params_list.append(("access_token", token))
    url = "https://api.vk.com/method/%s?%s" % (method, urlencode(params_list)) 
    return json.loads(urllib2.urlopen(url).read())["response"]
def get_albums(user_id, token):
    return call_api("photos.getAlbums", ("uid", user_id), token)
def get_photos_urls(user_id, album_id, token):
    photos_list = call_api("photos.get", [("uid", user_id), ("aid", album_id)], token)
    result = []
    for photo in photos_list:
        #Choose photo with largest resolution
        if "src_xxbig" in photo:
            url = photo["src_xxbig"]
        elif "src_xbig" in photo:
            url = photo["src_xbig"]
        else:
            url = photo["src_big"]
        result.append(url)
    return result
def save_photos(urls, directory):
    if not os.path.exists(directory):
        os.mkdir(directory)
    names_pattern = "%%0%dd.jpg" % len(str(len(urls)))
    for num, url in enumerate(urls):
        filename = os.path.join(directory, names_pattern % (num + 1))
        print "Downloading %s" % filename
        open(filename, "w").write(urllib2.urlopen(url).read())
if len(sys.argv) != 2:
   print "Usage: %s destination" % sys.argv[0]
   sys.exit(1)
directory = sys.argv[1]
email = raw_input("Email: ")
password = getpass.getpass()
token, user_id = vk_auth.auth(email, password, "2951857", "photos")
albums = get_albums(user_id, token)
print "\n".join("%d. %s" % (num + 1, album["title"]) for num, album in enumerate(albums))
choise = -1
while choise not in xrange(len(albums)):
    choise = int(raw_input("Choose album number: ")) - 1
photos_urls = get_photos_urls(user_id, albums[choise]["aid"], token)
save_photos(photos_urls, directory)

and how it looks in work:


Conclusion

Of course, this module is not suitable for use in serious projects, but for personal purposes - completely.
Link to GitHub.

Read Next