Creating .exe file for authorization and interaction with VK API using Python

In this article, we will consider a way to create an .exe file that allows you to go through VK authorization and fulfill certain requests to the VK API. Python installation is not required.

System requirements



Background and, in fact, why did I need it


About a year ago, I came across a public engaged in translating foreign articles into Russian.

After some time, the idea came up to create a page on which all-all-all translations will be categorized. And, of course, this page will need to be updated occasionally, i.e. collect information about already released materials, their categories, etc., and then lay them on the shelves. In general, VK API allows you to do this.

But that’s not all: every participant with editorial rights should also be able to update all pages. The way to "get these humanities of all editors to install python for themselves" obviously does not work. You just need to - click, click, start, earn, close. Simply put, you need a .exe file.

Moreover, if you add code that interacts with a specific user to the file body, you can create, for example, a universal audio sorter .

Perhaps the method outlined here is another invented bicycle, but still I will continue.

Step zero. Training


In the folder where our future files will be stored, create a subfolder (for example, bin), where we save chromedriver.exe, cacert.pem and (if you really want to) an icon for our future .exe file. In the same folder we place the empty text file path_to_chrome.txt for now .

Step one. Login


So, everything is ready. Once again about how the process of authorization and obtaining the access key access_token for working with the API occurs .

  1. The user follows the link, the application requests access
  2. User Grants Access
  3. A redirect to a  blank page occurs: access_token appears in the address bar

How the link is formed is described in the official documentation .

We import everything we need, declare constants:

import os
import sys
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import vk
from script import PagesUpdater
SCOPE = 'pages'  # vk.com/dev/permissions
OAUTH = 'https://oauth.vk.com/authorize?client_id=4836475&scope={}\
&redirect_uri=https://oauth.vk.com/blank.html&display=popup&v=5.37\
&response_type=token'.format(SCOPE)
IDLE = 3
TOKEN = 'access_token='  # после этого следует сам access_token
ATOKEN_LEN = 85          # и его длина — 85 символов
CHROME_DRIVER = '\chromedriver.exe'
PATH = os.getcwd()
PATH_TO_CHROME_TXT = PATH + '\path_to_chrome.txt'
class Executor(object):
    def launch(self):
        chrome_path = self.get_chrome_path()
        self.driver = webdriver.Chrome(chrome_path)  # запуск selenium
        access_token = self.get_access_token()
        vkapi = vk.API(access_token=access_token)    # запуск API
        script = PagesUpdater(vkapi, vk.api.VkAPIMethodError, self.driver)
        script.launch()
        self.driver.quit()

About  PagesUpdater and referred to it the arguments will be discussed later.

Selenium requires you to specify the full path to the chromedriver.exe file, which should be in the same folder with the browser. The browser path is probably different on each computer, so we’ll put it in a separate text file path_to_chrome.txt with the following contents:

C:\chrome-win32\chrome-win32

    def get_chrome_path(self):
        with open(PATH_TO_CHROME_TXT, 'r') as target:
            return target.read()

Of course, the user must specify the full path to the browser in path_to_chrome.txt in  advance .

We intercept access_token:

    def get_access_token(self):
        self.driver.get(OAUTH)  # открыть ссылку для получения ключа
        access_token = ''
        while not access_token:
            page_url = self.driver.current_url
            if TOKEN in page_url:
                token_start = page_url.index(TOKEN) + len(TOKEN)
                access_token = page_url[token_start:token_start+ATOKEN_LEN]
                break
            else:
                time.sleep(IDLE)
        return access_token

Well, finally, looking ahead, I’ll say that after compilation an error occurs due to the lack of the cacert.pem file . Here's the solution: add cacert.pem to PATH, and then remember to build .exe when building it:

def resource_path(relative):
    return os.path.join(getattr(sys, '_MEIPASS', os.path.abspath(".")),
                        relative)
cert_path = resource_path('cacert.pem')
os.environ['REQUESTS_CA_BUNDLE'] = cert_path

Finishing touch.
exe = Executor()
exe.launch()

All. Move on.

Step Two We turn to VK API


Again, we import everything we need, declare constants:

