Боевой краулер Python (5) | Моделирование входа в систему WeChat

Python внешний интерфейс WeChat рептилия

Добавить Автора

Публичный аккаунт WeChat:Python数据科学

Знаю почти:Аналитик данных Python


Мы не знаем, когда WeChat стал неотъемлемой частью нас, нашего круга общения, новостей или публичных аккаунтов, за которыми мы следим, а также личной информации или конфиденциальности — все это связано друг с другом. Поскольку это так важно, если мы можем использовать сканеры для имитации входа в систему, значит ли это, что мы можем получить эту информацию и даже эффективно просматривать и управлять ею по мере необходимости. Да, все верно, все в порядке. Этот блоггер расскажет вам, как模拟登录网页版的微信, и отобразить полученный после имитации входа в систему好友列表信息.

Процесс входа в симуляцию WeChat более сложен.Конечно, каким бы ни был метод, то же самое верно.Мы по-прежнему используемfiddlerИнструмент захвата пакетов для имитации процесса входа в систему. Что ж, давайте подробно объясним шаг за шагом, как добиться этого сложного процесса.

Имитация запроса на вход с помощью скрипача

Сначала мы открываем веб-версию WeChat в браузере (fiddlerОн был открыт ранее), и тогда мы увидим интерфейс QR-кода.

Затем мы используем мобильный телефон WeChat для сканирования и подтверждения, а затем выполняется вход в веб-версию WeChat.

ок, пойдем посмотримfiddlerСообщите нам, какие пакеты захвачены. Поскольку в процессе выдается много запросов, здесь захват пакетов разбит по операциям и анализируется один за другим.

1. Откройте веб-страницу WeChat.

Захват пакета на этом шаге подобен этому, и обнаружено, чтоlogin.wx.qq.comДве ссылки — это все, что нам нужно.

Так что нажмите, чтобы проанализировать подробно.

Первая ссылка выглядит следующим образом:get请求, вы можете видеть, что некоторые параметры передаются в uriappid、redirect_uri、fun、lang、_.

GET /jslogin?appid=wx782c26e4  c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1520350213674 HTTP/1.1

Найден после многих царапинappid、redirect_uri、fun、langпараметры фиксированы, а_Это серия меняющихся чисел.Как мы упоминали в предыдущей статье о моделировании торгового центра Jingdong, на самом деле это时间戳, если вам непонятно, вы можете просмотреть [боевой поисковый робот Python (4) | Имитация входа в торговый центр Jingdong] [1]

Зная эти параметры, можно имитировать получение и отправку.Так почему же мы моделируем этот шаг?

Это потому, что доступ к этой ссылке будет иметь следующий ответ, который содержит важную информацию, которая нам понадобится позжеuuid(будет упомянуто в следующих шагах).

window.QRLogin.code = 200; window.QRLogin.uuid = "Idf_QdW1OQ==";

2. Смоделируйте получение QR-кода

Метод входа, предоставляемый веб-страницей WeChat, заключается в сканировании кода, и мы не можем избежать этого даже с помощью симуляции, поэтому нам также необходимо сканировать код для проверки. Вернувшись в браузер, ссылку на QR-код можно легко найти с помощью инструментов разработчика.

https://login.weixin.qq.com/qrcode/AdgAWNry-w==

Мы обнаружили, что последняя строка изменена. подожди, это иuuidТочно так же. Да это такuuid, используемый для обеспечения уникальности QR-кода.

Поэтому мы будем извлекать вышеuuidПосле соединения с обратной стороной вы можете получить изображение QR-кода, а затем отсканировать код, чтобы подтвердить операцию.

3. Определите статус входа

Чтобы определить, успешен ли код сканирования, нам нужно использовать вторую ссылку, упомянутую выше на этом шаге.

GET /cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=Idf_QdW8OQ==&tip=1&r=68288473&_=1520050213675 HTTP/1.1

Эта ссылка такжеget请求, который также содержит некоторые параметры.

Фактически, в процессе захвата пакета обнаруживается, что пока мы не сканируем QR-код, ссылка будет отправляться повторно, пока QR-код не будет отсканирован или не истечет время ожидания.

Итак, как мы можем определить, был ли QR-код отсканирован или авторизован?

Об этом также судят по ответным данным. После анализа выясняется, что если QR-код не был отсканирован, ответ выглядит следующим образом:

window.code=408;

Но если QR-код сканируется, ответ такой:

