我们通过Python绕过了消息API Vkontakte的禁止

哈Ha 在我的上一篇文章中,我谈到了可以通过文档访问messages部分的方法的可能性,为此,足以登录到VK站点。许多人然后说这不是对用户个人数据的威胁,并且缺乏平台无法抽出他们的消息。另外,在注释中,我还保留了指向node.js库的链接,该可以使用登录名/密码登录并提供对消息API的访问,并假装是官方应用程序。

免责声明:


本文和所有书面代码仅出于教育和研究目的而创建,从未用于非法活动。作者不敦促您重复此处描述的任何操作,对此不承担任何责任。


但是并不是所有人都熟悉javascript和node.js,所以我决定用python写我的库,现在很多人都在使用python,这使我们能够通过文档的“测试请求”提供message API的全部功能。我立即要求您不要在我会重复过去“语音”的地方生气,因为我想以独立文档的形式来安排本文。

如何使用它?


该库本身位于github-a存储库中(在同一位置,在examples文件夹中,有一个脚本以及本文的使用示例)。要将其安装在计算机上,可以在终端中使用以下命令:
pip安装vk消息

现在,我们可以从该包中导入主类并创建它的实例,指定登录名,密码,该帐户使用的授权类型以及要保存用户授权cookie的目录。这是必要的,这样,具有双重授权的用户不必每次运行脚本时都从消息中不断输入代码。

from vk_messages import MessagesAPI

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

实际上,仅此而已。现在,我们只需要打开文档并使用我们感兴趣的方法即可。我想立即注意到,这种方法使我们几乎可以使用文档中的任何方法,甚至与消息部分无关:

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

我们还可以将此库与其他库结合使用,例如,通过vk_api,我们可以从计算机上载照片(此操作的代码在其示例部分中提供),并通过vk_messages将这些附件附加到消息上:

from vk_messages.utils import get_random

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

出于好奇,我实现了经典功能,该功能在给定的文件夹中创建了一个与之交谈的人的子文件夹,并试图提取出最新消息和照片的绝对URL。幸运的是,一切都像时钟一样工作,并且没有不必要的错误:

from vk_messages.utils import fast_parser

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

现在,我想进入该库最有趣的部分之一:拥有授权cookie,我们可以执行任何操作。让我给你一个个人的例子,当我要成为该小组成员的帖子时,我必须制作一个包含帖子ID及其作者的表格。但是要注意的是:官方api只返回发表该文章的人。使用嗅探器,我看到当您将鼠标悬停在发布后的日期时,将从服务器上下载此数据。之后,我编写了一个包装器,使您可以发送任意数量的类似请求,仅使用发布链接和授权cookie来获取作者。在下面的示例中,它仍然只是摆脱不必要的标签:

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)   

但是最上面的代码向我们证明了什么?没错,即使VC在其文档中关闭了测试请求,我们也始终可以模拟用户操作并获取必要的信息。作为一个实验,我做了一个小功能,可以通过“滚动”页面的请求来接收照片的链接,而无需使用官方API。

代码太大,所以我决定将其隐藏
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]


它看起来笨重吗?是。它的工作速度比官方API慢吗?是。但是,如果VC夺走了访问消息的最后机会,我们总能找到一条出路。

我还注意到,我尝试将所有可能出现错误的地方添加到库中,包括解释的例外,但是如果您发现任何未显示解释的事件,请告知我。
, , - , , , , , . , , .

?


对于那些对脚本幕后发生的事情感兴趣的人,我将简要介绍要点。在授权过程中,会进行简单的请求请求来模拟用户的登录,该请求仅会根据授权类型而略有变化,并且在成功登录后,Cookie将保存在pickle文件中。通过文档请求api时,会将“ param_”添加到所有自定义参数中,也就是说,偏移值将变为param_offset。同样,哈希码在请求中传输,该哈希码包含在“运行”按钮标记的data-hash属性中。据我所知,该值对于每种方法都是恒定的。

我还要指出一个重要的点:密码是以ANSI编码发送的,其中俄语字母的字符用符号“%”分隔,并且此代码足以实现这种解码。对于某些Linux用户来说,这可能是个问题,因为据我所知,此操作系统上的python默认不包含此编码。

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

另外,对我来说,问题之一是某些方法的奇怪行为。例如,如果我交换参数,脚本可能会返回比请求的响应小10倍的响应,或者根本不返回任何东西。为了解决此问题,我决定严格按照文档中指示的顺序解析和发送参数。也许这只是一个简单的巧合,但是在出现了此类问题之后,我没有了:

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')


好吧,对我来说,这个库是编写“开放”项目的第一次经验,所以我请您不要认真地判断它。我只是想帮助与我面临同样问题的人:消息API限制。我也非常感谢那些帮助我撰写本文和测试代码的朋友。

All Articles