1+ #!/usr/bin/env python
2+ #-*- coding:utf-8 -*-
3+
4+ # Build-in / Std
5+ import os , sys , time , platform , random
6+ import re , json , cookielib
7+
8+ # requirements
9+ import requests , termcolor
10+
11+ try :
12+ import BeautifulSoup
13+ except :
14+ from bs4 import BeautifulSoup
15+
16+ requests = requests .Session ()
17+ requests .cookies = cookielib .LWPCookieJar ('cookies' )
18+ try :
19+ requests .cookies .load (ignore_discard = True )
20+ except :
21+ pass
22+
23+ class Logging :
24+ flag = True
25+
26+ @staticmethod
27+ def error (msg ):
28+ if Logging .flag == True :
29+ print "" .join ( [ termcolor .colored ("ERROR" , "red" ), ": " , termcolor .colored (msg , "white" ) ] )
30+ @staticmethod
31+ def warn (msg ):
32+ if Logging .flag == True :
33+ print "" .join ( [ termcolor .colored ("WARN" , "yellow" ), ": " , termcolor .colored (msg , "white" ) ] )
34+ @staticmethod
35+ def info (msg ):
36+ # attrs=['reverse', 'blink']
37+ if Logging .flag == True :
38+ print "" .join ( [ termcolor .colored ("INFO" , "magenta" ), ": " , termcolor .colored (msg , "white" ) ] )
39+ @staticmethod
40+ def debug (msg ):
41+ if Logging .flag == True :
42+ print "" .join ( [ termcolor .colored ("DEBUG" , "magenta" ), ": " , termcolor .colored (msg , "white" ) ] )
43+ @staticmethod
44+ def success (msg ):
45+ if Logging .flag == True :
46+ print "" .join ( [ termcolor .colored ("SUCCES" , "green" ), ": " , termcolor .colored (msg , "white" ) ] )
47+
48+ # Setting Logging
49+ Logging .flag = True
50+
51+ class LoginPasswordError (Exception ):
52+ def __init__ (self , message ):
53+ if type (message ) != type ("" ) or message == "" : self .message = u"帐号密码错误"
54+ else : self .message = message
55+ Logging .error (self .message )
56+
57+ class NetworkError (Exception ):
58+ def __init__ (self , message ):
59+ if type (message ) != type ("" ) or message == "" : self .message = u"网络异常"
60+ else : self .message = message
61+ Logging .error (self .message )
62+ class AccountError (Exception ):
63+ def __init__ (self , message ):
64+ if type (message ) != type ("" ) or message == "" : self .message = u"帐号类型错误"
65+ else : self .message = message
66+ Logging .error (self .message )
67+
68+
69+
70+
71+
72+ def download_captcha ():
73+ url = "http://www.zhihu.com/captcha.gif"
74+ r = requests .get (url , params = {"r" : random .random ()} )
75+ if int (r .status_code ) != 200 :
76+ raise NetworkError (u"验证码请求失败" )
77+ image_name = u"verify." + r .headers ['content-type' ].split ("/" )[1 ]
78+ open ( image_name , "wb" ).write (r .content )
79+ """
80+ System platform: https://docs.python.org/2/library/platform.html
81+ """
82+ if platform .system () == "Linux" :
83+ os .system ("see %s &" % image_name )
84+ elif platform .system () == "Darwin" :
85+ os .system ("open %s &" % image_name )
86+ elif platform .system () == "SunOS" :
87+ os .system ("open %s &" % image_name )
88+ elif platform .system () == "FreeBSD" :
89+ os .system ("open %s &" % image_name )
90+ elif platform .system () == "Unix" :
91+ os .system ("open %s &" % image_name )
92+ elif platform .system () == "OpenBSD" :
93+ os .system ("open %s &" % image_name )
94+ elif platform .system () == "NetBSD" :
95+ os .system ("open %s &" % image_name )
96+ elif platform .system () == "Windows" :
97+ os .system ("open %s &" % image_name )
98+ else :
99+ Logging .info (u"我们无法探测你的作业系统,请自行打开验证码 %s 文件,并输入验证码。" % os .path .join (os .getcwd (), image_name ) )
100+
101+ captcha_code = raw_input ( termcolor .colored ("请输入验证码: " , "cyan" ) )
102+ return captcha_code
103+
104+ def search_xsrf ():
105+ url = "http://www.zhihu.com/"
106+ r = requests .get (url )
107+ if int (r .status_code ) != 200 :
108+ raise NetworkError (u"验证码请求失败" )
109+ results = re .compile (r"\<input\stype=\"hidden\"\sname=\"_xsrf\"\svalue=\"(\S+)\"" , re .DOTALL ).findall (r .text )
110+ if len (results ) < 1 :
111+ Logging .info (u"提取XSRF 代码失败" )
112+ return None
113+ return results [0 ]
114+
115+ def build_form (account , password ):
116+ account_type = "email"
117+ if re .match (r"^\d{11}$" , account ): account_type = "phone"
118+ elif re .match (r"^\S+\@\S+\.\S+$" , account ): account_type = "email"
119+ else : raise AccountError (u"帐号类型错误" )
120+
121+ form = {account_type : account , "password" : password , "remember_me" : True }
122+
123+ form ['_xsrf' ] = search_xsrf ()
124+ form ['captcha' ] = download_captcha ()
125+ return form
126+
127+ def upload_form (form ):
128+ url = "http://www.zhihu.com/login/email"
129+ headers = {
130+ 'User-Agent' : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36" ,
131+ 'Host' : "www.zhihu.com" ,
132+ 'Origin' : "http://www.zhihu.com" ,
133+ 'Pragma' : "no-cache" ,
134+ 'Referer' : "http://www.zhihu.com/" ,
135+ 'X-Requested-With' : "XMLHttpRequest"
136+ }
137+
138+ r = requests .post (url , data = form , headers = headers )
139+ if int (r .status_code ) != 200 :
140+ raise NetworkError (u"表单上传失败!" )
141+
142+ if r .headers ['content-type' ].lower () == "application/json" :
143+ result = r .json ()
144+ if result ["r" ] == 0 :
145+ Logging .success (u"登录成功!" )
146+ return {"result" : True }
147+ elif result ["r" ] == 1 :
148+ Logging .success (u"登录失败!" )
149+ return {"error" : {"code" : int (result ['errcode' ]), "message" : result ['msg' ], "data" : result ['data' ] } }
150+ else :
151+ Logging .warn (u"表单上传出现未知错误: \n \t %s )" % ( str (result ) ) )
152+ return {"error" : {"code" : - 1 , "message" : u"unknow error" } }
153+ else :
154+ Logging .warn (u"无法解析服务器的响应内容: \n \t %s " % r .text )
155+ return {"error" : {"code" : - 2 , "message" : u"parse error" } }
156+
157+
158+ def islogin ():
159+ # check session
160+ url = "http://www.zhihu.com/settings/profile"
161+ r = requests .get (url , allow_redirects = False )
162+ status_code = int (r .status_code )
163+ if status_code == 301 or status_code == 302 :
164+ # 未登录
165+ return False
166+ elif status_code == 200 :
167+ return True
168+ else :
169+ Logging .warn (u"网络故障" )
170+ return None
171+
172+
173+ def read_account_from_config_file (config_file = "config.ini" ):
174+ from ConfigParser import ConfigParser
175+ cf = ConfigParser ()
176+ if os .path .exists (config_file ) and os .path .isfile (config_file ):
177+ Logging .info (u"正在加载配置文件 ..." )
178+ cf .read (config_file )
179+ #cookies = cf._sections['cookies']
180+ email = cf .get ("info" , "email" )
181+ password = cf .get ("info" , "password" )
182+ if email == "" or password == "" :
183+ Logging .warn (u"帐号信息无效" )
184+ return (None , None )
185+ else : return (email , password )
186+ else :
187+ Logging .error (u"配置文件加载失败!" )
188+ return (None , None )
189+
190+
191+
192+
193+ def login (account = None , password = None ):
194+ if islogin () == True :
195+ Logging .success (u"你已经登录过咯" )
196+
197+ if account == None :
198+ (account , password ) = read_account_from_config_file ()
199+ if account == None :
200+ account = raw_input ("请输入登录帐号: " )
201+ password = raw_input ("请输入登录密码: " )
202+
203+
204+ form_data = build_form (account , password )
205+ """
206+ result:
207+ {"result": True}
208+ {"error": {"code": 19855555, "message": "unknow.", "data": "data" } }
209+ {"error": {"code": -1, "message": u"unknow error"} }
210+ """
211+ result = upload_form (form_data )
212+ if "error" in result :
213+ if result ["error" ]['code' ] == 1991829 :
214+ # 验证码错误
215+ Logging .error (u"验证码输入错误,请准备重新输入。" )
216+ return login ()
217+ else :
218+ Logging .warn (u"unknow error." )
219+ return False
220+ elif "result" in result and result ['result' ] == True :
221+ # 登录成功
222+ Logging .success (u"登录成功!" )
223+ requests .cookies .save ()
224+ return True
225+
226+ if __name__ == "__main__" :
227+ login ()
0 commit comments