最新 詳解如何模擬登錄知乎 英文驗證碼以及中文倒立字(繼上篇scrapy分析知乎)

可轉至我的博客:http://www.dwlufvexyu.com/%e6%9c%80%e6%96%b0-%e8%af%a6%e8%a7%a3%e5%a6%82%e4%bd%95%e6%a8%a1%e6%8b%9f%e7%99%bb%e5%bd%95%e7%9f%a5%e4%b9%8e%ef%bc%882-0-%e6%8c%81%e7%bb%ad%e6%9b%b4%e6%96%b0%ef%bc%81%ef%bc%89/

開始更新:

廢話不多講,一樣的直接入正題;

爬取知乎,需要登錄;只要登錄獲得了cookie,便可反覆利用

那麼首先說說如何最簡單的登錄知乎?看我的另一篇博文

接下來開始一步一步操作:

在這裏插入圖片描述
首先,第一步:直接 進行 “檢查”; 獲取到 “密碼登錄”的xpath元素(直接複製即可)。

#切換登錄
change_login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/div[1]/div[2]')
#進行點擊
change_login.click()

Step 2. 直接“檢查”到 賬號以及密碼框;

可以看到<賬號 是input name="username" 以及 密碼 name="password">

在這裏插入圖片描述
在這裏插入圖片描述
然後直接用find_element_by_name找到 對應輸入框,然後利用send_keys進行傳值。這裏爲了避免以前登陸過,進頁面會有存有的賬號密碼導致重複輸入,所以先執行ctrl+A進行輸入框內容的選中,再進行傳值。
在這裏插入圖片描述
這裏手機號以13579246810,密碼admin123爲例 ;如圖,這裏設置延時,爲了得到更好的查看效果,爲了速度可以不要。

#可以設置延時 
time.sleep(1)
#採用ctrl+a 進行選中輸入 進行覆蓋 避免追加輸入導致賬號密碼錯誤
browser.find_element_by_name("username").send_keys(Keys.CONTROL+"a")
browser.find_element_by_name("username").send_keys('13579246810')
time.sleep(1)
browser.find_element_by_name("password").send_keys(Keys.CONTROL + "a")
browser.find_element_by_name("password").send_keys('admin123')
time.sleep(1)

step3.賬號密碼輸入完畢後,進行確定。同樣進行 “檢查” 找到“確定”的xpath元素。再進行click() ; 等待10s進行判斷,這裏等待10s是爲了 假如登陸成功,需要一定時間進行加載頁面;”

login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/button')
login.click()
#這裏如果直接登錄成功,則等待10s等待跳轉後的頁面加載完成
time.sleep(10)
#如果是登錄False
login_success=False

那麼隨機找到一個登陸成功頁面的xpath元素,那麼就可以判斷是已登陸成功,反之False; 如找到如下元素div的xpath,如果存在,那麼爲True;
在這裏插入圖片描述

while not login_success:
    try:
        notify_ele=browser.find_element_by_class_name('Popover PushNotifications AppHeader-notifications')
        login_success=True
    except:
        pass

如果不存在,登陸失敗,則pass繼續操作,進行判斷中/英文驗證碼

英文驗證碼:

在這裏插入圖片描述
中文同樣的:
在這裏插入圖片描述

#判斷英文驗證碼還是倒立字驗證碼
try:
    english_captcha_element = browser.find_element_by_class_name('Captcha-englishImg')
except:
    english_captcha_element = None
try:
    chinese_captcha_element = browser.find_element_by_class_name('Captcha-chineseImg')
except:
    chinese_captcha_element = None

接下來就先識別英文二維碼吧:

有三種打碼方式 : 編碼實現(tesseract-ocr) 識別率低 , 在線打碼(雲打碼) ,人工打碼(後兩者識別率更高,通過平臺提供的API),通常使用在線打碼(也就是雲打碼)

這裏我們用 “雲打碼” 平臺(比較好用的在線打碼平臺),進入官網,我們創建一個用戶 以及 一個開發者賬號(賬號密碼可設置爲一樣) 方便測試;關於爲什麼 需要創建兩個賬戶,可以百度。這裏簡要說一下 ,開發者通過平臺給用戶提供調用API,當用戶在線打碼成功,開發者會獲得相應的分成利潤。

