В этой статье будет рассмотрен способ создать .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()