python爬虫scrapy框架:人工识别登录知乎倒立文字验证码和数字英文验证码(2)

                                                                                                                                  原创文章,转载请注明出处:https://www.cnblogs.com/wangchaowei/p/7518979.html

                                                                                                                                  操作环境:python3

                                                                                                                                  在上一文中我们已经介绍了用Requests库来登录知乎,本文如果看不懂可以先看之前的文章便于理解

                                                                                                                                  本文将介绍如何用scrapy来登录知乎。


                                                                                                                                  ?不多说,直接上代码:

                                                                                                                                  import scrapy
                                                                                                                                  import re
                                                                                                                                  import json
                                                                                                                                  
                                                                                                                                  '''
                                                                                                                                  遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料
                                                                                                                                  '''
                                                                                                                                  
                                                                                                                                  class ZhihuSpider(scrapy.Spider):
                                                                                                                                      name = 'zhihu'
                                                                                                                                      allowed_domains = ['www.zhihu.com']
                                                                                                                                      start_urls = ['https://www.zhihu.com/']
                                                                                                                                  
                                                                                                                                      headers = {
                                                                                                                                          'HOST': 'www.zhihu.com',
                                                                                                                                          'Referer': 'https://www.zhihu.com',
                                                                                                                                          'User-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8',
                                                                                                                                      }
                                                                                                                                  
                                                                                                                                      def parse(self, response):
                                                                                                                                          pass
                                                                                                                                  
                                                                                                                                      def parse_detail(self, response):
                                                                                                                                          # 爬取文章细节
                                                                                                                                          pass
                                                                                                                                  
                                                                                                                                      # scrapy开始时先进入start_requests()
                                                                                                                                      def start_requests(self):
                                                                                                                                          # 为了提取_xsrf:要先访问知乎的登录页面,让scrapy在登录页面获取服务器给我们的数据(_xsrf),再调用login
                                                                                                                                          return [scrapy.Request('https://www.zhihu.com/#signin', headers=self.headers, callback=self.login)]
                                                                                                                                  
                                                                                                                                      def login(self, response):
                                                                                                                                          xsrf = ''
                                                                                                                                          match_obj = re.match('[\s\S]*name="_xsrf" value="(.*?)"', response.text)
                                                                                                                                          if match_obj:
                                                                                                                                              xsrf =  match_obj.group(1)
                                                                                                                                  
                                                                                                                                          # 如果提取到了xsrf就进行下面的操作,如果没xsrf有就没必要往下做了
                                                                                                                                          if xsrf:
                                                                                                                                              post_data = {
                                                                                                                                                  'captcha_type': 'cn',
                                                                                                                                                  '_xsrf': xsrf,
                                                                                                                                                  'phone_num': 'YourPhoneNum',
                                                                                                                                                  'password': 'YourPassWord',
                                                                                                                                                  'captcha': '',
                                                                                                                                              }
                                                                                                                                              import time
                                                                                                                                              captcha_url = 'https://www.zhihu.com/captcha.gif?r=%d&type=login&lang=cn' % (int(time.time() * 1000))
                                                                                                                                              # scrapy会默认把Request的cookie放进去
                                                                                                                                              return scrapy.Request(captcha_url, headers=self.headers, meta={'post_data': post_data}, callback=self.login_after_captcha)
                                                                                                                                  
                                                                                                                                      def login_after_captcha(self, response):
                                                                                                                                          # 保存并打开验证码
                                                                                                                                          with open('captcha.gif', 'wb') as f:
                                                                                                                                              f.write(response.body)
                                                                                                                                              f.close()
                                                                                                                                          from PIL import Image
                                                                                                                                          try:
                                                                                                                                              img = Image.open('captcha.gif')
                                                                                                                                              img.show()
                                                                                                                                          except:
                                                                                                                                              pass
                                                                                                                                          # 输入验证码
                                                                                                                                          captcha = {
                                                                                                                                              'img_size': [200, 44],
                                                                                                                                              'input_points': [],
                                                                                                                                          }
                                                                                                                                          points = [[22.796875, 22], [42.796875, 22], [63.796875, 21], [84.796875, 20], [107.796875, 20],
                                                                                                                                                    [129.796875, 22], [150.796875, 22]]
                                                                                                                                          seq = input('请输入倒立字的位置\n>')
                                                                                                                                          for i in seq:
                                                                                                                                              captcha['input_points'].append(points[int(i) - 1])
                                                                                                                                          captcha = json.dumps(captcha)
                                                                                                                                  
                                                                                                                                          post_url = 'https://www.zhihu.com/login/phone_num'
                                                                                                                                          post_data = response.meta.get('post_data', {})
                                                                                                                                          post_data['captcha'] = captcha
                                                                                                                                          return scrapy.FormRequest(
                                                                                                                                              # 在这里完成像之前的requests的登录操作,每一个Request如果要做下一步处理都要设置callback
                                                                                                                                              url=post_url,
                                                                                                                                              formdata=post_data,
                                                                                                                                              headers=self.headers,
                                                                                                                                              callback=self.check_login,
                                                                                                                                          )
                                                                                                                                  
                                                                                                                                      def check_login(self, response):
                                                                                                                                          # 验证服务器的返回数据判断是否成功
                                                                                                                                          text_json = json.loads(response.text)
                                                                                                                                          if 'msg' in text_json and text_json['msg'] == '登录成功':
                                                                                                                                              print('登录成功!')
                                                                                                                                              for url in self.start_urls:
                                                                                                                                                  yield scrapy.Request(url, dont_filter=True, headers=self.headers)

                                                                                                                                  ?这个文件是你爬虫目录下的spider/zhihu.py,有scrapy基础的都看得懂。

                                                                                                                                  下面让我们一起分析一下这个逻辑


                                                                                                                                  ?

                                                                                                                                  首先你要知道:

                                                                                                                                  1. 如果要爬取知乎文章就必须先登录。
                                                                                                                                  2. 爬虫开始前要执行 start_requests() 函数 ,然后执行 parse() 函数。

                                                                                                                                  所以我们要在 start_requests() 里进行登录,再在 parse() 里进行提取我们要爬取的字段。这里我们不分析 parse() 怎么写,只分析如何登录。下面让我们逐步分析如何登录:

                                                                                                                                  首先要访问知乎的登录界面获取 "_xsrf" 字段的值:

                                                                                                                                  def start_requests(self):
                                                                                                                                      return [scrapy.Request('https://www.zhihu.com/#signin', headers=self.headers, callback=self.login)]

                                                                                                                                  在scrapy请求了https://www.zhihu.com/#signin后,知乎服务器返回的cookies就会被scrapy保存,下次请求(request)会默认带着这些cookies。

                                                                                                                                  在 login() 函数里进行提取 "_xsrf" 字段(看不懂如何提取的可参考之前的文章),并去请求知乎的验证码URL,这里是必须要注意的,在请求知乎的验证码URL后,知乎服务器会返回cookies,我们在提交验证码字段时必须带上直呼服务器给你的cookies,知乎服务器会进行匹配,如果cookies不对就会验证失败。

                                                                                                                                  def login(self, response):
                                                                                                                                      xsrf = ''
                                                                                                                                      match_obj = re.match('[\s\S]*name="_xsrf" value="(.*?)"', response.text)
                                                                                                                                      if match_obj:
                                                                                                                                          xsrf =  match_obj.group(1)
                                                                                                                                  '''
                                                                                                                                  遇到python不懂的问题,可以加Python学习交流群:1004391443一起学习交流,群文件还有零基础入门的学习资料
                                                                                                                                  '''
                                                                                                                                  
                                                                                                                                      # 如果提取到了xsrf就进行下面的操作,如果没xsrf有就没必要往下做了
                                                                                                                                      if xsrf:
                                                                                                                                          post_data = {
                                                                                                                                              'captcha_type': 'cn',
                                                                                                                                              '_xsrf': xsrf,
                                                                                                                                              'phone_num': '这里写你登录的电话号',
                                                                                                                                              'password': '这里写你的登录密码',
                                                                                                                                              'captcha': '',
                                                                                                                                          }
                                                                                                                                          import time
                                                                                                                                          captcha_url = 'https://www.zhihu.com/captcha.gifr=%d&type=login&lang=cn' % (int(time.time() * 1000))
                                                                                                                                          # scrapy会默认把Request的cookie放进去
                                                                                                                                          yield scrapy.Request(captcha_url, headers=self.headers, meta={'post_data': post_data}, callback=self.login_after_captcha)

                                                                                                                                  向知乎服务器请求验证码后,这个 request 返回的 response 里其实就是验证码图片了,下面我们会调用 login_after_captcha() 函数,进行验证码图片的保存、自动打开、手动输入验证码的位置,再利用 python 的 Json 模块把 captcha 这个dict转换成 Json 格式放入 post_data 中。顺便一提,目前这里的 yield 完全可以用 return 代替。

                                                                                                                                  def login_after_captcha(self, response):
                                                                                                                                      # 保存并打开验证码
                                                                                                                                      with open('captcha.gif', 'wb') as f:
                                                                                                                                          f.write(response.body)
                                                                                                                                          f.close()
                                                                                                                                      from PIL import Image
                                                                                                                                      try:
                                                                                                                                          img = Image.open('captcha.gif')
                                                                                                                                          img.show()
                                                                                                                                      except:
                                                                                                                                          pass
                                                                                                                                      # 输入验证码
                                                                                                                                      captcha = {
                                                                                                                                          'img_size': [200, 44],
                                                                                                                                          'input_points': [],
                                                                                                                                      }
                                                                                                                                      points = [[22.796875, 22], [42.796875, 22], [63.796875, 21], [84.796875, 20], [107.796875, 20],
                                                                                                                                                [129.796875, 22], [150.796875, 22]]
                                                                                                                                      seq = input('请输入倒立字的位置\n>')
                                                                                                                                      for i in seq:
                                                                                                                                          captcha['input_points'].append(points[int(i) - 1])
                                                                                                                                      captcha = json.dumps(captcha)
                                                                                                                                  
                                                                                                                                      post_url = 'https://www.zhihu.com/login/phone_num'
                                                                                                                                      post_data = response.meta.get('post_data', {})
                                                                                                                                      post_data['captcha'] = captcha
                                                                                                                                      return [scrapy.FormRequest(
                                                                                                                                          # 在这里完成像之前的requests的登录操作,每一个Request如果要做下一步处理都要设置callback
                                                                                                                                          url=post_url,
                                                                                                                                          formdata=post_data,
                                                                                                                                          headers=self.headers,
                                                                                                                                          callback=self.check_login,
                                                                                                                                      )]

                                                                                                                                  把填写好的 post_data 发送给知乎登录URL:https://www.zhihu.com/login/phone_num,这里只演示电话号码登录,邮箱登录一个原理,只不过URL不一样:https://www.zhihu.com/login/email。之后我们要调用 check_login() 函数来检查是否登录成功,思路就是查看返回的"msg"字段是否为"登陆成功"。然后再调用scrapy原有的 start_request() 函数里的方法,经查看源码它的方法实际就是下面的遍历self.start_url再进行request(我的start_url是知乎主页,所以这个request就会访问知乎主页)

                                                                                                                                  def check_login(self, response):
                                                                                                                                      # 验证服务器的返回数据判断是否成功
                                                                                                                                      text_json = json.loads(response.text)
                                                                                                                                      if 'msg' in text_json and text_json['msg'] == '登录成功':
                                                                                                                                          print('登录成功!')
                                                                                                                                          for url in self.start_urls:
                                                                                                                                              yield scrapy.Request(url, dont_filter=True, headers=self.headers)

                                                                                                                                  由于我们已经登陆成功了,scrapy再访问知乎主页www.zhihu.com就会带着知乎服务器返回已经登录成功的cookies,因此就会直接进入登录成功的主页。

                                                                                                                                  到此为止,我们就大功告成了!

                                                                                                                                  ?

                                                                                                                                  让我们利用 Pycharm 的 Debug 模式在parse那打个断点,查看response的text,已经登录上知乎了,是不是很开心!

                                                                                                                                  展开阅读全文

                                                                                                                                  没有更多推荐了,返回首页