window.code=201;window.userAvatar = .....
window.code=200;
window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AaL_Xd5muLPKNVY_Hzt_uoBs@qrticket_0&uuid=gbJqPdkNSQ==&lang=zh_CN&scan=1520353803";

code=201Указывает, что QR-код был успешно отсканирован.code=200Указывает, что вход в систему прошел успешно.

4. Войти

После сканирования QR-кода,fiddlerБудет еще несколько новых запросов.

Возможно, вы нашли, что в предыдущем шаге есть перенаправленные URI.

GET https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AaL_Xd5muLPKNVY_Hzt_udBs@qrticket_0&uuid=gbJqPdfNSQ==&lang=zh_CN&scan=1520353803&fun=new&version=v2 HTTP/1.1

Определив успешный ответ на вход на предыдущем шаге, мы можем получить все параметры в ответе. Да, эти параметры можно использовать в официальном логине (即跳转链接) запрос. Поэтому мы используем эти параметры, чтобы сделать еще один запрос на получение. Параметры переноски следующие:

Конечно, этот запрос на вход также вернет некоторые коды ответов. Коды ответов следующие:

<error>
     <ret>0</ret>
     <message>OK</message>
     <skey>xxx</skey>
     <wxsid>xxx</wxsid>
     <wxuin>xxx</wxuin>
     <pass_ticket>xxx</pass_ticket>
     <isgrayscale>1</isgrayscale>
</error>

Еще куча параметров, это почти бесконечно. Не волнуйтесь, мы почти у цели. Чтобы получить этот ответ, нам также нужно извлечь все параметры для следующего запроса.

5. Инициализируйте синхронизацию

Ну и, наконец, последний шаг — это запрос инициализации и синхронизации WeChat.Информация об инициализации связана следующим образом:

POST https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=64629109&pass_ticket=4dU5IS9EqtXt5cIV2Gni1tKG7m2V56PXk5XI%252BdjdrIk%253D HTTP/1.1

contactКонтактные ссылки следующие:

GET https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket=4dU5IS9EqtXt5cIV2Gni1tKG7m2V56PXk5XI%252BdjdrIk%253D&r=1520353806102&seq=0&skey=@crypt_a82dd73a_3885c878ae2f4590f7b2b5ee949dd1bd HTTP/1.1

Параметры в uripass_ticket,skeyОн был получен в ответе на предыдущем шаге, и его можно выполнить, отправив запрос напрямую. Из ответов по этим двум ссылкам мы можем получить реальную и полезную информацию.

Также есть ссылка на синхронный запрос, и необходимые параметры можно извлечь из ответов по двум ссылкам выше. Но пока мы можем получить нужную информацию по двум указанным выше ссылкам, поэтому нам не нужно запрашивать эту синхронную ссылку.

GEThttps://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1520353806125&skey=%40crypt_a82dd73a_3885c878ae2f4590f7b2b5ee949dd1bd&sid=O2Se5s2LJzPebME2&uin=254891255&deviceid=e289448639092966&synckey=1_694936977%7C2_694936979%7C3_694936982%7C1000_1520324882&_=1520353793581 HTTP/1.1

Базовый процесс входа в систему немного сложен, поэтому блоггер обобщил блок-схему для справки.

Код

запросить фиктивное использованиеrequestsМодуль завершен, разобран с помощьюre. Здесь следует отметить, что если операция продолжает сообщатьsslЭто неправильно, вы можете добавить это в запросverify=FalseРешено путем пропуска проверки подлинности сертификата.

1. Параметры инициализации

def __init__(self):
    self.session = requests.session()
    self.headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0'}
    self.QRImgPath = os.path.split(os.path.realpath(__file__))[0] + os.sep + 'webWeixinQr.jpg'
    self.uuid = ''
    self.tip = 0
    self.base_uri = ''
    self.redirect_uri = ''
    self.skey = ''
    self.wxsid = ''
    self.wxuin = ''
    self.pass_ticket = ''
    self.deviceId = 'e000000000000000'
    self.BaseRequest = {}
    self.ContactList = []
    self.My = []
    self.SyncKey = ''

Определите класс, инициализируйте все параметры запроса экземпляра и определите путь QR-кода.

2. Запросить UUID

def getUUID(self):
    url = 'https://login.weixin.qq.com/jslogin'
    params = {
        'appid': 'wx782c26e4c19acffb',
        'redirect_uri': 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage',
        'fun': 'new',
        'lang': 'zh_CN',
        '_': int(time.time() * 1000),  # 时间戳
    }
    response = self.session.get(url, params=params)
    target = response.content.decode('utf-8')
    pattern = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'
    ob = re.search(pattern, target)  # 正则提取uuid
    code = ob.group(1)
    self.uuid = ob.group(2)
    if code == '200':  # 判断请求是否成功
        return True
    return False

