python爬蟲scrapy框架——人工識別登錄知乎倒立文字驗證碼和數字英文驗證碼(2)


原創文章,轉載請注明出處! 

操作環境:python3

在上一文中python爬蟲scrapy框架——人工識別登錄知乎倒立文字驗證碼和數字英文驗證碼(1)我們已經介紹了用Requests庫來登錄知乎,本文如果看不懂可以先看之前的文章便於理解

本文將介紹如何用scrapy來登錄知乎。


 不多說,直接上代碼:

import scrapy
import re
import json


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)

    # 如果提取到了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,已經登錄上知乎了,是不是很開心!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM