Python-requests-知乎模拟登录

继续我的python爬虫旅程,开始写博客的时候,说一天一篇,真的只是动动嘴皮子,做起来还真的难,其实是自己给自己找理由…

  • 不管怎样,今天来更新一篇,写个知乎的模拟登录,感觉最开始学习爬虫的时候,大家都期盼着可以写那种需要登录的网站,或者有各种验证码的,那时候看人家在群里说谁能破解验证码,登录某网站抓取信息的时候,羡慕的不行,想要迫切的去学习,慢慢的学会了,感觉验证码的破解也就那么回事,有些很难,花很多功夫,或者直接放弃。再或者直接接个打码平台,也不用手动去登录,再学着学着,感觉验证码的破解也就没那么重要了,并没有在验证码的破解上投入很多的时间,可以直接交给打码平台,专业处理验证码的人去处理就好了,不过常用的一些还是要了解一些,原理是什么。

  • 再就是还有很多请求需要提交参数,有的明文直接写到data,或者params参数里面直接提交,有些网站给它加密或者做很多处理,让人很烦躁,还有很多参数用js代码去生成,对像我一样不太懂js得人来说,真的是蛋痛。

  • 说了这么多,其实就是说要学习真的太多,但是这个懒病该怎么治!!!

废话不多说,来看看知乎的模拟登录~!

知乎今年改版了好像网页,记得去年写过一次,今年再一看变了不少,百度搜的代码基本上都是以前版本的,很少有目前版本的模拟登录博客或者文章什么的。

#简单整理下思路:

  • 手动登录,抓包
  • 分析
  • 验证码获取
  • 验证码识别
  • 验证码验证,数据提交
  • 登录

#手动登录,抓包

  • 用fiddler来抓包,简单浏览分析下
    Python-requests-知乎模拟登录_第1张图片

#验证码获取

  • 手动登陆的时候,发现有的时候并没有验证码,有的时候是英文字母的验证码,有的时候是选择倒立汉字的验证码
  • show_captcha 为false 的时候,不需要验证码
  • show_captcha 为true的时候,需要验证码
  • 所以,搞个if判断,是否要验证码就可以

#验证码识别

  • 上面说到有2种验证码,怎么判断是哪种,其实,验证码的链接只有1个字每的差别
  • 多试几次,就会发现
  • 英文字母验证码链接: https://www.zhihu.com/api/v3/oauth/captcha?lang=en
  • 汉字验证码链接: https://www.zhihu.com/api/v3/oauth/captcha?lang=cn
    -就差在参数lang的值上
  • 怎么识别呢?
  • 判断 show_captcha 为true后,发现下面它又紧接着再次访问了下同一个链接,但是是put请求,看下它的内容会发现,它就是我们要的验证码图片,把它保存到本地
    Python-requests-知乎模拟登录_第2张图片
  • 本文章就使用英文字母的验证码,汉字的其实跟12306的差不多,也是提交坐标进行验证,不过它进行了处理,每个坐标都除以了2,而且y的值貌似必须按照 ?.03125的格式,x可以是整数或者?.5的形式
    -汉字: {“img_size”:[200,44],“input_points”:[[50,25.53125],[151,23.53125]]} size固定大小好像,测试了很多都是200,400
  • 字母:直接提交字母就可以
    Python-requests-知乎模拟登录_第3张图片

#验证码提交,进行验证

  • 验证链接:https://www.zhihu.com/api/v3/oauth/captcha?lang=en
  • 返回true即为验证通过
    Python-requests-知乎模拟登录_第4张图片

#最后,登录

  • 要提交的参数还是挺多的,一个一个来看
    Python-requests-知乎模拟登录_第5张图片

  • client_id:固定值 c3cef7c66a1843f8b3a9e6a1e3160e20

  • grant_type 固定值 password

  • timestamp 13位时间戳

  • source 固定值 com.zhihu.web

  • username 你的账号

  • password 你的密码

  • captcha 验证码

  • lang 貌似是验证码的那个lang 中文验证码的话 写cn 英文字母的写en

  • ref_source 固定值 other_

  • utm_source 可以为空,也可以写baidu,貌似是从哪进入的

  • 最后来说下至关重要的参数signature,也是比较恶心的一个了,它是js生成的,我们来找下他的js文件,直接ctrl+f搜索signature 查找就OK
    Python-requests-知乎模拟登录_第6张图片

  • 再在js里面搜索下signature
    Python-requests-知乎模拟登录_第7张图片

  • 它是用了hmac加密方法,把grant_type + client_id + source + timestamp一起放到里面加密了得到的结果

  • 简单的用Python方法实现就可以得到signature