Используйте обычный, чтобы извлечь соответствующийuuid,пройти черезcodeЧтобы определить, успешен ли запрос, ответ выглядит следующим образом:

window.QRLogin.code = 200; window.QRLogin.uuid = "Idf_QdW1OQ==";

3. Смоделируйте получение QR-кода

def showQRImage(self):
    url = 'https://login.weixin.qq.com/qrcode/' + self.uuid
    response = self.session.get(url)
    self.tip = 1
    with open(self.QRImgPath, 'wb') as f:
        f.write(response.content)
        f.close()
    # 打开二维码
    if sys.platform.find('darwin') >= 0:
        subprocess.call(['open', self.QRImgPath])  # 苹果系统
    elif sys.platform.find('linux') >= 0:
        subprocess.call(['xdg-open', self.QRImgPath])  # linux系统
    else:
        os.startfile(self.QRImgPath)  # windows系统
    print('请使用微信扫描二维码登录')

использоватьuuidЗапросите изображение QR-кода и автоматически откройте его в соответствии с операционной системой.

4. Определить зарегистрированные

def checkLogin(self):
    url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (
        self.tip, self.uuid, int(time.time() * 1000))
    response = self.session.get(url)
    target = response.content.decode('utf-8')
    pattern = r'window.code=(\d+);'
    ob = re.search(pattern, target)
    code = ob.group(1)
    if code == '201':  # 已扫描
        print('成功扫描,请在手机上点击确认登录')
        self.tip = 0
    elif code == '200':  # 已登录
        print('正在登录中...')
        regx = r'window.redirect_uri="(\S+?)";'
        ob = re.search(regx, target)
        self.redirect_uri = ob.group(1) + '&fun=new'
        self.base_uri = self.redirect_uri[:self.redirect_uri.rfind('/')]
    elif code == '408':  # 超时
        pass
    return code

Ответ выглядит следующим образом:

window.code=200;
window.redirect_uri="https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=AaL_Xd5muLPKNVY_Hzt_uoBs@qrticket_0&

Определите статус входа в систему на основе кода в ответе.408: тайм-аут201: отсканировано200:Вошел в систему

5. Войти

def login(self):
    response = self.session.get(self.redirect_uri, verify=False)
    data = response.content.decode('utf-8')
    doc = xml.dom.minidom.parseString(data)
    root = doc.documentElement
    # 提取响应中的参数
    for node in root.childNodes:
        if node.nodeName == 'skey':
            self.skey = node.childNodes[0].data
        elif node.nodeName == 'wxsid':
            self.wxsid = node.childNodes[0].data
        elif node.nodeName == 'wxuin':
            self.wxuin = node.childNodes[0].data
        elif node.nodeName == 'pass_ticket':
            self.pass_ticket = node.childNodes[0].data
    if not all((self.skey, self.wxsid, self.wxuin, self.pass_ticket)):
        return False
    self.BaseRequest = {
        'Uin': int(self.wxuin),
        'Sid': self.wxsid,
        'Skey': self.skey,
        'DeviceID': self.deviceId,
    }
    return True

Ссылка для входа в запрос скачка извлекается, и параметр кода ответа извлекается.Ответ выглядит следующим образом:

<error>
    <ret>0</ret>
    <message>OK</message>
    <skey>xxx</skey>
    <wxsid>xxx</wxsid>
    <wxuin>xxx</wxuin>
    <pass_ticket>xxx</pass_ticket>
    <isgrayscale>1</isgrayscale>
</error>

6. Инициализируйте доступ к информации

def webwxinit(self):
    url = self.base_uri + \
          '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (
              self.pass_ticket, self.skey, int(time.time() * 1000))
    params = {
        'BaseRequest': self.BaseRequest
    }
    h = self.headers
    h['ContentType'] = 'application/json; charset=UTF-8'
    response = self.session.post(url, data=json.dumps(params), headers=h, verify=False)
    data = response.content.decode('utf-8')
    print(data)
    dic = json.loads(data)
    self.ContactList = dic['ContactList']
    self.My = dic['User']
    SyncKeyList = []
    for item in dic['SyncKey']['List']:
        SyncKeyList.append('%s_%s' % (item['Key'], item['Val']))
    self.SyncKey = '|'.join(SyncKeyList)
    ErrMsg = dic['BaseResponse']['ErrMsg']
    Ret = dic['BaseResponse']['Ret']
    if Ret != 0:
        return False
    return True

