爬虫验证码问题
在采集数据过程中,经常会出现验证码的问题。我们可以使用打码平台进行机器识别。同时也可以手动完成。
现在有一个需求就是关于手动解决验证码的问题:当出现验证码时,出现一个提示窗口,要求手动输入验证码,再验证是否正确,如果为True,则继续采集。
第一种解决思路:
携带cookie 数值,弹出的窗口上会显示验证页面的链接,人眼识别之后,输入对话框,随机程序再携带验证结果post请求。
提醒样式
:
代码:
#coding: utf-8
#弹窗程序
import webbrowser
from tkinter import *
class ManualGetCode(object):
def __init__(self,url):
self.url=url
self.root=Tk()
self.root.title('验证码')
self.root.geometry('300x100')
l1=Label(self.root,text="请点击下方网址,并在输入框中输入网址中的验证码")
l1.pack()
def run_url():
webbrowser.open(self.url)
Button(self.root,text=self.url,command=run_url).pack()
self.xls_text = StringVar()
xls = Entry(self.root, textvariable=self.xls_text)
self.xls_text.set(" ")
xls.pack()
def on_click(self):
x = self.xls_text.get()
try:
x=str(x).split()[0]
except:
x=''
return x
def get_code(self):
Button(self.root, text="确定", command=self.on_click).pack()
self.root.mainloop()
return self.on_click()
if __name__ == '__main__':
code=ManualGetCode('http://www.baidu.com').get_code()
print(code)
#验证程序
def manual(self,verificate_ur,cookie):
with request.Session() as session:
# 如果在win上运行
if sys.platform.startswith('win'):
code = ManualGetCode(verificate_url).get_code()
# 如果运行环境是linux
else:
# linux
print(verificate_url)
code = input("请复制上面网址到浏览器,并输入网址中的验证码:")
data={
'icode':code#人人网验证码 所需的字段
}
session.post(verificate_ur,data)
return code
缺点:此种方式需要携带正确的cookie数值,以此来进行判断请求者身份,这样与爬虫程序的耦合性很强,同时别断也很明显:如果使用的不是同一个浏览器就行浏览,则获得的cookie数值就不相同,那么及时验证码输入正确,也不会验证成功。局限性很大。
第二种思路:
定义好接受的类型(如在程序定义接受图片或者图片的二进制流),爬虫程序将采集获得的图片或者图片的二进制传递过来,人眼解析的只是指定的图片,然后识别之后把结果返回给爬虫,这样,耦合性小,局限性小。
提醒样式:
!
代码示例:
#coding=utf-8
#弹窗代码
import requests
from lxml import etree
from PIL import ImageTk
from tkinter import *
import tkinter as tk
import os
import PIL
class GetCode(object):
def __init__(self):
self.data={}
self.root = tk.Tk()
self.root.title('验证码')
self.root.geometry('400x150')
self.root.resizable(width=False,height=False) # 固定长宽不可拉伸
self.textLabel=tk.Label(self.root,text="请输入验证码:").pack() # 标签
self.textStr=StringVar()
self.textEntry=tk.Entry(self.root,textvariable=self.textStr)
self.textStr.set("")
self.textEntry.pack() # 输入框
def get_code(self,imgs):
im = PIL.Image.open(imgs)
img=ImageTk.PhotoImage(im)
tk.Label(self.root,image=img).pack() # 显示图片
self.but = tk.Button(self.root, text="确认", command=self.return_code).pack(fill="both") # 按键
self.root.mainloop()
return self.data
def return_code(self):
# 返回输入框内容
x=self.textStr.get()
self.root.destroy() # 关闭窗体
try:
os.remove("test.jpg")#删除图片
except:
pass
try:
x = str(x).split()[0]
except:
x = ''
self.data['code']=x
#验证程序
def manual(self,verificate_ur):
# 如果在win上运行
if sys.platform.startswith('win'):
code = ManualGetCode(verificate_url).get_code()
# 如果运行环境是linux
else:
# linux
print(verificate_url)
code = input("请复制上面网址到浏览器,并输入网址中的验证码:")
return code
可以看出这样验证码的一个模块,所需的参数比之前简化了很多,同时也符合了面向对象的思想。
小知识点:在第二种思路中需要接受的image或者image的二进制流,如果单纯的传递图片的很好解决,但是验证码我们不需要进行本地保存(少进行io操作增加性能嘛==)所以我们最好采用二进制流的形式进行读取并在窗口中显示,其中遇到了一个问题,就是如何把验证码的二进制流转换成image对象(否则pillow打不开二进制流),
# 此处是图片的二进制流 img_str=session.get(code_url).content # 需要将二进制转换成image对象 img=BytesIO(img_str)
BytesIO模块,直接将二进制转换成image,这样pillow就能识别。