diff --git a/.DS_Store b/.DS_Store index d66f50f..ac77640 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/Github/.DS_Store b/Github/.DS_Store new file mode 100644 index 0000000..55cc84b Binary files /dev/null and b/Github/.DS_Store differ diff --git a/Github/github_login.py b/Github/github_login.py new file mode 100644 index 0000000..045f7c0 --- /dev/null +++ b/Github/github_login.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import requests +from lxml import etree +try: + import cookielib +except: + import http.cookiejar as cookielib + + +class GithubLogin(object): + + def __init__(self): + self.headers = { + 'Referer': 'https://github.com/', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36', + 'Host': 'github.com' + } + self.login_url ='https://github.com/login' + self.post_url = 'https://github.com/session' + self.logined_url = 'https://github.com/settings/profile' + + self.session = requests.session() + self.session.cookies = cookielib.LWPCookieJar(filename='github_cookie') + + def load_cookie(self): + try: + self.session.cookies.load(ignore_discard=True) + except: + print('cookie 不成功') + + def get_param(self): + response = self.session.get(self.login_url, headers=self.headers) + selector = etree.HTML(response.text) + field_one = selector.xpath('//div/input[2]/@value') + print(field_one) + return field_one + pass + + + def post_param(self, email, password): + post_data = { + 'commit': 'Sign in', + 'utf8': '✓', + 'authenticity_token': self.get_param()[0], + 'login': email, + 'password': password + } + response = self.session.post(self.post_url, data=post_data, headers=self.headers) + self.session.cookies.save() + + pass + + + def bool_login(self): + self.load_cookie() + response = self.session.get(self.logined_url, headers=self.headers) + selector = etree.HTML(response.text) + flag = selector.xpath('//div[@class="column two-thirds"]/dl/dt/label/text()') + print(u'个人设置Profile包括: %s'%flag) + pass + +if __name__ == "__main__": + Github = GithubLogin() + Github.post_param(email='******', password='******') + # Github.bool_login() \ No newline at end of file diff --git a/LICENSE b/LICENSE index cc6ebec..86b1e04 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License -Copyright (c) 2016-2019 CriseLYJ. +Copyright (c) 2018 CriseLYJ. +https://github.com/CriseLYJ/awesome-python-login-model Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +10,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 2180317..b41b95f 100755 --- a/README.md +++ b/README.md @@ -1,36 +1,91 @@ +

Website_login_mode

+ +
+

+ Master +

+ +
+ +

"Did you know all your doors were locked?" - Riddick (The Chronicles of Riddick)

+ +
+
+ Created by + CriseLYJ +
+ +
+ +**** + # Website_login_mode -各大网站登陆方式,有的是通过selenium登录,有的是通过抓包直接模拟登录,希望对小白有所帮助,本项目用于研究和分享各大网站的模拟登陆方式,主要使用selenium+phantomjs或者直接登录的方式,语言采用Python +收集了一些各大网站登陆方式,有的是通过selenium登录,有的是通过抓包直接模拟登录,希望对小白有所帮助,本项目用于研究和分享各大网站的模拟登陆方式。 + +## 模拟登录一些常见的网站 +主要基于以下的 Python 的第三 library + +1. requests +2. selenium +3. rsa +4. phantomjs -## 关于 - -由于工作需要,研究了一段时间的新浪微博登陆方式,在网上也查看了很多别人的经验,但是有相当一部分都是转载而且代码老旧,所以便有了这个repo。 +## About 模拟登陆基本采用的是直接登录或者使用selenium+webdriver的方式,有的网站直接登录难度很大,比如qq空间,如果采用selenium就相对轻松一些。 虽然在登录的时候采用的是selenium,为了效率,我们可以在登录过后得到的cookie维护起来,然后调用requests或者scrapy等进行数据采集,这样数据采集的速度可以得到保证。 -下面是已经实现的目标 +## Done -- [x] Facebook -- [x] Twitter -- [x] Weibo -- [x] 知乎 -- [x] QQZone -- [x] CSDN -- [x] 淘宝 -- [x] Baidu -- [x] 果壳 -- [x] JingDong -- [x] 163main -- [x] 拉钩 +- [x] [Facebook](https://www.facebook.com/) +- [x] [Twitter](https://twitter.com/) +- [x] [微博网页版](http://weibo.com) +- [x] [知乎](http://zhihu.com) +- [x] [QQZone](https://qzone.qq.com/) +- [x] [CSDN](https://www.csdn.net/) +- [x] [淘宝](www.taobao.com) +- [x] [Baidu](www.baidu.com) +- [x] [果壳](https://www.guokr.com/) +- [x] [JingDong](https://www.jd.com/) +- [x] [163mail](https://mail.163.com/) +- [x] [拉钩](https://www.lagou.com/) +- [x] [Bilibili](https://www.bilibili.com/) +- [x] [豆瓣](https://www.douban.com/) +- [x] [V2EX](https://www.v2ex.com/) +- [x] [Baidu2](www.baidu.com) +- [x] [猎聘网](https://www.liepin.com/) +- [x] [微信网页版](https://wx.qq.com/) +- [x] [gihub](https://github.com/) +- [x] [图虫](https://tuchong.com/) +## tips of pull request -## 常见问题 +- 欢迎大家一起来 pull request + +## Problems - 关于验证码:本项目所用的方法都没有处理验证码,识别复杂验证码的难度就目前来说,还是比较大的。以我的心得来说,做爬虫最好的方式就是尽量规避验证码。 - 代码失效:由于网站策略或者样式改变,导致代码失效,请给我提issue,如果你已经解决,可以提PR,谢谢! -## 其它 +## Another - 如果你有什么比较难登陆的网站,比如发现用了selenium+webdriver都还登陆不了的网站,欢迎给我提issue - 如果该repo对大家有帮助,给个star鼓励鼓励吧 + +## something to add + +1. 项目写了一段时间后,发现代码的风格和程序的易用性,可扩展性,代码的可读性,都存在一定的问题,所以接下来最重要的是重构代码,让大家可以更容易的做出一些自己的小功能。 +2. 如果你觉得某个网站的登录很有代表性,欢迎在 issue 中提出 +3. 如果网站的登录很有意思,我会在后面的更新中加入 +4. 网站的登录机制有可能经常的变动,所以当现在的模拟的登录的规则不能使用的时候,请在 issue 中提出 +- 如果关注量大的话,我还是会不断维护此仓库带来更多的东西,并且重构代码! + +## Acknowledgement +- Thanks for all! + +## Written at the end +- I need your support. +- And I think you can give me a ``star``! + diff --git a/baidu2/baidu.py b/baidu2/baidu.py new file mode 100644 index 0000000..31aaaf0 --- /dev/null +++ b/baidu2/baidu.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +''' +Required +- requests (必须) +- rsa (必须) +''' +import requests +import math +import random +import rsa +# import binascii +# import os +import base64 +import time + + +""" +1. 百度的模拟登录,请确保你的账号能在浏览器下登录不需要验证码 + 百度的三次登录错误后,才会出现验证码,但是还有一种情况是百度的账号异常 + +2. 这是一个初级的版本的,以后会对代码整理完善,并且统一代码风格 + +几个重要的参数 + +-- gid:js 代码构造 不需要 http 请求 + +-- token:需要 https://passport.baidu.com/v2/api/? 加上参数 +但是不能使用 requests 的 params 参数 因为顺序不同的话,访问出问题 + +-- pubkey 和 rsakey +需要 http 请求获得 +还有 RSA 的加密 以及 加密后的 base64 字符串化 + +-- callback 的 参数是动态变化的 + +post login 时候 还有 ppui_logintime 时间间隔参数 + +我是通过随机访问了一个贴吧的链接 查查是否有我的用户名 来判断是否登录成功的 +if there are still questions you encounter, feel free to contact by email + +""" + + +# gid 在同一个登录的 session 相同 +def get_gid(): + gid = "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + gid = list(gid) + for xy in range(len(gid)): + if gid[xy] in "xy": + r = int(random.random()*16) + if gid[xy] == "x": + gid[xy] = hex(r).replace("0x", '').upper() + else: + gid[xy] = hex(r & 3 | 8).replace("0x", '').upper() + else: + pass + return ''.join(gid) + + +# 每次都不同 +def get_callback(): + loopabc = '0123456789abcdefghijklmnopqrstuvwxyz' + prefix = "bd__cbs__" + n = math.floor(random.random() * 2147483648) + a = [] + while n != 0: + a.append(loopabc[n % 36]) + n = n // 36 + a.reverse() + callback = prefix + ''.join(a) + return callback + + +# 密码加密 +def get_password(password_input, pubkey): + pub = rsa.PublicKey.load_pkcs1_openssl_pem(pubkey.encode("utf-8")) + password_input = password_input.encode("utf-8") + psword = rsa.encrypt(password_input, pub) + psword = base64.b64encode(psword) + return psword.decode("utf-8") + + +# 构造 Request headers + +agent = 'Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0' +headers = { + 'User-Agent': agent, + "Host": "passport.baidu.com", + "Referer": "https://www.baidu.com/" +} +gid = get_gid() +session = requests.session() +# 访问登录页面的初始页面,然后这次访问会话带上 cookies +session.get("https://passport.baidu.com/v2/?login", headers=headers) + + +# 同一个登录下 token 是唯一的 +def get_token(): + # 此处必须是自己构造 url 不能采用 params 的参数, params 参数的顺序是变化的 + token_callback = get_callback() + global init_time + init_time = str(int(time.time() * 1000)) + token_url = "https://passport.baidu.com/v2/api/?getapi&tpl=pp&apiver=v3&tt=" + token_url = token_url + init_time + "&class=login&gid=" + token_url = token_url + gid + "&logintype=basicLogin&callback=" + token_url = token_url + token_callback + # token_params = { + # "getapi": "", + # "tpl": "pp", + # "apiver": "v3", + # "tt": str(int(time.time() * 1000)), + # "class": "login", + # "gid": gid, + # "logintype": "dialogLogin", + # "callback": get_callback() + # } + token_html = session.get(token_url, headers=headers) + token_content_all = token_html.text.replace(token_callback, "") + token_content_all = eval(token_content_all) + # print(token_content_all) + return token_content_all['data']['token'] + +token = (get_token()) + + +def get_publickey(token): + publickey_callback = get_callback() + publickey_url = "https://passport.baidu.com/v2/getpublickey?token=" + publickey_url = publickey_url + token + "&tpl=pp&apiver=v3&tt=" + publickey_url = publickey_url + str(int(time.time() * 1000)) + "&gid=" + publickey_url = publickey_url + gid + "&callback=" + publickey_url = publickey_url + publickey_callback + # print(publickey_url) + + headers["Referer"] = "https://passport.baidu.com/v2/?login" + publickey_html = session.get(publickey_url, headers=headers) + publickey_content_all = eval(publickey_html.text.replace(publickey_callback, "")) + + return publickey_content_all['pubkey'], publickey_content_all['key'] + + +# print(get_publickey(token)) + +pubkey, key = get_publickey(token) + +# 随机停顿 几秒 模拟真实的浏览情况 +time.sleep(random.randint(2, 5)) + + +def login(username, password, key): + login_url = "https://passport.baidu.com/v2/api/?login" + login_time = str(int(time.time() * 1000)) + login_callback = get_callback() + login_postdata = { + "staticpage": "https://passport.baidu.com/static/passpc-account/html/v3Jump.html", + "charset": "UTF-8", + "token": token, + "tpl": "pp", + "subpro": "", + "apiver": "v3", + "tt": login_time, + "codestring": "", + "safeflg": "0", + "u": "https://passport.baidu.com/", + "isPhone": "", + "detect": "1", + "gid": gid, + "quick_user": "0", + "logintype": "basicLogin", + "logLoginType": "pc_loginBasic", + "idc": "", + "loginmerge": "true", + "username": username, + "password": password, + "verifycode": "", + "mem_pass": "on", + "rsakey": key, + "crypttype": "12", + "ppui_logintime": str(int(login_time) - int(init_time)), + "countrycode": "", + "callback": login_callback, + } + + login_html = session.post(login_url, data=login_postdata, headers=headers) + print(login_html.cookies) + # html_index = session.get('贴吧的任何一个帖子的地址,判断是否登录成功') + # with open("tieba.html", 'wb') as f: + # f.write(html_index.content) + # f.close() + +try: + input = raw_input +except: + pass + + +if __name__ == "__main__": + username = input("请输入你的手机号或者邮箱\n >:") + secret = input("请输入你的密码\n >:") + login(username, get_password(secret, pubkey), key) diff --git a/bilibili/.DS_Store b/bilibili/.DS_Store new file mode 100644 index 0000000..18b5317 Binary files /dev/null and b/bilibili/.DS_Store differ diff --git a/bilibili/bilibili.py b/bilibili/bilibili.py new file mode 100644 index 0000000..f9eeeb4 --- /dev/null +++ b/bilibili/bilibili.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +''' +Required +- requests (必须) +- pillow (可选) +''' + +import requests +import re +import time +import sys +import json +import rsa +import os.path +import binascii +import datetime +from bs4 import BeautifulSoup +try: + import cookielib +except: + import http.cookiejar as cookielib + +try: + from PIL import Image +except: + pass + +session = requests.Session() +session.headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0', + 'Connection': 'keep-alive', + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', +} + +# 使用登录cookie信息 +session = requests.session() +session.cookies = cookielib.LWPCookieJar(filename='cookies') +try: + session.cookies.load(ignore_discard=True) +except: + print("Cookie 未能加载") + +def rsaEncrypt(password): + url = 'http://passport.bilibili.com/login?act=getkey' + try: + getKeyRes = session.get(url) + token = json.loads(getKeyRes.content.decode('utf-8')) + pw = str(token['hash'] + password).encode('utf-8') + + key = token['key'] + key = rsa.PublicKey.load_pkcs1_openssl_pem(key) + + pw = rsa.encrypt(pw, key) + password = binascii.b2a_base64(pw) + return password + except: + return False + +def get_vdcode(): + t = str(int(time.time()*1000)) + captcha_url = 'https://passport.bilibili.com/captcha.gif?r=' + t + "&type=login" + r = session.get(captcha_url) + with open('captcha.jpg', 'wb') as f: + f.write(r.content) + f.close() + # 用pillow 的 Image 显示验证码 + # 如果没有安装 pillow 到源代码所在的目录去找到验证码然后手动输入 + try: + im = Image.open('captcha.jpg') + im.show() + im.close() + except: + print(u'请到 %s 目录找到captcha.jpg 手动输入' % os.path.abspath('captcha.jpg')) + captcha = input("please input the captcha\n>") + return captcha + +def login(user, password): + post_url = 'https://passport.bilibili.com/login/dologin' + payload = { + 'act': 'login', + 'gourl': '', + 'keeptime': '2592000', + 'userid': user, + 'pwd': password, + 'vdcode': get_vdcode(), + } + if payload["vdcode"] == None: + return False + + try: + resp = session.post(post_url, data=payload) + session.cookies.save() + + soup = BeautifulSoup(resp.content, 'lxml') + s = str(soup.select('center')[0]) + s = s.replace('\n', '') + s = s.replace('\r', '') + s = s.replace(' ', '') + s = s.split('>') + s = s[2] + s = s.replace('
img')[0]['src'] + authcodeUrl = 'https://passport.liepin.com{}'.format(imageUrl) + response = self.session.get(authcodeUrl) + if response.status_code == 200: + with open(self.Dir, 'wb') as f: + f.write(response.content) + authcode = input('plz input authcode:') + return authcode + + def login(self): + payload ={ + 'user_login':self.username, + 'isMd5':1, + 'user_pwd': self._md5(), + 'user_kind':2, #根据你是否为正式会员而定,根据自身情况可能需要修改 + 'verifycode':self._getAuthcode(), + 'url':'', + } + del self.headers['Upgrade-Insecure-Requests'] + self.headers['Origin'] = 'https://passport.liepin.com' + self.headers['Referer']='https://passport.liepin.com/h/account' + response = self.session.post(self.loginUrl, headers=self.headers, data = payload, timeout =10, proxies = self.proxies ,allow_redirects =False) + return response.status_code, response.text + +if __name__ == '__main__': + + userName = input("plz input username:") + password = input("plz input password:") + lp = Leipin(userName,password) + print (lp.login()) diff --git a/qqzone_login/login_by_selenium.py b/qqzone/login_by_selenium.py similarity index 100% rename from qqzone_login/login_by_selenium.py rename to qqzone/login_by_selenium.py diff --git a/sina_login/sina_login_by_selenium.py b/sina/sina_login_by_selenium.py similarity index 100% rename from sina_login/sina_login_by_selenium.py rename to sina/sina_login_by_selenium.py diff --git a/sina_login/sina_login_direct.py b/sina/sina_login_direct.py similarity index 100% rename from sina_login/sina_login_direct.py rename to sina/sina_login_direct.py diff --git a/sina_login/sina_login_phone.py b/sina/sina_login_phone.py similarity index 100% rename from sina_login/sina_login_phone.py rename to sina/sina_login_phone.py diff --git a/sina_login/sina_login_pyexecjs.py b/sina/sina_login_pyexecjs.py similarity index 100% rename from sina_login/sina_login_pyexecjs.py rename to sina/sina_login_pyexecjs.py diff --git a/sina_login/sinalogin.js b/sina/sinalogin.js similarity index 100% rename from sina_login/sinalogin.js rename to sina/sinalogin.js diff --git a/taobao_login/taobao.py b/taobao/taobao.py similarity index 100% rename from taobao_login/taobao.py rename to taobao/taobao.py diff --git a/tuchong/tuchong.py b/tuchong/tuchong.py new file mode 100644 index 0000000..1234c05 --- /dev/null +++ b/tuchong/tuchong.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +''' +Required +- requests (必须) +''' + +import rsa +import binascii +import requests +from base64 import b64decode +import sys +reload(sys) +sys.setdefaultencoding('utf8') + + +agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36" + +#pubkey 在页面的js中: http://static.tuchong.net/js/pc/page/welcome_6e7f1cd.js +pubkey = "D8CC0180AFCC72C9F5981BDB90A27928672F1D6EA8A57AF44EFFA7DAF6EFB17DAD9F643B9F9F7A1F05ACC2FEA8DE19F023200EFEE9224104627F1E680CE8F025AF44824A45EA4DDC321672D2DEAA91DB27418CFDD776848F27A76E747D53966683EFB00F7485F3ECF68365F5C10C69969AE3D665162D2EE3A5BA109D7DF6C7A5" +session = requests.session() + + + +def get_crypt_password(message): + rsaPublickey = int(pubkey, 16) + key = rsa.PublicKey(rsaPublickey, 65537) + passwd = rsa.encrypt(message, key) + passwd = binascii.b2a_hex(passwd) + return passwd + +def get_captcha(): + captcha_url="https://tuchong.com/rest/captcha/image" + headers={ + 'user-agent': agent + } + rsp=session.post(captcha_url, data=None, headers=headers).json() + captcha_id=rsp['captchaId'] + captcha_base64=rsp['captchaBase64'] + captcha_base64=captcha_base64.replace("data:image/png;base64,","") + with open("captcha.png",'w') as f: + f.write(b64decode(captcha_base64)) + captcha=input(u'输入当前目录下 captcha.png 上的验证码:') + return captcha_id,captcha + + +def login(username,passwd): + login_url = 'https://tuchong.com/rest/accounts/login' + headers={ + 'user-agent': agent + } + + passwd_crypt=get_crypt_password(passwd) + postdata = { + 'account': username, + 'password': passwd_crypt, + } + + rsp = session.post(login_url, data=postdata, headers=headers) + rsp=rsp.json() + print(rsp) + #登录成功 + if rsp.has_key('result') and rsp['result']=="SUCCESS": + print(rsp['message']) + return + + #登录失败 + if rsp.has_key('code') and rsp.has_key('message'): + print("response code:%d, message:%s"%(rsp['code'],rsp['message'])) + if rsp['message'].find("验证码")>=0 : + print(rsp['message']) + captcha=get_captcha() + postdata={ + 'account': username, + 'password': passwd_crypt, + 'captcha_id': captcha[0], + 'captcha_token': int(captcha[1]) + } + rsp = session.post(login_url, data=postdata, headers=headers) + if str(rsp).find('200'): + print("登陆成功!") + + +if __name__ == '__main__': + username=raw_input(u'用户名:') + passwd=raw_input(u'密码:') + login(username,passwd) \ No newline at end of file diff --git a/twitter_login/twitter.py b/twitter/twitter.py similarity index 100% rename from twitter_login/twitter.py rename to twitter/twitter.py diff --git a/v2ex/v2ex.py b/v2ex/v2ex.py new file mode 100644 index 0000000..f3f6a53 --- /dev/null +++ b/v2ex/v2ex.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# coding: utf-8 +# v2ex.py + + +''' +Required +- requests +- lxml +''' + + +import requests +from io import StringIO +from lxml import etree + + +class V2EX: + login_url = 'https://www.v2ex.com/signin' + + + def __init__(self, user_name, password): + self.user_name = user_name + self.password = password + + session = requests.Session() + session.headers = { + 'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50" + } + self.session = session + + + def get_login_parameters(self): + response = self.session.get(self.login_url) + + parser = etree.HTMLParser() + tree = etree.parse(StringIO(response.text), parser) + elements = tree.xpath('//form[@method="post" and @action="/signin"]/table[@cellpadding="5" and @cellspacing="0" and @border="0" and @width="100%"]//tr[position()>> window.QRLogin.code = 200; window.QRLogin.uuid = "oZwt_bFfRg=="; + + regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"' + pm = re.search(regx, data) + + code = pm.group(1) + uuid = pm.group(2) + + if code == '200': + return True + + return False + + +def showQRImage(): + global tip + + url = 'https://login.weixin.qq.com/qrcode/' + uuid + params = { + 't': 'webwx', + '_': int(time.time()), + } + + response = session.get(url, params=params) + + tip = 1 + + with open(QRImgPath, 'wb') as f: + f.write(response.content) + f.close() + + if sys.platform.find('darwin') >= 0: + subprocess.call(['open', QRImgPath]) + elif sys.platform.find('linux') >= 0: + subprocess.call(['xdg-open', QRImgPath]) + else: + os.startfile(QRImgPath) + + print('请使用微信扫描二维码以登录') + + +def waitForLogin(): + global tip, base_uri, redirect_uri + + url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % ( + tip, uuid, int(time.time())) + + response = session.get(url) + data = response.content.decode('utf-8') + + # print(data) + + # window.code=500; + regx = r'window.code=(\d+);' + pm = re.search(regx, data) + + code = pm.group(1) + + if code == '201': # 已扫描 + print('成功扫描,请在手机上点击确认以登录') + tip = 0 + elif code == '200': # 已登录 + print('正在登录...') + regx = r'window.redirect_uri="(\S+?)";' + pm = re.search(regx, data) + redirect_uri = pm.group(1) + '&fun=new' + base_uri = redirect_uri[:redirect_uri.rfind('/')] + + # closeQRImage + if sys.platform.find('darwin') >= 0: # for OSX with Preview + os.system("osascript -e 'quit app \"Preview\"'") + elif code == '408': # 超时 + pass + # elif code == '400' or code == '500': + + return code + + +def login(): + global skey, wxsid, wxuin, pass_ticket, BaseRequest + + response = session.get(redirect_uri) + data = response.content.decode('utf-8') + + # print(data) + + ''' + + 0 + OK + xxx + xxx + xxx + xxx + 1 + + ''' + xml.dom + doc = xml.dom.minidom.parseString(data) + root = doc.documentElement + + for node in root.childNodes: + if node.nodeName == 'skey': + skey = node.childNodes[0].data + elif node.nodeName == 'wxsid': + wxsid = node.childNodes[0].data + elif node.nodeName == 'wxuin': + wxuin = node.childNodes[0].data + elif node.nodeName == 'pass_ticket': + pass_ticket = node.childNodes[0].data + + # print('skey: %s, wxsid: %s, wxuin: %s, pass_ticket: %s' % (skey, wxsid, + # wxuin, pass_ticket)) + + if not all((skey, wxsid, wxuin, pass_ticket)): + return False + + BaseRequest = { + 'Uin': int(wxuin), + 'Sid': wxsid, + 'Skey': skey, + 'DeviceID': deviceId, + } + + return True + + +def webwxinit(): + + url = base_uri + \ + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % ( + pass_ticket, skey, int(time.time())) + params = { + 'BaseRequest': BaseRequest + } + + h = headers + h['ContentType'] = 'application/json; charset=UTF-8' + response = session.post(url, data=json.dumps(params), headers=h) + data = response.content.decode('utf-8') + #print(data) + + global ContactList, My, SyncKey + + dic = json.loads(data) + ContactList = dic['ContactList'] + My = dic['User'] + + SyncKeyList = [] + for item in dic['SyncKey']['List']: + SyncKeyList.append('%s_%s' % (item['Key'], item['Val'])) + SyncKey = '|'.join(SyncKeyList) + + ErrMsg = dic['BaseResponse']['ErrMsg'] + + Ret = dic['BaseResponse']['Ret'] + if Ret != 0: + return False + + return True + + +def webwxgetcontact(): + + url = base_uri + \ + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % ( + pass_ticket, skey, int(time.time())) + + h = headers + h['ContentType'] = 'application/json; charset=UTF-8' + response = session.get(url, headers=h) + 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'] == My['UserName']: # 自己 + MemberList.remove(Member) + + return MemberList + +def main(): + + if not getUUID(): + print('获取uuid失败') + return + + showQRImage() + time.sleep(1) + + while waitForLogin() != '200': + pass + + os.remove(QRImgPath) + + if not login(): + print('登录失败') + return + #登录完成, 下面查询好友 + if not webwxinit(): + print('初始化失败') + return + + MemberList = 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'])) + + + + + +if __name__ == '__main__': + print('开始') + main() diff --git a/zhihu_login/do_login.py b/zhihu/do_login.py similarity index 100% rename from zhihu_login/do_login.py rename to zhihu/do_login.py