首先控制selenium模拟请求知乎登录界面:https://www.zhihu.com/signup?next=%2F
但是默认为注册,所以需要控制鼠标来点击上边那个登录按钮,就变成这样了。
然后我们在控制浏览器找到输入用户名、密码的这两个input标签,使用send_keys()来将用户名密码输入进去即可,部分代码如下:
但就在我正要点击登录按钮时问题出现了:验证码
经过测试,知乎的验证码分为两种,而且两种验证码所对应的代码是不一样的:
第一种:4位英文数字组成的图片,用户输入对应的英文数字即可。
第二种:纯中文组成的图片,用户使用鼠标来点击图片中倒立的汉字进行验证。
但是我发现验证码也不是次次都出现的,而且如果验证码要出现,当用户输入完密码之后就可以看见了。
而且即使验证码没有在页面中显示出来,却并不代表没有验证码,验证码那一部分代码还是存在的,只是在存放验证码图片的标签那儿图片链接显示为null而已。
那么当一个用户在登录知乎,输入完用户名、密码之后会遇到两大类、四小类(一共四种情况):
(一)无验证码:用户输入完用户名、密码之后没有看见验证码
(1):(英文数字验证码)代码部分中的class为englishImg, src为null
(2):(纯中文验证码)代码部分中的class为chineseImg, src为null
(二)有验证码:用户输入完用户名、密码之后会看见验证码
(1):(英文数字验证码)代码部分中的class为englishImg, src为base64编码的图片路径
(2):(纯中文验证码)代码部分中的class为chineseImg, src为base64编码的图片路径
既然分为两大类四小类,那我们就挨个的处理,首先在输入用户名改密码密码后判断有没有验证码显示出来,若没有,则直接点击登录按钮进行登录;
这是我辨别验证码类型的部分代码:
若有验证码显示出来,则再进行细分,是中文点击验证码还是英文数字输入验证码,辨别验证码类型后再分别进行处理。
首先说一下中文验证码:嘿嘿嘿,时间比较仓促,这个我就没有识别,我看别人都是将验证码保存至本地然后对每个中文的位置进行大致定位,然后人工输入倒立的字的序号,然后计算出该字在图片中的位置,在操控鼠标进行模拟点击。
下面我们说说英文数字验证码:这个主要有两种解决方案
第一种:人工打码。当验证码出现时,将经过base64编码的图片进行解码然后保存至本地,然后将图片展示出来进行人工识别,再在控制台中输入识别后的结果,最后李勇模拟浏览器将结果输入到登录页面中进行登录。
第二种:借助第三方打码平台自动打码,首先将验证码保存至本地,然后接入第三方打码平台的接口进行机器打码,最后将结果输入登录界面。
在这里我详细说说第三方打码的过程:
我是用的是“云打码”平台的接口(然后无意间发发现了他们平台一个错别字,是登录,不是登陆),首先注册一个开发者账号:
进去之后点击我的软件,新建一个自己的软件(名字随便填),然后会获得一个软件代码(id),通讯密钥(key),这两个参数在调用接口时要用。
然后联系官网上的客服,以开发者的身份向他索要测试分(云打码平台是收费的,没有题分,无法识别验证码)
然后在官网上的开发文档中选择调用示例及最新DLL,点击PythonHTTP示例下载,下载接口Demo
然后将Demo中的必要参数改一改(如username、password、id、Key等)最后在记得的代码中调用一下即可。
在验证码全部处理完毕之后再进行模拟登录就可以正常的访问知乎中的内容了,最终我获取了知乎首页的文章标题
ps: 下边这张图片中的代码是在模拟登录之后将cookies持久化至本地,等下次调用时直接将cookies放到session中保持长连接进行请求,不需要再登录
使用session.get()保持长连接重新请求时,一定要带上header
完整代码
import time
import base64
import json
from PIL import Image
from selenium import webdriver
from dama import use_ydm
'''
遇到不懂的问题?Python学习交流群:821460695满足你的需求,资料都已经上传群文件,可以自行下载!
'''
user = '**********'
pwd = '***********'
def main():
options = webdriver.ChromeOptions()
options.add_argument('lang=zh_CN.UTF-8') # 设置中文
options.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"')
browser = webdriver.Chrome(chrome_options=options)
browser.get('https://www.zhihu.com/signup?next=%2F') # 请求登录界面
span_lable = browser.find_elements_by_xpath('//div[@class="SignContainer-switch"]/span')[0]
span_lable.click()
username = browser.find_elements_by_name('username')[0] # 获取username的input标签
time.sleep(3)
username.send_keys(user)
time.sleep(3)
password = browser.find_elements_by_name('password')[0] # 获取password的input标签
password.send_keys(pwd)
time.sleep(3)
return browser
def make_base64(new_img_code): # 处理经base64加密的图片并保存到本地
new_img_code = base64.b64decode(new_img_code)
with open('img_code.png', 'wb') as f:
f.write(new_img_code)
def show_img(): # 展示验证码
img = Image.open('img_code.png')
img.show()
def get_message(browser): # 获取主页title列表
title_list = browser.find_elements_by_xpath('//div[contains(@class, "TopstoryItem-isRecommend")]/div[@class="Feed"]/div[contains(@class, "AnswerItem")]') # 获取主页标题列表
for i in title_list:
title_dict = i.get_attribute("data-zop")
title_dict = json.loads(title_dict)
print(title_dict['title'])
if __name__ == '__main__':
while True:
browser= main()
span_lable = browser.find_elements_by_xpath('//div[contains(@class, SignFlow-captchaContainer)]/div/span[@class="Captcha-englishImage"]') # 查找英文验证码的标签
button = browser.find_elements_by_xpath('//button[contains(@class, "SignFlow-submitButton")]')[0] # 登录按钮
if len(span_lable) == 0: # 判断验证码类型,是中文(点击类型)还是英文(输入类型)的
img_lable = browser.find_elements_by_xpath('//img[@class="Captcha-chineseImg"]')[0] # 查找中文验证码标签
img_url = img_lable.get_attribute('src') # 验证码图片路径
if img_url == 'data:image/jpg;base64,null': # 判断是否有验证码
print('没有验证码,直接点击登录!')
button.click()
time.sleep(3)
get_message(browser)
break
else:
print('中文验证码,暂时处理不了,跳过')
continue
elif len(span_lable) == 1:
img_lable = browser.find_elements_by_xpath('//img[@class="Captcha-englishImg"]')[0] # 查找中文验证码标签
img_url = img_lable.get_attribute('src') # 验证码图片路径
if img_url == 'data:image/jpg;base64,null': # 判断是否有验证码
print('没有验证码,直接点击登录!')
button.click()
time.sleep(3)
get_message(browser)
break
else:
base64_img_url = img_url.replace('data:image/jpg;base64,','') # 对base64做处理
make_base64(base64_img_url)
input_lable = browser.find_elements_by_name('captcha')[0]
# show_img()
# input_lable = input('手动打码,请输入验证码>>>')
code = use_ydm('img_code.png') # 调用云打码接口,返回识别后的内容
input_lable.send_keys(code) # 将验证码写入
time.sleep(3)
button.click()
time.sleep(3)
get_message(browser) # 获取主页标题
break
ps: 在一切处理好之后我在模拟点击登录时又碰到了一个问题,切切都处理好了,在点击登录按钮后并没有进入登录页面,而是显示 “Miss argument grant_type ” 查询资料说是浏览器版本问题,当我将Chrome从70降到60后问题就解决了。