import requests
import time
LIMIT = 1
CAPTCHA_IMG = 'captcha_img'
CAPTCHA_SID = 'captcha_sid'
class PagesUpdater(object):
    def __init__(self, vkapi, vkerror, driver):
        self.vkapi = vkapi
        self.vkerror = vkerror
        self.driver = driver

self.vkapi will be used to access any VK API methods. Two other arguments will be used to handle Captcha (see below).

Error processing


I came across three errors that were guaranteed to appear with a large number of requests:

  1. VK Captcha Error (no text entered from the Captcha image)
  2. VK Flood Control (too many requests per second)
  3. requests.exceptions.Timeout (appears whenever she wants)

Let's create a method in which we will "wrap" all requests to the VK API:

    def errors(self, vkmethod, **vkkwargs):
        while True:
            try:
                time.sleep(LIMIT)  # LIMIT == 1 just in case
                return vkmethod(**vkkwargs)
            except requests.exceptions.Timeout:
                continue
            break

The second and third errors were sorted out:

  • Before each request there will be a second delay (more about requests in  the documentation ).
  • If requests.exceptions.Timeout occurs, the request will simply be repeated

With Captcha, things are a little more complicated. Read the documentation :
... you should ask the user to enter text from the captcha_img image and repeat the request by adding parameters to it:

  • captcha_sid  - received identifier
  • captcha_key  - text entered by the user

Add an except block in which Captcha will be processed:

            except self.vkerror as e:
                if CAPTCHA_IMG in e.error:
                    self.driver.get(e.error[CAPTCHA_IMG])  # открыть картинку
                    key = raw_input(R)                     # ввести код
                    vkkwargs[captcha_sid] = e.error[CAPTCHA_SID]
                    vkkwargs[captcha_key] = key            # повторить запрос
                    continue
                else:
                    raise  # если же ошибка не в Captcha

We sorted out the errors. Move on.

API request


Add a simple method that returns a list of wiki pages in a group:

    def get_titles(self, gid=NGID):
        return self.errors(self.vkapi.pages.getTitles,
                           group_id=gid)

And the launch method , in which everything else will happen:

    def launch(self):
        print self.get_titles()
        # do something else

That's all.

Compilation in .exe


It remains only to run the file with the following code:

import os
import sys
from distutils.core import setup
import py2exe
sys.argv.append('py2exe')
AUTHOR = 'Варвара Холодная'
COMPANY = 'ООО «Контора»'
NAME = 'Имя будущего .exe файла'
DESCRIPTION = 'Описание файла'
SCRIPT = 'auth_user.py'
VERSION = '1.0.0.0'
BIN = 'bin/'
# файлы, которые нужно будет собрать из папки bin
DATA_FILES = [
    'path_to_chrome.txt', 'cacert.pem', 'chromedriver.exe'
]
setup(
    options={
        'py2exe': {
            'bundle_files': 1,
            'compressed': True,
            'unbuffered': True,
            'optimize': 0,
        }
    },
    data_files=[('', [BIN+s for s in DATA_FILES])],
    console=[{
        'script': SCRIPT,
        'name': NAME,
        'dest_base': NAME,
        'description': DESCRIPTION,
        'copyright': AUTHOR,
        'company_name': COMPANY,
        'version': VERSION
    }],
    zipfile=None,
)

After successful compilation, the dist folder will appear, in which all the necessary files will be located.

PS


Testing files with manual authorization is inconvenient. It is much easier to go through the OAuth link yourself, write out access_token and then execute another file, for example, auth_direct.py (of course, it will not be suitable for transfer to another user):

import vk
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from script import PagesUpdater
ACCESS_TOKEN = 'Заранее полученный и сохраненный access_token'
VKAPI = vk.API(access_token=ACCESS_TOKEN)
VKError = vk.api.VkAPIMethodError
class DirectUpdater(PagesUpdater):
    def __init__(self, driver_path):
        self.vkapi = VKAPI
        self.vkerror = VKError
        self.driver = webdriver.Chrome(driver_path)
du = DirectUpdater(
    'Путь к chromedriver.exe')
du.launch()

Also popular now: