def login(self, pin_code): ''' 登陆 :param pin_code: 登陆校验码 :return: 登陆失败原因(若登陆成功返回'') ''' params = { 'client' : 'ssologin.js(v1.4.18)', 'entry' : 'weibo', 'encoding' : cfg.CHARSET_UTF8, 'su' : self.base64_un, 'sp' : self.rsa_pwd, 'pcid' : self.cookie.pin_code_id, 'door' : pin_code, 'servertime' : self.cookie.servertime, 'nonce' : self.cookie.nonce, 'rsakv' : self.cookie.rsakv, 'pwencode' : 'rsa2', 'gateway' : '1', 'from' : '', 'savestate' : '7', 'qrcode_flag' : 'false', 'useticket' : 'useticket', 'vsnf' : '1', 'service' : 'miniblog', 'sr' : '1366*768', 'prelt' : '7873', 'pagerefer' : 'https://login.sina.com.cn/crossdomain2.php?action=logout&r=https://weibo.com/logout.php?backurl=%252F', 'url' : 'https://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack', 'returntype' : 'META', } response = requests.get(url=cfg.LOGIN_URL, headers=xhr.get_headers(), params=params, allow_redirects=False) xhr.take_response_cookies(response, self.cookie) callback_url = self.get_callback_url(response.text, 'location\.replace\("([\s\S]*)"\);') reason = self.is_logined(callback_url) if not reason : # 登陆后, 第一次重定向 response = requests.get(url=callback_url, headers=xhr.get_headers(self.cookie.to_nv())) xhr.take_response_cookies(response, self.cookie) callback_url = self.get_callback_url(response.text, "location\.replace\('([\s\S]*)'\);") # 第二次重定向(获取登录信息) response = requests.get(url=callback_url, headers=xhr.get_headers(self.cookie.to_nv())) xhr.take_response_cookies(response, self.cookie) root = json.loads(xhr.to_json(response.text)) if root.get('result') == True : userinfo = root.get('userinfo') self.cookie.user_id = userinfo.get('uniqueid', '') # 用户ID else: reason = '登陆成功, 但获取用户信息失败' return reason
def get_album_list(self): ''' 获取相册列表 :return: 相册列表(仅相册信息, 不含内部照片信息) ''' print('正在提取新浪用户 [%s] 的相册列表...' % self.sina_user_id) params = { 'uid' : self.sina_user_id, '__rnd' : str(int(time.time() * 1000)), 'page' : '1', 'count' : '20' } response = requests.get(url=cfg.ALBUM_LIST_URL, headers=xhr.get_headers(self.cookie.to_nv()), params=params) albums = [] try: root = json.loads(response.text) data = root['data'] album_list = data['album_list'] for album in album_list : aid = album.get('album_id', '') name = album.get('caption', '') type = int(album.get('type', '0')) total_pic_num = album.get('count').get('photos', 0) albums.append(Album(aid, name, type, total_pic_num)) except: print('提取新浪用户 [%s] 的相册列表异常' % self.sina_user_id) traceback.print_exc() return albums
def get_page_moods_json(self, page): ''' 获取分页的说说Json :param page: 页码 :return: 分页的说说Json ''' headers = xhr.get_headers(self.cookie.to_nv()) headers['Referer'] = cfg.MOOD_REFERER params = { 'g_tk': self.cookie.gtk, 'qzonetoken': self.cookie.qzone_token, 'uin': self.QQ, 'hostUin': self.QQ, 'pos': str((page - 1) * cfg.BATCH_LIMT), 'num': str(cfg.BATCH_LIMT), 'cgi_host': cfg.MOOD_DOMAIN, 'inCharset': cfg.DEFAULT_CHARSET, 'outCharset': cfg.DEFAULT_CHARSET, 'notice': '0', 'sort': '0', 'code_version': '1', 'format': 'jsonp', 'need_private_comment': '1', } response = requests.get(cfg.MOOD_URL, headers=headers, params=params) return xhr.to_json(response.text)
def init_cookie_env(self): ''' 初始化登陆用的cookie环境参数. 主要提取SIG值(cookie属性名为:pt_login_sig) :return: ''' params = { 'proxy_url': 'https://qzs.qq.com/qzone/v6/portal/proxy.html', 's_url': 'https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&from=iqq', 'pt_qr_link': 'http://z.qzone.com/download.html', 'self_regurl': 'https://qzs.qq.com/qzone/v6/reg/index.html', 'pt_qr_help_link': 'http://z.qzone.com/download.html', 'qlogin_auto_login': '******', 'low_login': '******', 'no_verifyimg': '1', 'daid': '5', 'appid': '549000912', 'hide_title_bar': '1', 'style': '22', 'target': 'self', 'pt_no_auth': '0', 'link_target': 'blank' } response = requests.get(url=cfg.SIG_URL, headers=xhr.get_headers(), params=params) xhr.take_response_cookies(response, self.cookie) print('已获得本次登陆的SIG码: %s' % self.cookie.sig)
def get_album_list(self): ''' 获取相册列表 :return: 相册列表(仅相册信息, 不含内部照片信息) ''' print('正在提取QQ [%s] 的相册列表...' % self.QQ) response = requests.get(url=cfg.ALBUM_LIST_URL, headers=xhr.get_headers(self.cookie.to_nv()), params=self._get_album_parmas()) albums = [] try: root = json.loads(xhr.to_json(response.text)) data = root['data'] album_list = data['albumListModeSort'] for album in album_list: name = album.get('name', '') question = album.get('question', '') if not question: total = album.get('total', 0) id = album.get('id', 'unknow') url = cfg.ALBUM_URL(self.QQ, id) albums.append(Album(id, name, url, total)) print('获得相册 [%s] (照片x%d), 地址: %s' % (name, total, url)) else: print('相册 [%s] 被加密, 无法读取' % name) except: print('提取QQ [%s] 的相册列表异常' % self.QQ) traceback.print_exc() return albums
def get_page_photos(self, album, page): ''' 获取相册的分页照片信息 :param album: 相册信息 :param page: 页数 :return: 分页照片信息 ''' response = requests.get(url=cfg.PHOTO_LIST_URL, headers=xhr.get_headers(self.cookie.to_nv()), params=self._get_photo_parmas(album.id, page)) photos = [] try: root = json.loads(xhr.to_json(response.text)) data = root['data'] photo_list = data['photoList'] for photo in photo_list: desc = photo.get('desc', '') time = photo.get('uploadtime', '') url = pic.convert(photo.get('url', '')) photos.append(Photo(desc, time, url)) except: print('提取相册 [%s] 第%d页的照片信息异常' % (album.name, page)) traceback.print_exc() return photos
def login(self, rsa_pwd, vcode, verify): ''' 登陆. ----------------- 登陆成功, 服务器响应: ptuiCB('0','0','https://ptlogin2.qzone.qq.com/check_sig?pttype=1&uin=272629724&service=login&nodirect=0&ptsigx=be9afd54dc7c9b05caf879056d01bff9520c147e19953b9577bf32a4a15b19f1cdfd7ceb17a27939d7596593032d4bcebfb57a4f58ae3ac6d9f078797ad04cd3&s_url=https%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&f_url=&ptlang=2052&ptredirect=100&aid=549000912&daid=5&j_later=0&low_login_hour=0®master=0&pt_login_type=1&pt_aid=0&pt_aaid=0&pt_light=0&pt_3rd_aid=0','0','登录成功!', 'EXP') 登陆失败, 服务器响应: ptuiCB('3','0','','0','你输入的帐号或密码不正确,请重新输入。', '') ptuiCB('4','0','','0','你输入的验证码不正确,请重新输入。', '') ptuiCB('7','0','','0','提交参数错误,请检查。(1552982056)', '') ptuiCB('24','0','','0','很遗憾,网络连接出现异常,请你检查是否禁用cookies。(1479543040)', '') :param rsa_pwd: RSA加密后的密码 :param vcode: 本次登陆的验证码 :param verify: 本次登陆的验证码的校验码 :return: 若登陆成功, 则返回可提取p_skey的回调地址 若登陆失败, 则返回失败原因(或回调函数) ''' print('正在登陆QQ [%s] ...' % self.QQ) params = { 'login_sig': self.cookie.sig, 'u': self.QQ, 'p': rsa_pwd, 'verifycode': vcode, 'pt_verifysession_v1': verify, 'pt_vcode_v1': '0' if self.is_falsu_vcode(vcode) else '1', 'from_ui': '1', # 重要参数 'pt_uistyle': '40', # 重要参数 'u1': 'https://qzs.qq.com/qzone/v5/loginsucc.html?para=izone', 'pt_randsalt': '2', 'aid': '549000912', 'daid': '5', 'ptredirect': '0', 'h': '1', 't': '1', 'g': '1', 'ptlang': '2052', 'js_ver': '10270', 'js_type': '1' } response = requests.get(url=cfg.XHR_LOGIN_URL, headers=xhr.get_headers(), params=params) rc = re.compile("'([^']*)'") groups = rc.findall(response.text) if len(groups) >= 6: if groups[0] == '0': xhr.take_response_cookies(response, self.cookie) self.cookie.nickName = groups[5] callback = groups[2] # 登陆成功: 提取p_skey的回调地址 else: callback = groups[4] # 登陆失败原因 else: callback = response.text # 登陆失败的回调函数 return callback
def take_vcode(self): ''' 提取登陆用的验证码. ----------------------------- 一般情况下, 不需要输入图片验证, 此时服务器的回调函数是: ptui_checkVC('0','!VAB','\x00\x00\x00\x00\x10\x3f\xff\xdc','cefb41782ce53f614e7665b5519f9858c80ab8925b8060d7a790802212da7205be1916ac4d45a77618c926c6a5fb330520b741d749519f33','2') 其中: 0 表示不需要验证码 !VAB 为伪验证码 cefb41782ce53f614e7665b5519f9858c80ab8925b8060d7a790802212da7205be1916ac4d45a77618c926c6a5fb330520b741d749519f33 则为验证码的校验码 ----------------------------- 但有时需要输入图片验证码(一般是输入了无效的QQ号导致的), 此时服务器的回调函数是: ptui_checkVC('1','FLQ8ymCigFmw30P7YaLP6iVCZHuyzjJWN2lH4M_OMFBndsUiMY9idQ**','\x00\x00\x00\x00\x00\x12\xd6\x87','','2') 其中: 1 表示需要验证码 FLQ8ymCigFmw30P7YaLP6iVCZHuyzjJWN2lH4M_OMFBndsUiMY9idQ** 是用于获取验证码图片的参数(随机生成) 然后代入参数访问以下地址得到验证码图片: https://ssl.captcha.qq.com/getimage?uin={QQ号}&cap_cd=FLQ8ymCigFmw30P7YaLP6iVCZHuyzjJWN2lH4M_OMFBndsUiMY9idQ** 同时该地址的Response Header中带有了该验证码的校验码: Set-Cookie:verifysession=h02iEMnHmjdBoYn7eDlj7AX37Lk7ORMFwJnJSlMufnESimC64Uqa2jz4gHI3ws5jlmiGq5Hg5lfs-2aMkVQ_Gu-vyR7aflns97t :return: 验证码:vcode, 校验码:verify ''' params = { 'u1': 'https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&from=iqq&r=0.7018623383003015&pt_uistyle=40', 'uin': self.QQ, 'login_sig': self.cookie.sig, 'pt_vcode': '1', 'regmaster': '', 'pt_tea': '2', 'appid': '549000912', 'js_ver': '10215', 'js_type': '1' } response = requests.get(url=cfg.VCODE_URL, headers=xhr.get_headers(), params=params) rc = re.compile("'([^']*)'") groups = rc.findall(response.text) if groups[0] == '0': vcode = groups[1] verify = groups[3] else: vcode, verify = self.take_vcode_by_image(groups[1]) print('已获得本次登陆的验证码: %s' % vcode) print('已获得本次登陆的校验码: %s' % verify) return vcode, verify
def take_gtk_and_token(self, callback_url): ''' 提取本次登陆的GTK与QzoneToken :param callback_url: 用于提取p_skey的回调地址(p_skey用于计算GTK, GTK用于获取QzoneToken) :return: True:提取成功; False:提取失败 ''' print('正在提取本次登陆的 GTK 与 QzoneToken ...') # 从登陆回调页面中提取p_skey, 并用之计算GTK(注意callbackURL是一个存在重定向页面, 且p_skey只存在于重定向前的页面) response = requests.get(callback_url, headers=xhr.get_headers(self.cookie.to_nv()), allow_redirects=False) # 关闭重定向 xhr.take_response_cookies(response, self.cookie) print('本次登陆的 GTK: %s' % self.cookie.gtk) # 从QQ空间首页的页面源码中提取QzoneToken response = requests.get(cfg.QZONE_HOMR_URL(self.QQ), headers=xhr.get_headers(self.cookie.to_nv())) self.cookie.qzone_token = self.get_qzone_token(response.text) print('本次登陆的 QzoneToken: %s' % self.cookie.qzone_token) return (not not self.cookie.gtk) and (not not self.cookie.qzone_token)
def download_mood(self, mood): ''' 下载单条说说及相关的照片 :param mood: 说说信息 :return: 成功下载的照片数 ''' headers = xhr.get_headers(self.cookie.to_nv()) headers['Referer'] = cfg.MOOD_REFERER cnt = 0 for idx, pic_url in enumerate(mood.pic_urls): pic_name = pic.get_pic_name(str(idx + 1), mood.content) is_ok = self.download_photo(headers, mood.page, pic_name, pic_url) cnt += (1 if is_ok else 0) print(' -> 下载照片进度(%s): %d/%d' % ('成功' if is_ok else '失败', cnt, mood.pic_num())) return cnt
def download_photo(self, album, photo): ''' 下载单张照片 :param album: 照片所属的相册信息 :param photo: 照片信息 :return: 是否下载成功 ''' headers = xhr.get_headers(self.cookie.to_nv()) save_path = '%s%s/%s' % (self.ALBUM_DIR, album.name, photo.name) is_ok = False for retry in range(cfg.RETRY) : is_ok, set_cookie = xhr.download_pic(photo.url, headers, '{}', save_path) if is_ok == True : break elif os.path.exists(save_path) : os.remove(save_path) return is_ok
def init_cookie_env(self): ''' 获取登陆前环境参数 :return: None(保存到cookie中) ''' params = { 'su' : self.base64_un, '_' : str(int(time.time() * 1000)), 'callback' : 'sinaSSOController.preloginCallBack', 'client' : 'ssologin.js(v1.4.18)', 'entry' : 'weibo', 'rsakt' : 'mod', 'checkpin' : '1' } response = requests.get(url=cfg.PRE_LOGIN_URL, headers=xhr.get_headers(), params=params) root = json.loads(xhr.to_json(response.text)) self.cookie.servertime = root.get('servertime', '') self.cookie.vcode_id = root.get('pcid', '') self.cookie.showpin = root.get('showpin', 0) self.cookie.nonce = root.get('nonce', '') self.cookie.pubkey = root.get('pubkey', '') self.cookie.rsakv = root.get('rsakv', '')
def take_vcode_by_image(self, vcode_id): ''' 下载验证码图片及其校验码, 同时返回人工输入的验证码 :param vcode_id: 用于下载验证码图片的ID :return: 验证码:vcode, 校验码:verify ''' params = {'uin': self.QQ, 'cap_cd': vcode_id} is_ok, set_cookie = xhr.download_pic(cfg.VCODE_IMG_URL, xhr.get_headers(), params, cfg.VCODE_PATH) if is_ok == True: self.cookie.add(set_cookie) verify = self.cookie.verifysession with Image.open(cfg.VCODE_PATH) as image: image.show() vcode = input('请输入验证码: ').strip() else: vcode = '' verify = '' return vcode, verify
def get_vcode(self, pcid): ''' 获取登陆图片验证码 :param pcid: 图片验证码ID :return: 图片验证码 ''' params = { 'r' : int(time.time()), 's' : '0', 'p' : pcid } is_ok, set_cookie = xhr.download_pic(pic_url=cfg.VCODE_URL, headers=xhr.get_headers(), params=params, save_path=cfg.VCODE_PATH) vcode = '' if is_ok == True : self.cookie.add(set_cookie) with Image.open(cfg.VCODE_PATH) as image: image.show() vcode = input("请输入图片验证码:").strip() return vcode
def get_page_photos(self, album, page): ''' 获取相册的分页照片信息 :param album: 相册信息 :param page: 页数 :return: 分页照片信息 ''' params = { 'uid' : self.sina_user_id, 'album_id' : album.id, '__rnd' : str(int(time.time() * 1000)), 'page' : str(page), 'count' : str(cfg.BATCH_LIMT), 'type' : str(album.type) } response = requests.get(url=cfg.PHOTO_LIST_URL, headers=xhr.get_headers(self.cookie.to_nv()), params=params) photos = [] try: root = json.loads(response.text) data = root['data'] photo_list = data['photo_list'] for photo in photo_list : desc = photo.get('caption_render', '') upload_time = photo.get('updated_at', '') pic_host = photo.get('pic_host', '') pic_name = photo.get('pic_name', '') url = cfg.PHOTO_URL(pic_host, pic_name) photos.append(Photo(desc, upload_time, url)) except: print('提取相册 [%s] 第%d页的照片信息异常' % (album.name, page)) traceback.print_exc() return photos