如👇,可以看到我的開發者用戶,測試已經獲取0.1的分成了。

在這裏插入圖片描述
用戶界面,每次打碼都會消耗積分
在這裏插入圖片描述
進入開發者中心,下載最新雲打碼接口在這裏插入圖片描述
在這裏插入圖片描述
下好之後,記得改好文件;如下
在這裏插入圖片描述
現在來仔細看英文驗證碼:從圖中可以明顯看出是base64加密,且有一段是多餘的,除此之外有個坑還~仔細看有個%0A也是多餘的
在這裏插入圖片描述
所以我們先解碼

if english_captcha_element:
    #這裏解碼同中文倒立字
    #取src
    base64_text = english_captcha_element.get_attribute("src")
    import base64
    #打上斷點來debug看看code是什麼 
    #替換
    code = base64_text.replace("data:image/jpg;base64,","").replace("%0A","")
    #print(code) 
    f = open("yzm_en.jpeg", "wb")
    f.write(base64.b64decode(code))
    f.close()

多達75次的%0A
在這裏插入圖片描述
在這裏插入圖片描述

接下來就是調用雲打碼平臺提供的api,下面的賬號密碼是註冊的用戶的賬號密碼~

驗證碼code 如果爲空,就繼續打碼

#採用雲解碼提供的API 在線打碼
#導入之前雲打碼下載的那個包
from douban.tools.yundama_requests import YDMHttp
#username passwd appID appKey
yundama=YDMHttp("username","passwd",'appid','appkey')
#圖片名 及 打碼方式
code=yundama.decode("yzm_en.jpeg",5000,60)
#判斷是否成功
while True:
    if code == "":
        code = yundama.decode("yzm_en.jpeg",5000,60)
    else:
        break
#成功後自動輸入驗證碼;同樣send_keys傳入驗證碼值~
browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/div[4]/div/div/label/input').send_keys(code)

如下可以看到已經識別成功了~~~
在這裏插入圖片描述
在這裏插入圖片描述
然後就直接登錄成功了~~


接下來講關於中文驗證碼的識別:

ps:關於中文驗證碼的識別,有些還不太夠詳細甚至有點草率,後面完成互聯網+ 項目後,找時間進行完善。

首先是在github上search "zhiye" 大佬

download後,cp到當前工程目錄下

現需要安裝依賴包

在這裏插入圖片描述
在這裏插入圖片描述
除了註釋以外的,都需要install;
如果有些加了 豆瓣源依舊無法下載成功,那麼到鏈接下載,然後離線安裝whl。安裝中,有什麼問題可以在下方留言。
對於如何調用,在“zheye”下方就可以看到;在這裏插入圖片描述
通過使用方法,我們用自帶的a.gif做測試
在這裏插入圖片描述
在這裏插入圖片描述

"[(50.66702980958077, 39.79269455429982), (47.53827248468947, 278.193248033293)]"雖然座標是這樣的,但其實實際上並不是

我們進入zheye的在線測試地址:http://zheye.74ls74.org/;同樣的我們提交a.gif

在這裏插入圖片描述
打開json文件,可以看到,座標是這樣的,(關於兩個字的先後順序,不重要,只要是準確的就可以)
在這裏插入圖片描述
實際上首先座標是有點問題的,看上圖可以明顯看出,第二個字的x座標更大,y座標更小,但是顯示卻是顛倒的,所以需要進行座標的調換,除此之外,知乎倒立字爲1-2個;所以作出以下判斷

from zheye import zheye
z = zheye()
positions = z.Recognize('D:/Pythonstudy/douban/douban/spiders/zhihu_image/a.gif')
last_position = []
#只有兩種情況 一個倒立字 和 兩個倒立字
#兩個倒立字時
if len(positions) == 2:
    #進行座標的調整 直接複製即可 ,因爲 默認是有問題的
    if positions[0][1] > positions[1][1]:
        last_position.append([positions[1][1], positions[1][0]])
        last_position.append([positions[0][1], positions[0][0]])
    else:
        last_position.append([positions[0][1], positions[0][0]])
        last_position.append([positions[1][1], positions[1][0]])
#print(last_position)
#一個倒立字時
else:
    last_position.append([positions[0][1], positions[0][0]])

