В этой статье будет рассмотрен способ создать .exe файл, позволяющий пройти авторизацию ВК и выполнить определенные запросы к VK API. Установка Python не требуется.
Около года тому назад я наткнулся на паблик, занимающийся переводами зарубежных статей на русский.
Спустя какое-то время возникла идея создать страницу, на которой все-все-все переводы будут распределены по категориям. И, разумеется, эту страницу нужно будет изредка обновлять, т.е. собирать информацию о уже выпущенных материалах, их категориях и т.п., а затем раскладывать по полочкам. В целом, VK API позволяет это сделать.
Но и это не все: каждый участник, наделенный редакторскими правами, также должен быть в состоянии обновить все странички. Способ «заставитьэтих гуманитариев всех редакторов установить себе python» очевидно не подходит. Нужно просто — клик, клик, запустилось, заработало, закрылось. Проще говоря, нужен .exe файл.
Более того, если в тело файла добавить код, взаимодействующий с конкретным пользователем, то можно создать, к примеру, универсальный сортировщик аудио.
Возможно, изложенный здесь способ — очередной изобретенный велосипед, но все-таки я продолжу.
В папке, где будут храниться наши будущие файлы, создаем подпапку (например, bin), куда сохраняем chromedriver.exe, cacert.pem и (если очень хочется) иконку для нашего будущего .exe файла. В эту же папку помещаем пока что пустой текстовый файл path_to_chrome.txt.
Итак, все готово. Еще раз о том, как происходит процесс авторизации и получения ключа доступа access_token для работы с API.
О том, как формируется ссылка, рассказано в официальной документации.
Импортируем все необходимое, объявляем константы:
О PagesUpdater и передаваемых ему аргументах будет рассказано чуть позже.
Selenium требует указать полный путь к файлу chromedriver.exe, который должен находиться в одной папке с браузером. Путь к браузеру наверняка отличается на каждом компьюетере, поэтому вынесем его в отдельный текстовый файл path_to_chrome.txt следующего содержания:
Разумеется, пользователь должен заранее указать полный путь к браузеру в path_to_chrome.txt.
Перехватываем access_token:
Ну, и напоследок, забегая вперед, скажу, что после компиляции возникает ошибка из-за нехватки файла cacert.pem. Здесь дано решение: добавить cacert.pem в PATH, а потом не забыть собрать при сборке .exe:
Последний штрих.
Всё. Идем дальше.
Опять же, импортируем все необходимое, объявляем константы:
self.vkapi будет использоваться для обращения к любым методам VK API. Два других аргумента будут использоваться для обработки Captcha (см. ниже).
Я столкнулся с тремя ошибками, которые гарантированно появлялись при большом количестве запросов:
Создадим метод, в который будем «оборачивать» все запросы к VK API:
Со второй и третьей ошибками разобрались:
С Captcha всё чуточку сложнее. Читаем документацию:
Добавим except блок, в котором будет происходить обработка Captcha:
С ошибками разобрались. Идем дальше.
Добавим простой метод, который возвращает список вики-страниц в группе:
И метод launch, в котором будет происходить все остальное:
Собственно, вот и всё.
Осталось только запустить файл со следующим кодом:
После успешной компиляции появится папка dist, в которой и будут находиться все нужные файлы.
Тестировать файлы с ручной авторизацией — неудобно. Куда легче самому пройти по ссылке OAuth, выписать access_token и в дальнейшем выполнять другой файл, например, auth_direct.py (разумеется, он не подойдет для передачи другому пользователю):
Системные требования
- Windows
- Браузер (на базе Chromium)
- Python, py2exe и Selenium
- Приложение для работы с VK API
- Файлы cacert.pem и chromedriver.exe
Предыстория и, собственно, зачем мне это понадобилось
Около года тому назад я наткнулся на паблик, занимающийся переводами зарубежных статей на русский.
Спустя какое-то время возникла идея создать страницу, на которой все-все-все переводы будут распределены по категориям. И, разумеется, эту страницу нужно будет изредка обновлять, т.е. собирать информацию о уже выпущенных материалах, их категориях и т.п., а затем раскладывать по полочкам. В целом, VK API позволяет это сделать.
Но и это не все: каждый участник, наделенный редакторскими правами, также должен быть в состоянии обновить все странички. Способ «заставить
Более того, если в тело файла добавить код, взаимодействующий с конкретным пользователем, то можно создать, к примеру, универсальный сортировщик аудио.
Возможно, изложенный здесь способ — очередной изобретенный велосипед, но все-таки я продолжу.
Шаг нулевой. Подготовка
В папке, где будут храниться наши будущие файлы, создаем подпапку (например, bin), куда сохраняем chromedriver.exe, cacert.pem и (если очень хочется) иконку для нашего будущего .exe файла. В эту же папку помещаем пока что пустой текстовый файл path_to_chrome.txt.
Шаг первый. Авторизация
Итак, все готово. Еще раз о том, как происходит процесс авторизации и получения ключа доступа access_token для работы с API.
- Пользователь переходит по ссылке, приложение запрашивает доступ
- Пользователь разрешает доступ
- Происходит перенаправление на пустую страницу: в адресной строке появляется access_token
О том, как формируется ссылка, рассказано в официальной документации.
Импортируем все необходимое, объявляем константы:
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()
О PagesUpdater и передаваемых ему аргументах будет рассказано чуть позже.
Selenium требует указать полный путь к файлу chromedriver.exe, который должен находиться в одной папке с браузером. Путь к браузеру наверняка отличается на каждом компьюетере, поэтому вынесем его в отдельный текстовый файл path_to_chrome.txt следующего содержания:
C:\chrome-win32\chrome-win32
def get_chrome_path(self): with open(PATH_TO_CHROME_TXT, 'r') as target: return target.read()
Разумеется, пользователь должен заранее указать полный путь к браузеру в path_to_chrome.txt.
Перехватываем 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
Ну, и напоследок, забегая вперед, скажу, что после компиляции возникает ошибка из-за нехватки файла cacert.pem. Здесь дано решение: добавить cacert.pem в PATH, а потом не забыть собрать при сборке .exe:
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
Последний штрих.
exe = Executor() exe.launch()
Всё. Идем дальше.
Шаг второй. Обращаемся к VK API
Опять же, импортируем все необходимое, объявляем константы:
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 будет использоваться для обращения к любым методам VK API. Два других аргумента будут использоваться для обработки Captcha (см. ниже).
Обработка ошибок
Я столкнулся с тремя ошибками, которые гарантированно появлялись при большом количестве запросов:
- VK Captcha Error (не введен текст с изображения Captcha)
- VK Flood Control (слишком много запросов в секунду)
- requests.exceptions.Timeout (появляется, когда ей угодно)
Создадим метод, в который будем «оборачивать» все запросы к 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
Со второй и третьей ошибками разобрались:
- Перед каждым запросом будет происходить секундная задержка (подробнее о запросах в в документации).
- Если возникнет requests.exceptions.Timeout, то запрос попросту повторится
С Captcha всё чуточку сложнее. Читаем документацию:
… следует запросить пользователя ввести текст с изображения captcha_img и повторить запрос, добавив в него параметры:
- captcha_sid — полученный идентификатор
- captcha_key — текст, который ввел пользователь
Добавим except блок, в котором будет происходить обработка Captcha:
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
С ошибками разобрались. Идем дальше.
Запрос к API
Добавим простой метод, который возвращает список вики-страниц в группе:
def get_titles(self, gid=NGID): return self.errors(self.vkapi.pages.getTitles, group_id=gid)
И метод launch, в котором будет происходить все остальное:
def launch(self): print self.get_titles() # do something else
Собственно, вот и всё.
Компиляция в .exe
Осталось только запустить файл со следующим кодом:
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, )
После успешной компиляции появится папка dist, в которой и будут находиться все нужные файлы.
P.S.
Тестировать файлы с ручной авторизацией — неудобно. Куда легче самому пройти по ссылке OAuth, выписать access_token и в дальнейшем выполнять другой файл, например, auth_direct.py (разумеется, он не подойдет для передачи другому пользователю):
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()