Ссылка на запрос инициализации, получение данных ответа инициализации.

def webwxgetcontact(self):
    url = self.base_uri + \
          '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % (
              self.pass_ticket, self.skey, int(time.time()))
    h = self.headers
    h['ContentType'] = 'application/json; charset=UTF-8'
    response = self.session.get(url, headers=h, verify=False)
    data = response.content.decode('utf-8')
    # print(data)
    dic = json.loads(data)
    MemberList = dic['MemberList']
    # 倒序遍历,不然删除的时候出问题..
    SpecialUsers = ["newsapp", "fmessage", "filehelper", "weibo", "qqmail", "tmessage", "qmessage", "qqsync",
                    "floatbottle", "lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp", "blogapp",
                    "facebookapp", "masssendapp",
                    "meishiapp", "feedsapp", "voip", "blogappweixin", "weixin", "brandsessionholder",
                    "weixinreminder", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "officialaccounts",
                    "notification_messages", "wxitil", "userexperience_alarm"]
    for i in range(len(MemberList) - 1, -1, -1):
        Member = MemberList[i]
        if Member['VerifyFlag'] & 8 != 0:  # 公众号/服务号
            MemberList.remove(Member)
        elif Member['UserName'] in SpecialUsers:  # 特殊账号
            MemberList.remove(Member)
        elif Member['UserName'].find('@@') != -1:  # 群聊
            MemberList.remove(Member)
        elif Member['UserName'] == self.My['UserName']:  # 自己
            MemberList.remove(Member)
    return MemberList

проситьcontactссылка для получения контактов, официальных аккаунтов, групповых чатов и личной информации. Код ответаjsonформате следующим образом:

{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
}
,
"Count": 11,
"ContactList": [{
"Uin": 0,
"UserName": "filehelper",
"NickName": "文件传输助手",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=621637626&username=filehelper&skey=@crypt_a82dd73a_7e8e1054c011e8d71d0b542f39c7db85",
"ContactFlag": 3,
"MemberCount": 0,
"MemberList": [],
"RemarkName": "",
"HideInputBarFlag": 0,
"Sex": 0,
"Signature": "",
"VerifyFlag": 0,
"OwnerUin": 0,
"PYInitial": "WJCSZS",
"PYQuanPin": "wenjianchuanshuzhushou",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": 0,
"AppAccountFlag": 0,
"Statues": 0,
"AttrStatus": 0,
"Province": "",
"City": "",
"Alias": "",
"SnsFlag": 0,
"UniFriend": 0,
"DisplayName": "",
"ChatRoomId": 0,
"KeyWord": "fil",
"EncryChatRoomId": "",
"IsOwner": 0
}
,{...}
...

В соответствии с ответной информацией в полевых операциях здесь нужно получить список друзей, поэтому количество других полей, таких как общедоступный, групповой чат, сами удаляются, оставляя только информацию о друзьях.

7. Основная функция запуска

def main(self):
    if not self.getUUID():
        print('获取uuid失败')
        return
    self.showQRImage()
    time.sleep(1)
    while self.checkLogin() != '200':
        pass
    os.remove(self.QRImgPath)
    if not self.login():
        print('登录失败')
        return
    # 登录完成, 下面查询好友
    if not self.webwxinit():
        print('初始化失败')
        return
    MemberList = self.webwxgetcontact()
    print('通讯录共%s位好友' % len(MemberList))
    for x in MemberList:
        sex = '未知' if x['Sex'] == 0 else '男' if x['Sex'] == 1 else '女'
        print('昵称:%s, 性别:%s, 备注:%s, 签名:%s' % (x['NickName'], sex, x['RemarkName'], x['Signature']))

Имитация результата входа

Список друзей выглядит следующим образом:

Конечно, список друзей — это всего лишь пример, и мы также можем просматривать и управлять другой информацией или анализировать данные.

Суммировать

В этой статье мы поделимся с вами смоделированным процессом входа в веб-версию WeChat. Хотя запрос немного сложен в процессе, пока мы его тщательно анализируем, его можно реализовать шаг за шагом. Надеюсь, это будет полезно для всех. Код был загружен на github:Связь

полный.


Обратите внимание на публичный аккаунт WeChatPythonНаука о данных,Получать120GУчебные материалы по искусственному интеллекту.