得到正確的座標,應該是這樣的
在這裏插入圖片描述
座標確立之後,接下來的任務就簡單了。
進行move,以及click。但是實際上知乎的倒立字,單獨打開和頁面上的大小不一致;如圖

在這裏插入圖片描述
所以做如下變化,關於代碼這裏還有個坑,同時做實驗時,需要把下方的chrome下載任務關掉,否則座標點擊錯誤。且需要關注;

ele_location=chinese_captcha_element.location
##
x_relative=ele_location["x"]
y_relative=ele_location["y"]
#這裏是減去 瀏覽器上方工具欄的高度 保證Y座標的精度
browser_navigation_panel_height=browser.execute_script('return window.outerHeight - window.innerHeight;')

在這裏插入圖片描述

#兩個倒立字時:
#第一個倒立字的座標 除2是因爲 知乎的驗證碼 比 本地保存的小 2倍
first_position=[int(last_position[0][0] / 2),int(last_position[0][1] / 2)]
second_position=[int(last_position[1][0] / 2),int(last_position[1][1] / 2)]
#鼠標move及click
move(x_relative+first_position[0],y_relative+browser_navigation_panel_height+first_position[1])
click()
#等待3s 點擊第二個
time.sleep(3)
move(x_relative + second_position[0],
     y_relative + browser_navigation_panel_height + second_position[1])
click()

#一個倒立字時:
first_position = [int(last_position[0][0] / 2), int(last_position[0][1] / 2)]
move(x_relative + first_position[0],
     y_relative + browser_navigation_panel_height + first_position[1])
click()

倒立字點擊後,點擊登錄即可;最終就成功了;

附上知乎所有源碼🐎~另一篇只有分析知乎的。

一些參數需要自己修改成自己的~

# -*- coding: utf-8 -*-
import json

import scrapy
import re
from selenium import webdriver
from urllib import parse
from scrapy.loader import ItemLoader
from douban.items import ZhihuAnswerItem,ZhihuQuestionItem
import datetime

from zheye import zheye


from mouse import move,click

import time

from selenium.webdriver.common.keys import Keys


