Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 74 additions & 81 deletions auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@

# Build-in / Std
import os, sys, time, platform, random
import re, json, cookielib
import re, json, http.cookiejar
from getpass import getpass

# requirements
import requests, termcolor


requests = requests.Session()
requests.cookies = cookielib.LWPCookieJar('cookies')
requests.cookies = http.cookiejar.LWPCookieJar('cookies')
try:
requests.cookies.load(ignore_discard=True)
except:
Expand All @@ -23,94 +22,102 @@ class Logging:
@staticmethod
def error(msg):
if Logging.flag == True:
print "".join( [ termcolor.colored("ERROR", "red"), ": ", termcolor.colored(msg, "white") ] )
print("".join([termcolor.colored("ERROR", "red"), ": ", termcolor.colored(msg, "white")]))
@staticmethod
def warn(msg):
if Logging.flag == True:
print "".join( [ termcolor.colored("WARN", "yellow"), ": ", termcolor.colored(msg, "white") ] )
print("".join([termcolor.colored("WARN", "yellow"), ": ", termcolor.colored(msg, "white")]))
@staticmethod
def info(msg):
# attrs=['reverse', 'blink']
if Logging.flag == True:
print "".join( [ termcolor.colored("INFO", "magenta"), ": ", termcolor.colored(msg, "white") ] )
print("".join([termcolor.colored("INFO", "magenta"), ": ", termcolor.colored(msg, "white")]))
@staticmethod
def debug(msg):
if Logging.flag == True:
print "".join( [ termcolor.colored("DEBUG", "magenta"), ": ", termcolor.colored(msg, "white") ] )
print("".join([termcolor.colored("DEBUG", "magenta"), ": ", termcolor.colored(msg, "white")]))
@staticmethod
def success(msg):
if Logging.flag == True:
print "".join( [ termcolor.colored("SUCCES", "green"), ": ", termcolor.colored(msg, "white") ] )
print("".join([termcolor.colored("SUCCES", "green"), ": ", termcolor.colored(msg, "white")]))

# Setting Logging
Logging.flag = True

class LoginPasswordError(Exception):
def __init__(self, message):
if type(message) != type("") or message == "": self.message = u"帐号密码错误"
else: self.message = message
if not isinstance(message, str) or message == "":
self.message = "帐号密码错误"
else:
self.message = message
Logging.error(self.message)

class NetworkError(Exception):
def __init__(self, message):
if type(message) != type("") or message == "": self.message = u"网络异常"
else: self.message = message
if not isinstance(message, str) or message == "":
self.message = "网络异常"
else:
self.message = message
Logging.error(self.message)

class AccountError(Exception):
def __init__(self, message):
if type(message) != type("") or message == "": self.message = u"帐号类型错误"
else: self.message = message
if not isinstance(message, str) or message == "":
self.message = "帐号类型错误"
else:
self.message = message
Logging.error(self.message)





def download_captcha():
url = "https://www.zhihu.com/captcha.gif"
r = requests.get(url, params={"r": random.random(), "type": "login"}, verify=False)
if int(r.status_code) != 200:
raise NetworkError(u"验证码请求失败")
image_name = u"verify." + r.headers['content-type'].split("/")[1]
open( image_name, "wb").write(r.content)
"""
System platform: https://docs.python.org/2/library/platform.html
"""
Logging.info(u"正在调用外部程序渲染验证码 ... ")
raise NetworkError("验证码请求失败")
image_name = "verify." + r.headers['content-type'].split("/")[1]
open(image_name, "wb").write(r.content)

Logging.info("正在调用外部程序渲染验证码 ... ")
if platform.system() == "Linux":
Logging.info(u"Command: xdg-open %s &" % image_name )
os.system("xdg-open %s &" % image_name )
Logging.info("Command: xdg-open %s &" % image_name)
os.system("xdg-open %s &" % image_name)
elif platform.system() == "Darwin":
Logging.info(u"Command: open %s &" % image_name )
os.system("open %s &" % image_name )
Logging.info("Command: open %s &" % image_name)
os.system("open %s &" % image_name)
elif platform.system() in ("SunOS", "FreeBSD", "Unix", "OpenBSD", "NetBSD"):
os.system("open %s &" % image_name )
os.system("open %s &" % image_name)
elif platform.system() == "Windows":
os.system("%s" % image_name )
os.system("%s" % image_name)
else:
Logging.info(u"我们无法探测你的作业系统,请自行打开验证码 %s 文件,并输入验证码。" % os.path.join(os.getcwd(), image_name) )
Logging.info("我们无法探测你的作业系统,请自行打开验证码 %s 文件,并输入验证码。" % os.path.join(os.getcwd(), image_name))

sys.stdout.write(termcolor.colored(u"请输入验证码: ", "cyan") )
captcha_code = raw_input( )
sys.stdout.write(termcolor.colored("请输入验证码: ", "cyan"))
captcha_code = input()
return captcha_code