#登录

  • 按照样式制作表单data
  • 提交链接https://www.zhihu.com/api/v3/oauth/sign_in

最后,上代码

# -*- coding: utf-8 -*-
import base64
import hmac
import re
from hashlib import sha1
import requests
import time
import json

default_headers = {
    'accept': 'application/json, text/plain, */*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Host': 'www.zhihu.com',
    'Origin': 'https://www.zhihu.com',
    'Referer': 'https://www.zhihu.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
    'X-Requested-With': 'Fetch',
}

class ZhihuCrawler():

    def __init__(self):
        self.sess = requests.Session()
        self.sess.headers = default_headers
        self.show_captcha_url = 'https://www.zhihu.com/api/v3/oauth/captcha?lang=en'
        self.login_url = 'https://www.zhihu.com/api/v3/oauth/sign_in'
        self.base_url = 'https://www.zhihu.com/'
        self.client_id = 'c3cef7c66a1843f8b3a9e6a1e3160e20'
        self.source = 'com.zhihu.web'
        self.username = '+8618306420050'
        self.passwd = 'pengchao123'

    def params(self):
        '''
        访问首页获取两个参数 _xsrf和d_c0
        :return: 
        '''
        self.sess.get(self.base_url)
        cookies = self.sess.cookies
        xsrf = cookies.get('_xsrf')
        xuuid = cookies.get('d_c0')

        return xsrf,xuuid

    def show_captcha(self):
        '''
        判断是否需要验证码
        :return: 
        '''

        content = self.sess.get(self.show_captcha_url).json()

        if content.get('show_captcha'):
            return True
        else:
            return False

    def captcha_image_download(self):
        '''
        下载验证码图片
        :return: 
        '''

        base64_img = self.sess.put(self.show_captcha_url).json().get('img_base64')
        imgdata = base64.b64decode(base64_img)
        with open('captcha_img.jpg', 'wb') as f:
            f.write(imgdata)
            f.close()

    def get_input_values(self):
        '''
        手动输入验证码英文字母
        :return: 
        '''
        input_values = input('请输入验证码:')

        return input_values

    def captcha_validation(self):
        '''
        提交验证码进行验证
        :return: 
        '''

        input_values = self.get_input_values()

        validation_data = {
            'input_text': input_values
        }
        result = self.sess.post(self.show_captcha_url, data=validation_data).text
        print(result)
        return input_values

   def get_signature(self, time_str):
        '''
        获取signature参数
        :param time_str:
        :return:
        '''
        signature_hmac = hmac.new(key='d1b964811afb40118a12068ff74a12f4'.encode('utf-8'), digestmod=sha1)
        grant_type = 'password'
        client_id = self.client_id
        source = self.source
        now = time_str
        signature_hmac.update((grant_type + client_id + source + now).encode('utf-8'))
        return signature_hmac.hexdigest()

    def login_zhihu(self):
        '''
        登录
        :return:
        '''
        xxsrftoken,xudid = self.params()
        self.sess.headers.update({
            'X-Xsrftoken': xxsrftoken,
            'X-UDID': re.sub(r'(\|\d+)|(")','',xudid),
        })

        if self.show_captcha():
            self.captcha_image_download()
            input_values = self.captcha_validation()
        else:
            input_values = ''
            print('无验证码')

        time_str = str(int(time.time()*1000))

        login_data = {
            'client_id': self.client_id,
            'grant_type' : 'password',
            'timestamp' : time_str,
            'source' : self.source,
            'signature' : self.get_signature(time_str),
            'username' : self.username,
            'password' : self.passwd,
            'captcha' : json.dumps(input_values),
            'lang' : 'cn',
            'ref_source' : 'other_',
            'utm_source' : 'baidu',
        }

        login = self.sess.post(self.login_url, data=login_data)
        print(login.text)


if __name__ == '__main__':
    crawler = ZhihuCrawler()
    crawler.login_zhihu()

#总结:

  • 可以保存cookies 下次登录的时候不用再次获取了
  • 验证码可以接入打码平台
  • 编码问题
  • js加强

你可能感兴趣的:(python爬虫)