目录
思路
测试
解析网页
代码
加载验证码图像
光学字符识别
使用tesseract分析验证码
完整注册提交代码
结束语
在爬虫过频繁时可能会返回验证码界面,区别人类和图灵机,我们要做的是就是将验证码转换为文字,提交表单。
下面链接是教科书中的例子
http://example.webscraping.com/places/default/user/register#
这个表单的提交方式有点复杂,recaptcha_response_field即是验证码提交字段,而_formkey显然是经过js加密的,每次提交都不一样,所以下面我们要换一种方法,采用驱动浏览器内核的方式模拟注册。
# coding:utf-8
import unittest
import time
from selenium import webdriver
from bs4 import BeautifulSoup
import _elementtree
class test:
def __init__(self):
self.first_name = 'hongyuan' # 名
self.last_name = 'guo' # 姓
self.email = '[email protected]' # 邮箱
self.password = '[email protected]' # 密码
self.password_two = '[email protected]' # 二次输入密码
self.response_field = 'other' # 验证码
self.driver = webdriver.Firefox()
def testEle(self):
driver = self.driver
driver.maximize_window()
driver.get("http://example.webscraping.com/places/default/user/register#")
driver.find_element_by_id("auth_user_first_name").send_keys(self.first_name)
driver.find_element_by_id("auth_user_last_name").send_keys(self.last_name)
driver.find_element_by_id("auth_user_email").send_keys(self.email)
driver.find_element_by_id("auth_user_password").send_keys(self.password)
driver.find_element_by_id("auth_user_password_two").send_keys(self.password_two)
driver.find_element_by_name("recaptcha_response_field").send_keys(self.response_field)
driver.find_element_by_css_selector("input.btn").click()
driver.switch_to.default_content()
soup = BeautifulSoup(driver.page_source,'lxml') # xml,html亦可
print soup
if __name__ == "__main__":
q = test()
q.testEle()
以上的结果说明基本框架已经搭好,我们只要从验证码中提取文本,提交表单即可。
通过一下代码便可获得并查看验证码图片
开始几行使用 lxml 从表单中获取图像数据。 图像数据的前缀定义了 数据类型。 在本例中,这是一张进行了 Base64 编码的 PNG 图像, 这种格式会使用 ASCII 编码表示二进制数据 。 我们可以通过在第一个逗号处分割 的方法移除该前缀。 然后 , 使用 Base64 解码图像数据, 回到最初的二进制格式。 要想加载图像, PIL 需要一个类似文件的接口,所以在传给Image类之前, 我们又使用 了 BytesIO 对这个二进制数据进行了封装。
......
def get_captcha(self,img_data):
print img_data
img = str(img_data).partition(',')[-1]
binary_img_data = img.decode('base64')
file_like = BytesIO(binary_img_data)
img_ = Image.open(file_like)
plt.imshow(img_)
plt.show()
def testEle(self):
driver = self.driver
driver.maximize_window()
driver.get("http://example.webscraping.com/places/default/user/register#")
soup_1 = BeautifulSoup(driver.page_source, 'lxml')
img = soup_1. find('img').get('src')
self.get_captcha(img)
......
从图片可以看出, 验证码文本一般都是黑色的,背景则会更加明亮,所以我们可以通过检查像素是否为黑色将文本分离出来该处理过程又被称为阁值化。通过Pillow可以很容易地实现该处理过程。
保存图片
img_.save('capcha_original.png')
将原图片转化为灰度图保存,最后只将灰度图中纯黑的部分保存。
......
img = Image.open('capcha_original.png')
gray = img.convert('L')
gray.save('capcha_gray.png')
bw = gray.point(lambda x: 0 if x < 1 else 255, '1')
bw.save('capcha_thresholded.png')
......
tesseract以及pytesseract的配置见
https://blog.csdn.net/sinat_36053757/article/details/78136005
......
bw = Image.open('capcha_thresholded.png')
print pytesseract.image_to_string(bw)
......
运行,成功注册
# coding:utf-8
import unittest
import time
from selenium import webdriver
from bs4 import BeautifulSoup
import _elementtree
from io import BytesIO
import lxml.html
from PIL import Image
import matplotlib.pyplot as plt
import pytesseract
class test:
def __init__(self):
self.first_name = 'hongyuan1' # 名
self.last_name = 'guo1' # 姓
self.email = '*' # 邮箱
self.password = '*' # 密码
self.password_two = '*' # 二次输入密码
self.response_field = 'other' # 验证码
self.driver = webdriver.Firefox()
def get_captcha(self,img_data):
print img_data
img = str(img_data).partition(',')[-1]
binary_img_data = img.decode('base64')
file_like = BytesIO(binary_img_data)
img_ = Image.open(file_like)
img_.save('capcha_original.png')
gray = img_.convert('L')
gray.save('capcha_gray.png')
bw = gray.point(lambda x: 0 if x < 1 else 255, '1')
bw.save('capcha_thresholded.png')
print pytesseract.image_to_string(bw)
return pytesseract.image_to_string(bw)
#plt.imshow(img_)
#plt.show()
def testEle(self):
driver = self.driver
driver.maximize_window()
driver.get("http://example.webscraping.com/places/default/user/register#")
soup_1 = BeautifulSoup(driver.page_source, 'lxml')
img = soup_1. find('img').get('src')
self.response_field = self.get_captcha(img)
driver.find_element_by_id("auth_user_first_name").send_keys(self.first_name)
driver.find_element_by_id("auth_user_last_name").send_keys(self.last_name)
driver.find_element_by_id("auth_user_email").send_keys(self.email)
driver.find_element_by_id("auth_user_password").send_keys(self.password)
driver.find_element_by_id("auth_user_password_two").send_keys(self.password_two)
driver.find_element_by_name("recaptcha_response_field").send_keys(self.response_field)
driver.find_element_by_css_selector("input.btn").click()
driver.switch_to.default_content()
soup_2 = BeautifulSoup(driver.page_source,'lxml') # xml,html亦可
print soup_2
if __name__ == "__main__":
q = test()
q.testEle()
1.这是是用最low的方法解决最low的验证码,其他方法,博主会继续探索
2.这个提交表单页面选的失败,幸好博主有两个邮箱,一个测试,一个最后实战,建议大家将带验证码的网页收集起来,多多分享
3.这里使用driver驱动浏览器,一个缺点是慢,第二个是必须同样用这种方法的爬虫程序才能解决验证码问题,还的潜心研习js,尽情破解加密字段。