新版知乎模拟登陆

「爱情、让人受尽委屈。」 2022-05-23 02:19 448阅读 0赞

1.Cookie和Session的区别

如果你登录知乎,填写过用户名、密码下次进来的时候不想再填写了,那么你在第一次登录后,服务器就会发送给你的浏览器一个Cookie,Cookie中包含了你的用户名、密码,下次再次发送请求给知乎的时候,浏览器会自动给请求加上Cookie,这样服务器就能知道你是谁。这就是Cookie的机制。

但是这种机制是不安全的,当你本地Cookie被别人获取后,就能直接使用你的账号了。于是出现了session机制:当你第一次以用户名、密码登录知乎时,知乎服务器会自己生成一条Session Id和sesson Value保存在服务器端,同时将Session Id作为Cookie中的一个键值对返回给浏览器,当你第二次请求知乎服务器的时候,请求会自动加上Session Id,那么服务器端收到Session Id后,会在服务器上查询是否有此Session Id,如果查询到,那么就匹配到相应的Session Value,也就是包含用户名密码的部分,这时候服务器就能识别出来你是那个用户了。

明确一点:Session Value中包含的是加密的用户名、密码等用户的Profile,是存放在服务器端,同事每一个Session Id都是有一个有效期的,这两点保证了安全性。

2.Http状态码


































code 说明
200 请求被成功处理
301/302 永久重定向/临时重定向
403 没有访问权限
404 灭有对应的资源
500 服务器错误
503 服务器停机或正在维护

3.分析知乎登录

3.1 找到登录请求URL

通过尝试手机号码和邮箱号,分析得出知乎统一登录网址为:https://www.zhihu.com/api/v3/oauth/sign_in

3.2 找到登录时的header部分

  1. :authority: www.zhihu.com
  2. :method: POST
  3. origin: https://www.zhihu.com
  4. referer: https://www.zhihu.com/signup?next=%2F
  5. user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
  6. x-xsrftoken: 405b7e07-d4f5-4b35-8b70-3e129d97a4d8

主要的是找到x-xsrftoken,以前的教程写的都是_xsrf在返回的html中标签中,改版后变化了,我们可以在返回的cookie中找到,所以
首先设置一个header

  1. agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
  2. header = {
  3. "HOST": "www.zhihu.com",
  4. "Referer": "https://www.zhihu.com/",
  5. "User-Agent": agent,
  6. }

取得_xsrf

  1. def get_xsrf():
  2. response = session.post("https://www.zhihu.com/signup?next=%2F", headers=header)
  3. return response.cookies['_xsrf']

随后更新header

  1. header.update({
  2. "X-Xsrftoken": xsrf
  3. })

3.3分析请求登录页面的data数据

3.3.1得到请求的data数据格式为下

  1. ------WebKitFormBoundary4YUIdOtklYTeomJn
  2. Content-Disposition: form-data; name="client_id"
  3. c3cef7c66a1843f8b3a9e6a1e3160e20
  4. ------WebKitFormBoundary4YUIdOtklYTeomJn
  5. Content-Disposition: form-data; name="grant_type"
  6. password
  7. ------WebKitFormBoundary4YUIdOtklYTeomJn
  8. Content-Disposition: form-data; name="timestamp"
  9. 1527727860453
  10. ------WebKitFormBoundary4YUIdOtklYTeomJn
  11. Content-Disposition: form-data; name="source"
  12. com.zhihu.web
  13. ------WebKitFormBoundary4YUIdOtklYTeomJn
  14. Content-Disposition: form-data; name="signature"
  15. 8417b29b51739a7dba377934e7678c35625464f0
  16. ------WebKitFormBoundary4YUIdOtklYTeomJn
  17. Content-Disposition: form-data; name="username"
  18. +8615639151994
  19. ------WebKitFormBoundary4YUIdOtklYTeomJn
  20. Content-Disposition: form-data; name="password"
  21. admin123
  22. ------WebKitFormBoundary4YUIdOtklYTeomJn
  23. Content-Disposition: form-data; name="captcha"
  24. ------WebKitFormBoundary4YUIdOtklYTeomJn
  25. Content-Disposition: form-data; name="lang"
  26. en
  27. ------WebKitFormBoundary4YUIdOtklYTeomJn
  28. Content-Disposition: form-data; name="ref_source"
  29. homepage
  30. ------WebKitFormBoundary4YUIdOtklYTeomJn
  31. Content-Disposition: form-data; name="utm_source"
  32. ------WebKitFormBoundary4YUIdOtklYTeomJn--

