📜 ⬆️ ⬇️

We write the module for authorization in VK API

The other day there was a need to save all the photos from your VK photo album to your hard drive. Naturally, the option, with the preservation of photos one by one, 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 states the following:
The application authorization process consists of 3 steps:
  1. Opening a browser window for user authentication on the site VKontakte.
  2. User permission to access their data.
  3. Passing the access_token key to the API to the application.


At first glance, jot out a simple portable script does not work. 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 an opener

In order to pretend to be a real browser, simply 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 appeal to the login 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 options can be found in the documentation . It should be noted here that we will use the display parameter with the value wap , since In this version of the page there is practically no javascript. scope is a list of rights names that we want to access.

Parsim answer

Let's look at the authorization page code. Most of all we will be interested in the following section:
 <form method="POST" action="https://login.vk.com/?act=login&soft=1&utf8=1"> <input type="hidden" name="q" value="1"> <input type="hidden" name="from_host" value="oauth.vk.com"> <input type="hidden" name="from_protocol" value="http"> <input type="hidden" name="ip_h" value="df5a3639f3cb32ecc1" /> <input type="hidden" name="to" value="aHR0cDovL29hdXRoLnZrLmNvbS9vYXV0aC9hdXRob3JpemU/Y2xpZW50X2lkPTI5NTE4NTcmcmVkaXJlY3RfdXJpPWJsYW5rLmh0bWwmcmVzcG9uc2VfdHlwZT10b2tlbiZzY29wZT00JnN0YXRlPSZkaXNwbGF5PXdhcA--"> <span class="label">  e-mail:</span><br /> <input type="text" name="email"><br /> <span class="label">:</span><br /> <input type="password" name="pass"> <div style="padding: 8px 0px 5px 0px"> <div class="button_yes"> <input type="submit" value="" /> </div> <a class="button_no" href="https://oauth.vk.com/grant_access?hash=95a8fc64a19d011436&client_id=2951857&settings=4&redirect_uri=blank.html&cancel=1&state=&token_type=0"> <div>  </div> </a> </form> = "to" value = "aHR0cDovL29hdXRoLnZrLmNvbS9vYXV0aC9hdXRob3JpemU / Y2xpZW50X2lkPTI5NTE4NTcmcmVkaXJlY3RfdXJpPWJsYW5rLmh0bWwmcmVzcG9uc2VfdHlwZT10b2tlbiZzY29wZT00JnN0YXRlPSZkaXNwbGF5PXdhcA -"> <form method="POST" action="https://login.vk.com/?act=login&soft=1&utf8=1"> <input type="hidden" name="q" value="1"> <input type="hidden" name="from_host" value="oauth.vk.com"> <input type="hidden" name="from_protocol" value="http"> <input type="hidden" name="ip_h" value="df5a3639f3cb32ecc1" /> <input type="hidden" name="to" value="aHR0cDovL29hdXRoLnZrLmNvbS9vYXV0aC9hdXRob3JpemU/Y2xpZW50X2lkPTI5NTE4NTcmcmVkaXJlY3RfdXJpPWJsYW5rLmh0bWwmcmVzcG9uc2VfdHlwZT10b2tlbiZzY29wZT00JnN0YXRlPSZkaXNwbGF5PXdhcA--"> <span class="label">  e-mail:</span><br /> <input type="text" name="email"><br /> <span class="label">:</span><br /> <input type="password" name="pass"> <div style="padding: 8px 0px 5px 0px"> <div class="button_yes"> <input type="submit" value="" /> </div> <a class="button_no" href="https://oauth.vk.com/grant_access?hash=95a8fc64a19d011436&client_id=2951857&settings=4&redirect_uri=blank.html&cancel=1&state=&token_type=0"> <div>  </div> </a> </form> 

For the subsequent form submission, it is necessary to parse all input-s (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 <form>") self.in_form = False self.form_parsed = True 

Log in

Substitute in the request parameters email and user password and send 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 will be the Allow and Deny buttons. Parsim and send the form using the method described above.

We get token and user_id

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

from where simple manipulations can get us the access_token and user_id we 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.

Source: https://habr.com/ru/post/143972/


All Articles