def search_xsrf():
url = "http://www.zhihu.com/"
r = requests.get(url, verify=False)
if int(r.status_code) != 200:
raise NetworkError(u"验证码请求失败")
results = re.compile(r"\<input\stype=\"hidden\"\sname=\"_xsrf\"\svalue=\"(\S+)\"", re.DOTALL).findall(r.text)
url = "https://www.zhihu.com/"
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
'Referer': "https://www.zhihu.com/"
}
r = requests.get(url, headers=headers, verify=False)
print("Status Code:", r.status_code) # 打印状态码
print("Response Text:", r.text[:100]) # 打印部分响应内容
if r.status_code != 200:
raise NetworkError("验证码请求失败")
results = re.findall(r'<input type="hidden" name="_xsrf" value="(\S+)"', r.text)
if len(results) < 1:
Logging.info(u"提取XSRF 代码失败" )
Logging.info("提取XSRF 代码失败")
return None
return results[0]


def build_form(account, password):
if re.match(r"^1\d{10}$", account): account_type = "phone_num"
elif re.match(r"^\S+\@\S+\.\S+$", account): account_type = "email"
else: raise AccountError(u"帐号类型错误")
else: raise AccountError("帐号类型错误")

form = {account_type: account, "password": password, "remember_me": True }
form = {account_type: account, "password": password, "remember_me": True}

form['_xsrf'] = search_xsrf()
form['captcha'] = download_captcha()
Expand All @@ -122,7 +129,7 @@ def upload_form(form):
elif "phone_num" in form:
url = "https://www.zhihu.com/login/phone_num"
else:
raise ValueError(u"账号类型错误")
raise ValueError("账号类型错误")

headers = {
'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36",
Expand All @@ -135,30 +142,28 @@ def upload_form(form):

r = requests.post(url, data=form, headers=headers, verify=False)
if int(r.status_code) != 200:
raise NetworkError(u"表单上传失败!")
raise NetworkError("表单上传失败!")

if r.headers['content-type'].lower() == "application/json":
try:
# 修正 justkg 提出的问题: https://github.com/egrcc/zhihu-python/issues/30
result = json.loads(r.content)
result = json.loads(r.content.decode('utf-8'))
except Exception as e:
Logging.error(u"JSON解析失败!")
Logging.error("JSON解析失败!")
Logging.debug(e)
Logging.debug(r.content)
result = {}
if result["r"] == 0:
Logging.success(u"登录成功!" )
Logging.success("登录成功!")
return {"result": True}
elif result["r"] == 1:
Logging.success(u"登录失败!" )
return {"error": {"code": int(result['errcode']), "message": result['msg'], "data": result['data'] } }
Logging.success("登录失败!")
return {"error": {"code": int(result['errcode']), "message": result['msg'], "data": result['data']}}
else:
Logging.warn(u"表单上传出现未知错误: \n \t %s )" % ( str(result) ) )
return {"error": {"code": -1, "message": u"unknown error"} }
Logging.warn("表单上传出现未知错误: \n \t %s )" % (str(result)))
return {"error": {"code": -1, "message": "unknown error"}}
else:
Logging.warn(u"无法解析服务器的响应内容: \n \t %s " % r.text )
return {"error": {"code": -2, "message": u"parse error"} }

Logging.warn("无法解析服务器的响应内容: \n \t %s " % r.text)
return {"error": {"code": -2, "message": "parse error"}}

def islogin():
# check session
Expand All @@ -171,68 +176,56 @@ def islogin():
elif status_code == 200:
return True
else:
Logging.warn(u"网络故障")
Logging.warn("网络故障")
return None


def read_account_from_config_file(config_file="config.ini"):
# NOTE: The ConfigParser module has been renamed to configparser in Python 3.
# The 2to3 tool will automatically adapt imports when converting your sources to Python 3.
# https://docs.python.org/2/library/configparser.html
from ConfigParser import ConfigParser
from configparser import ConfigParser
cf = ConfigParser()
if os.path.exists(config_file) and os.path.isfile(config_file):
Logging.info(u"正在加载配置文件 ...")
Logging.info("正在加载配置文件 ...")
cf.read(config_file)

email = cf.get("info", "email")
password = cf.get("info", "password")
if email == "" or password == "":
Logging.warn(u"帐号信息无效")
Logging.warn("帐号信息无效")
return (None, None)
else: return (email, password)
else:
return (email, password)
else:
Logging.error(u"配置文件加载失败!")
Logging.error("配置文件加载失败!")
return (None, None)




def login(account=None, password=None):
if islogin() == True:
Logging.success(u"你已经登录过咯")
Logging.success("你已经登录过咯")
return True

if account == None:
(account, password) = read_account_from_config_file()
if account == None:
sys.stdout.write(u"请输入登录账号: ")
account = raw_input()
sys.stdout.write("请输入登录账号: ")
account = input()
password = getpass("请输入登录密码: ")

form_data = build_form(account, password)
"""
result:
{"result": True}
{"error": {"code": 19855555, "message": "unknown.", "data": "data" } }
{"error": {"code": -1, "message": u"unknown error"} }
"""
result = upload_form(form_data)
if "error" in result:
if result["error"]['code'] == 1991829:
# 验证码错误
Logging.error(u"验证码输入错误,请准备重新输入。" )
Logging.error("验证码输入错误,请准备重新输入。")
return login()
elif result["error"]['code'] == 100005:
# 密码错误
Logging.error(u"密码输入错误,请准备重新输入。" )
Logging.error("密码输入错误,请准备重新输入。")
return login()
else:
Logging.warn(u"unknown error." )
Logging.warn("unknown error.")
return False
elif "result" in result and result['result'] == True:
# 登录成功
Logging.success(u"登录成功!" )
Logging.success("登录成功!")
requests.cookies.save()
return True

Expand Down