3.3.2 通过多次请求发现如下会改变的信息只有

  1. username
  2. password
  3. timestamp
  4. captcha:
  5. signature:

其中用户名、密码是自己控制的

3.3.2.1 timestamp

比较简单就是距离1970年过去的秒数的整数部分。

  1. timestamp = str(int(time.time()))

3.2.2.2 captcha

验证码处理:现在知乎更新了,有的使用字母验证码,有的使用点击倒立的数字验证码。但是我发现登录时只考虑图形字母验证码也可以登录,但不知道* 为什么 *

对字母验证码处理的方法就是下载图片,然后打开,进行手动输入

分析对验证码图片的请求url和header

请求url为:https://www.zhihu.com/api/v3/oauth/captcha?lang=cn

header如下

  1. :authority: www.zhihu.com
  2. :method: GET
  3. :path: /api/v3/oauth/captcha?lang=cn
  4. :scheme: https
  5. accept: application/json, text/plain, */*
  6. accept-encoding: gzip, deflate, br
  7. accept-language: zh-CN,zh;q=0.9,en;q=0.8
  8. authorization: oauth c3cef7c66a1843f8b3a9e6a1e3160e20
  9. cookie: d_c0="AIDk5lsxrA2PTn6UE7w8ZXwIcLwr6s4V8TM=|1527673963"; q_c1=29e9198d965c42b4b4d13820bc7023db|1527673963000|1527673963000; _zap=a0bc8c13-50e7-484b-abde-db97010a065b; l_cap_id="YzIxYmFmNzg0YjViNGZmODljNTIwMjUwMTQ5NWY0NTY=|1527688563|f3bfccc25e61ab0e6c92295650f257e2f9cd779b"; r_cap_id="YTE3M2JlMWQ4NWQzNDA2NzllYThmMWYxNjMxZmRhMTY=|1527688563|be9b0cd7843bf090e5e0194ceb29a99fc60a1cce"; cap_id="ZWFjYTg3ODljMzI0NDVmOTgyYmE0NjRiMGQyZGRmYmU=|1527688563|4636e8decd51156afe879407f16e6c7f57e222ce"; tgw_l7_route=5bcc9ffea0388b69e77c21c0b42555fe; _xsrf=405b7e07-d4f5-4b35-8b70-3e129d97a4d8; capsion_ticket="2|1:0|10:1527727674|14:capsion_ticket|44:Y2M3YTcwYWQ3OGFiNGIxMjk3MWUwY2I5ZWQ0OWM0ZjQ=|ed0700eadd8466ffd6f4c61dfbce11a1f4d11483f32f1ae8262501a7fd859558"
  10. if-none-match: "fa4cf03c0ac47ca1c52ed2df2b71dfda86db6655"
  11. referer: https://www.zhihu.com/signup?next=%2F
  12. user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
  13. x-udid: AIDk5lsxrA2PTn6UE7w8ZXwIcLwr6s4V8TM=

发现了有一个authorization字段,我在此踩坑,没加这个字段的header不能成功请求图片验证码
所以:

  1. def get_captcha():
  2. captcha_url = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=cn'
  3. header.update({
  4. "authorization": "oauth c3cef7c66a1843f8b3a9e6a1e3160e20"
  5. })
  6. response = session.get(captcha_url, headers=header)
  7. r = re.findall('"show_captcha":(\w+)', response.text)
  8. if r[0] == 'false':
  9. return ''
  10. else:
  11. print("需要输入验证码!")
  12. response = session.put('https://www.zhihu.com/api/v3/oauth/captcha?lang=cn', headers=header)
  13. show_captcha = json.loads(response.text)['img_base64']
  14. with open('captcha.jpg', 'wb') as f:
  15. f.write(base64.b64decode(show_captcha))
  16. try:
  17. im = Image.open('captcha.jpg')
  18. im.show()
  19. im.close()
  20. except:
  21. print("打开文件失败!")
  22. captcha = input('输入验证码:')
  23. return captcha

3.2.2.3 signature

这边是参照大神的分析

多次请求,其他参数都是固定的,但是signature参数是个什么东西…知道意思是签名,但是我们要从哪里获取这个呢.网页源码里没有,那肯定是js生成的,去js里搜索在
https://static.zhihu.com/heifetz/main.app.19b9c7c4c4502d8ef477.js 中总算是找到了.为了好看下载到编译器里,实在太大,编译器直接卡死了,太尴尬了….漫长的等待后拿到这么一段js

  1. function (e, t, n) {
  2. "use strict";
  3. function r(e, t) {
  4. var n = Date.now(), r = new a.a("SHA-1", "TEXT");
  5. return r.setHMACKey("d1b964811afb40118a12068ff74a12f4", "TEXT"), r.update(e), r.update(i.a), r.update("com.zhihu.web"), r.update(String(n)), c({
  6. clientId: i.a,
  7. grantType: e,
  8. timestamp: n,
  9. source: "com.zhihu.web",
  10. signature: r.getHMAC("HEX")
  11. }, t)
  12. }

可以看出是使用sha-1 key=’d1b964811afb40118a12068ff74a12f4’和其他的一些字段生成的HMAX

  1. def get_signature(time_str):
  2. h = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)
  3. grant_type = 'password'
  4. client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
  5. source = 'com.zhihu.web'
  6. now = time_str
  7. h.update((grant_type + client_id + source + now).encode('utf-8'))
  8. return h.hexdigest()

3.4 封装data和header进行登录

  1. def zhihu_login(account, password):
  2. # 知乎登录
  3. time_str = str(int(time.time()))
  4. xsrf = get_xsrf()
  5. header.update({
  6. "X-Xsrftoken": xsrf
  7. })
  8. post_url = "https://www.zhihu.com/api/v3/oauth/sign_in"
  9. post_data = {
  10. "client_id": "c3cef7c66a1843f8b3a9e6a1e3160e20",
  11. "grant_type": "password",
  12. "timestamp": time_str,
  13. "source": "com.zhihu.web",
  14. "signature": get_signature(time_str),
  15. "username": account,
  16. "password": password,
  17. "captcha": get_captcha(),
  18. "lang": "cn",
  19. "ref_source": "homepage",
  20. "utm_source": ""
  21. }
  22. response = session.post(post_url, data=post_data, headers=header, cookies=session.cookies)
  23. if response.status_code == 201:
  24. # 保存cookie,下次直接读取保存的cookie,不用再次登录
  25. print("登录成功")
  26. response = session.post("https://www.zhihu.com", headers=header, cookies=session.cookies)
  27. #print(response.text)
  28. session.cookies.save()
  29. else:
  30. print("登录失败")

如果登录成功我们要保存cookie,下次登录就直接实用cookie登录即可,所以我们首先要加载cookie

  1. session = requests.session()
  2. session.cookies = cookielib.LWPCookieJar(filename="cookies.txt") # cookie存储文件,
  3. try:
  4. session.cookies.load(ignore_discard=True) # 从文件中读取cookie
  5. except:
  6. print("cookie 未能加载")

3.5 判断是否已经登录

因为我们加载了cookie,如果cookie有效且正确,这不用再次登录

这里采用点击用户个人中心的链接,且不允许跳转,从而获取返回的状态码,如果状态码为200则证明登录了,否则要进行登录

  1. def is_login():
  2. # 通过个人中心页面返回状态码来判断是否登录
  3. # 通过allow_redirects 设置为不获取重定向后的页面
  4. response = session.get("https://www.zhihu.com/inbox", headers=header, allow_redirects=False)
  5. print(response.cookies)
  6. print(response.status_code)
  7. if response.status_code != 200:
  8. print("尚未登录")
  9. zhihu_login("+***", "***")
  10. else:
  11. print("你已经登陆了")

至此,就可以成功登录啦!

具体实现完整代码,请移驾github

发表评论

表情:
评论列表 (有 0 条评论,448人围观)

还没有评论,来说两句吧...

相关阅读

    相关 创始人周源

    不平凡的人有不一样的人生轨迹,作为知乎创始人的周源,他的创业轨迹又是怎样的呢? 青葱大学 1999年,周源成为成都理工大学计算机系的一名学生。入学初期,周源在图书馆看了一本