Ignoramos a proibição de mensagens API Vkontakte através do Python

Olá, Habr. No meu artigo anterior, falei sobre a possibilidade de acessar os métodos da seção de mensagens através da documentação, para a qual bastava fazer login no site da VK. Muitos disseram que isso não é uma ameaça aos dados pessoais dos usuários, e a incapacidade de transmitir suas mensagens é a falta de uma plataforma. Também nos comentários, deixei um link para a biblioteca node.js. , que pode efetuar login usando login / senha e fornecer acesso à API da mensagem, fingindo ser um aplicativo oficial.

Aviso Legal:


O artigo e todo o código escrito foram criados apenas para fins educacionais e de pesquisa e nunca foram usados ​​para atividades ilegais. O autor não recomenda que você repita nenhuma das ações descritas aqui e não assume nenhuma responsabilidade por elas.


Mas nem todas as pessoas estão familiarizadas com javascript e node.js, então decidi escrever minha biblioteca em python, que muitas pessoas usam agora, o que nos permite fornecer a funcionalidade completa da API de mensagens através das "solicitações de teste" da documentação. Peço imediatamente que você não fique com raiva de mim em lugares onde repetirei aspectos do “discurso” passado, porque quero organizar este artigo na forma de documentação independente.

Como usá-lo?


A própria biblioteca está localizada no repositório github-a (no mesmo local, na pasta examples, há um script com exemplos de uso deste artigo). Para instalá-lo em um computador, você pode usar o comando no terminal:
pip instalar mensagens vk

Agora podemos importar a classe principal deste pacote e criar uma instância, especificando o login, a senha, que tipo de autorização essa conta usa, bem como o diretório em que queremos salvar os cookies de autorização do usuário. Isso é necessário para que os usuários com autorização de dois fatores não precisem inserir constantemente o código da mensagem sempre que o script for executado.

from vk_messages import MessagesAPI

login, password = 'login', 'password'                                
messages = MessagesAPI(login=login, password=password,
                                two_factor=True, cookies_save_path='sessions/')

E, de fato, é tudo. Agora só precisamos abrir a documentação e usar os métodos nos quais estamos interessados. Quero observar imediatamente que essa abordagem nos permite usar praticamente qualquer método da documentação, mesmo que não relacionado à seção de mensagens:

history = messages.method('messages.getHistory', user_id='1234567', count=5)

Também podemos combinar essa biblioteca com outras pessoas, por exemplo, através do vk_api , podemos baixar fotos de um computador (o código para esta ação é fornecido na seção de exemplos) e, através do vk_messages, anexamos esses anexos à mensagem:

from vk_messages.utils import get_random

messages.method('messages.send', user_id=peer_id, message='Hello',
            attachment='photo123456_7891011', random_id=get_random())

Por curiosidade, implementei uma função clássica que, em uma determinada pasta, cria subpastas de pessoas com quem uma pessoa estava falando e tenta gerar as mensagens mais recentes e os URLs absolutos das fotos. Felizmente, tudo funcionou como um relógio e não houve erros desnecessários:

from vk_messages.utils import fast_parser

fast_parser(messages, path='parsing/',                    
       count_conv=10, messages_deep=400, photos_deep=100) 

Agora quero passar para uma das partes mais interessantes desta biblioteca: com cookies de autorização, podemos executar absolutamente qualquer ação. Deixe-me dar um exemplo pessoal: quando, para as postagens do grupo em que sou membro, precisei criar uma tabela que consistisse no ID da postagem e em seu autor . Mas qual foi o problema: a API oficial retorna apenas a pessoa que publicou o artigo. Usando o sniffer, vi que quando você passa o mouse sobre a data de publicação posterior, esses dados são baixados do servidor. Depois disso, escrevi um invólucro que lhe permitia enviar quantos pedidos semelhantes você desejasse, usando apenas o link de postagem e os cookies de autorização para obter os autores. No exemplo abaixo, resta apenas eliminar as tags desnecessárias:

def get_creators(post, cookies):
    group = -int(post.split('_')[0])
    response = requests.post('https://vk.com/al_page.php', cookies=cookies,
               data=f"_ads_group_id={group}&act=post_author_data_tt&al=1&raw={post}")

    response_json = json.loads(response.text[4:])['payload'][1]
    return response_json[0]

authors = get_creators(post='-12345_67890',
                    cookies=messages.get_cookies())
print(authors)   

Mas o que a parte superior do código nos prova? É isso mesmo, mesmo que o VC feche solicitações de teste em sua documentação, sempre podemos simular ações do usuário e obter as informações necessárias. Como experiência, criei uma pequena função que, através de pedidos de "rolagem" da página, pode receber links para fotos sem usar a API oficial.