class ZhihuSpider(scrapy.Spider):
    name = 'zhihu'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['https://www.zhihu.com/']

    start_answer_url="https://www.zhihu.com/api/v4/questions/{0}/answers?include=data%5B*%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%2Cis_recognized%2Cpaid_info%2Cpaid_info_content%3Bdata%5B*%5D.mark_infos%5B*%5D.url%3Bdata%5B*%5D.author.follower_count%2Cbadge%5B*%5D.topics&offset={1}&limit={2}&sort_by=default&platform=desktop"

    headers = {
        # "HOST": "www.zhihu.com",
        # "Referer": "https://www.zhizhu.com",
        #User-Agent必不可少
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
    }
    def parse(self, response):
        #xpath檢測知乎標題 取出所有a標籤的href_url
        all_urls = response.xpath("//a/@href").extract()
        #接下來遍歷list類型的all_urls
        #因爲
        all_urls = [parse.urljoin(response.url, url) for url in all_urls]
        for url in all_urls:
            # print(url)
            match_obj=re.match("(.*zhihu.com/question/(\d+)).*",url)
            if match_obj:
                request_url=match_obj.group(1)
                yield scrapy.Request(request_url,headers=self.headers,callback=self.parse_question)
                # break
            else:
                yield scrapy.Request(url,headers=self.headers,callback=self.parse)
                # print(request_url,question_id)

    def parse_question(self,response):
        match_obj = re.match("(.*zhihu.com/question/(\d+)).*", response.url)
        if match_obj:
            question_id = int(match_obj.group(2))
        item_loader=ItemLoader(item=ZhihuQuestionItem(),response=response)
        print(response)
        item_loader.add_css("title", 'h1.QuestionHeader-title::text')
        item_loader.add_css('content', '.QuestionHeader-detail')
        item_loader.add_value('url', response.url)
        item_loader.add_value('zhihu_id', question_id)
        item_loader.add_css('answer_num', '.List-headerText span::text')
        item_loader.add_css('comments_num', '.QuestionHeader-Comment button::text')
        item_loader.add_css('topics', '.QuestionHeader-topics .Popover div::text')
        item_loader.add_css('watch_user_num', '.NumberBoard-itemValue::text')

        question_item = item_loader.load_item()
        yield scrapy.Request(self.start_answer_url.format(question_id, 0, 20), headers=self.headers, callback=self.parse_answer)
        yield question_item
    def parse_answer(self,response):
        ans_json = json.loads(response.text)
        is_end = ans_json["paging"]["is_end"]
        next_url = ans_json["paging"]["next"]
        totals_num=ans_json["paging"]["totals"]
        #提取具體字段
        for answer in ans_json["data"]:
            answer_item = ZhihuAnswerItem()
            answer_item["zhihu_id"] = answer["id"]
            answer_item["url"] = answer["url"]
            answer_item["question_id"] = answer["question"]["id"]
            answer_item["author_id"] = answer["author"]["id"] if "id" in answer["author"] else None
            answer_item["content"] = answer["content"] if "content" in answer else None
            answer_item["praise_num"] = answer["voteup_count"]
            answer_item["comments_num"] = answer["comment_count"]
            answer_item["create_time"] = answer["created_time"]
            answer_item["update_time"] = answer["updated_time"]
            answer_item["crawl_time"] = datetime.datetime.now()

            yield answer_item
        if not is_end:
            yield scrapy.Request(next_url,headers=self.headers,callback=self.parse_answer)







    def start_requests(self):


        # cookies=pickle.load(open("D:/Pythonstudy/douban/douban/spiders/cookies/zhihu_cookies","rb"))
        # cookie_dict = {}
        #
        # for cookie in cookies:
        #     cookie_dict[cookie['name']] = cookie['value']
        #
        # return [scrapy.Request(url=self.start_urls[0], dont_filter=True, cookies=cookie_dict)]



        # 1.cmd 設置端口 啓動chrome (啓動之前確保所有chrome實例已經關閉 否則不成功)
        # 一行 js 代碼識別 Selenium+Webdriver 及其應對方案 知乎的反爬措施 確定是否爲模擬登錄 window.navigator.webdriver
        ##驗證碼操作

        chrome_option=webdriver.ChromeOptions()
        chrome_option.add_argument('--disable-extensions')
        chrome_option.add_experimental_option('debuggerAddress',"127.0.0.1:9222")

        # browser = webdriver.Chrome()
        browser=webdriver.Chrome(executable_path="D:/Envs/py3scrapy/Scripts/chromedriver.exe",chrome_options=chrome_option)
        browser.get("https://www.zhihu.com/")

        #最大化窗口,確保定位準確 如果已經是最大化 則pass
        try:
            browser.maximize_window()
        except:
            pass

        # time.sleep(30)
        # cookies = browser.get_cookies()
        #
        # pickle.dump(cookies, open("D:/Pythonstudy/douban/douban/spiders/cookies/zhihu_cookies",'wb'))
        #
        # cookie_dict = {}
        #
        # for cookie in cookies:
        #     cookie_dict[cookie['name']] = cookie['value']
        #
        # return [scrapy.Request(url=self.start_urls[0], dont_filter=True, cookies=cookie_dict)]

        #進行切換登錄及登錄操作 延時看你心情
        change_login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/div[1]/div[2]')
        change_login.click()
        time.sleep(1)
        #採用ctrl+a 進行選中輸入 進行覆蓋 避免追加輸入導致賬號密碼錯誤
        browser.find_element_by_name("username").send_keys(Keys.CONTROL+"a")
        browser.find_element_by_name("username").send_keys('num')
        time.sleep(1)
        browser.find_element_by_name("password").send_keys(Keys.CONTROL + "a")
        browser.find_element_by_name("password").send_keys('passwd')
        time.sleep(1)
        login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/button')
        login.click()
        #這裏如果直接登錄成功,則等待10s等待跳轉後的頁面加載完成
        time.sleep(10)
        #如果是登錄False
        login_success=False
        while not login_success:
            try:
                notify_ele=browser.find_element_by_class_name('Popover PushNotifications AppHeader-notifications')
                login_success=True
            except:
                pass

            #判斷英文驗證碼還是倒立字驗證碼
            try:
                english_captcha_element = browser.find_element_by_class_name('Captcha-englishImg')
            except:
                english_captcha_element = None
            try:
                chinese_captcha_element = browser.find_element_by_class_name('Captcha-chineseImg')
            except:
                chinese_captcha_element = None

            #如果是中文驗證碼
            if chinese_captcha_element:
                #進行倒立字圖片的定位
                ele_location=chinese_captcha_element.location
                ##
                x_relative=ele_location["x"]
                y_relative=ele_location["y"]
                #這裏是減去 瀏覽器上方工具欄的高度 保證Y座標的精度
                browser_navigation_panel_height=browser.execute_script('return window.outerHeight - window.innerHeight;')

                #取倒立字的src,並保存在本地進行base64的解碼
                base64_text=chinese_captcha_element.get_attribute("src")
                import base64
                #這裏 %0A 很奇葩
                code=base64_text.replace("data:image/jpg;base64,","").replace("%0A","")
                f=open("yzm_cn.jpeg","wb")
                f.write(base64.b64decode(code))
                f.close()
                #這裏進行倒立字的座標確定 github:zheye
                z=zheye()
                #圖片路徑
                positions = z.Recognize('yzm_cn.jpeg')

                last_position = []
                #只有兩種情況 一個倒立字 和 兩個倒立字
                #兩個倒立字時
                if len(positions) == 2:
                    #進行座標的調整 直接複製即可 ,因爲 默認是有問題的
                    if positions[0][1] > positions[1][1]:
                        last_position.append([positions[1][1], positions[1][0]])
                        last_position.append([positions[0][1], positions[0][0]])
                    else:
                        last_position.append([positions[0][1], positions[0][0]])
                        last_position.append([positions[1][1], positions[1][0]])
                    #第一個倒立字的座標 除2是因爲 知乎的驗證碼 比 本地保存的小 2倍
                    first_position=[int(last_position[0][0] / 2),int(last_position[0][1] / 2)]
                    second_position=[int(last_position[1][0] / 2),int(last_position[1][1] / 2)]
                    #鼠標move及click
                    move(x_relative+first_position[0],y_relative+browser_navigation_panel_height+first_position[1])
                    click()
                    #等待3s 點擊第二個
                    time.sleep(3)
                    move(x_relative + second_position[0],
                         y_relative + browser_navigation_panel_height + second_position[1])
                    click()

                #只有一個倒立字時
                else:
                    last_position.append([positions[0][1], positions[0][0]])
                    ###
                    first_position = [int(last_position[0][0] / 2), int(last_position[0][1] / 2)]
                    move(x_relative + first_position[0],
                         y_relative + browser_navigation_panel_height + first_position[1])
                    click()

                #倒立字點擊之後 重新進行賬號密碼的輸入
                browser.find_element_by_name("username").send_keys(Keys.CONTROL + "a")
                browser.find_element_by_name("username").send_keys('num')
                time.sleep(1)
                browser.find_element_by_name("password").send_keys(Keys.CONTROL + "a")
                browser.find_element_by_name("password").send_keys('passwd')
                time.sleep(1)
                login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/button')
                login.click()

            if english_captcha_element:
                #這裏解碼同中文倒立字
                base64_text = english_captcha_element.get_attribute("src")
                import base64
                code = base64_text.replace("data:image/jpg;base64,","").replace("%0A", "")
                f = open("yzm_en.jpeg", "wb")
                f.write(base64.b64decode(code))
                f.close()

                #採用雲解碼提供的API 在線打碼
                from douban.tools.yundama_requests import YDMHttp
                #用戶username passwd appID appKey
                #填寫你自己的數據,需要自己修改
                appid=11111
                yundama=YDMHttp("user","passwd",appid,'appkey')
                #圖片路徑 及 打碼方式
                code=yundama.decode("yzm_en.jpeg",5000,60)
                #判斷是否成功
                while True:
                    if code == "":
                        code = yundama.decode("yzm_en.jpeg",5000,60)
                    else:
                        break
                #成功後自動輸入驗證碼
                browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/div[4]/div/div/label/input').send_keys(code)

                #重新輸入賬號密碼
                browser.find_element_by_name("username").send_keys(Keys.CONTROL + "a")
                browser.find_element_by_name("username").send_keys('num')
                time.sleep(1)
                browser.find_element_by_name("password").send_keys(Keys.CONTROL + "a")
                browser.find_element_by_name("password").send_keys('passwd')
                time.sleep(1)
                login = browser.find_element_by_xpath('//*[@id="root"]/div/main/div/div/div[1]/div/form/button')
                login.click()

        pass



Thanks your watching!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章