原創文章,轉載請注明出處!
操作環境: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基礎的都看得懂。
下面讓我們一起分析一下這個邏輯
首先你要知道:
- 如果要爬取知乎文章就必須先登錄。
- 爬蟲開始前要執行 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,已經登錄上知乎了,是不是很開心!