O código era muito grande, então eu decidi escondê-lo
def get_attachments(attachment_type, peer_id, count, offset, cookies_final):
    if attachment_type == 'photo':
        session = requests.Session()
        parsed = []
        
        response = session.post(f'https://vk.com/wkview.php',
                            data=f'act=show&al=1&dmcah=&loc=im&ref=&w=history{peer_id}_photo', cookies=cookies_final)
        
        response_json = json.loads(response.text[4:])
        
        try:
            last_offset = response_json['payload'][1][2]['offset']
            count_all = response_json['payload'][1][2]['count']
        except:
            last_offset = response_json['payload'][1][0]['offset']
            count_all = response_json['payload'][1][0]['count']

        while (len(parsed) < count + offset)  and (last_offset != count_all):
            response_json = json.loads(response.text[4:])
        
            try:
                last_offset = response_json['payload'][1][2]['offset']
            except:
                last_offset = response_json['payload'][1][0]['offset']

            photos_vk =  re.findall(r'<a href="/photo(\S*)?all=1"', response_json['payload'][1][1])
            mails =  re.findall(r"'(\S*)', {img: this ,", response_json['payload'][1][1])

            for photo, mail in zip(photos_vk, mails):
                if len(parsed) < offset:
                    parsed.append(photo)
                    continue
                
                response = session.post(f'https://vk.com/al_photos.php', cookies=cookies_final,
                            data=f'act=show&al=1&al_ad=0&dmcah=&gid=0&list={mail}&module=im&photo={photo}')
                
                response_json = json.loads(response.text[4:])
                photo_size = list(response_json['payload'][1][3][0].items())
                photo_size.reverse()
                
                for i in range(len(photo_size)):
                    if 'attached_tags' in photo_size[i][0]:
                        photo_size = photo_size[:i]
                        break
            
                parsed.append(photo_size) 

            response = session.post(f'https://vk.com/wkview.php', cookies=cookies_final,
                    data=f'act=show&al=1&offset={last_offset}&part=1&w=history{peer_id}_photo')
        
        return parsed[offset + 3 : offset + 3 + count]


Parece volumoso? Sim. Funciona muito mais devagar que a API oficial? Sim. Mas se o VC tirar a última oportunidade de acessar mensagens, sempre podemos encontrar uma saída.

Também observo que tentei adicionar a todos os lugares da biblioteca onde erros são possíveis, exceções com explicações, mas se você encontrar algum evento que não exiba uma explicação, informe-me sobre isso.
, , - , , , , , . , , .

?


Para aqueles que estão interessados ​​no que está acontecendo nos bastidores deste script, irei abordar brevemente os pontos principais. Durante a autorização, são feitas solicitações de solicitação simples que simulam o logon do usuário, que muda apenas ligeiramente dependendo do tipo de autorização e, após o logon bem-sucedido, os cookies são salvos em um arquivo pickle. Ao solicitar a API por meio da documentação, "param_" é adicionado a todos os parâmetros personalizados, ou seja, o valor do deslocamento se transforma em param_offset. Além disso, o código hash é transmitido na solicitação, que está contida no atributo data-hash da tag do botão "Executar". Tanto quanto eu notei, esse valor é constante para cada método.

Também observo um ponto importante: a senha é enviada na codificação ANSI, onde os caracteres do alfabeto russo são separados pelo sinal "%" e esse código é suficiente para implementar essa decodificação. Isso pode ser um problema para alguns usuários de Linux, porque, tanto quanto me lembro, essa codificação não é incluída por padrão no python neste sistema operacional.

self.password = str(password.encode('ANSI')).replace('\\x', '%')[2:-1]

Além disso, um dos problemas para mim foi o comportamento estranho de alguns métodos. Por exemplo, se eu trocasse os parâmetros, o script poderia retornar uma resposta 10 vezes menor que a solicitada ou não retornar absolutamente nada. Para resolver esse problema, resolvi analisar e enviar os parâmetros em uma ordem estrita, conforme indicado na documentação. Talvez seja uma simples coincidência, mas após esse tipo de problema eu não tive:

response = session.get(f'https://vk.com/dev/{name}', cookies=self.cookies_final)
hash_data =  re.findall(r'data-hash="(\S*)"', response.text)

soup = BeautifulSoup(response.text, features="html.parser")
params = soup.findAll("div", {"class": "dev_const_param_name"})
params = [cleanhtml(str(i)) for i in params]

payload, checker = '', 0
for param in params:
   if param in kwargs:
       checker += 1
       payload += '&{}={}'.format('param_' + \
   param, quote(str(kwargs[param]) if type(kwargs[param]) != bool else str(int(kwargs[param]))))
        
if checker != len(kwargs):
   raise Exception_MessagesAPI('Some of the parametrs invalid', 'InvalidParameters')

Total


Bem, para mim, essa biblioteca foi a primeira experiência em escrever projetos "abertos", por isso peço que você não a julgue severamente. Eu só queria ajudar pessoas que enfrentam o mesmo problema que eu: limitação da API de mensagens. Também quero agradecer aos meus amigos que me ajudaram a escrever este artigo e testar o código.

All Articles