Python3.6+selenium+pytesser3 实现爬虫:含验证码和弹框的页面信息爬取

一、爬虫环境搭建

爬虫目前的主流编写语言为python,确实非常好用,“人生苦短,我用python” 的宣传语也并非吹嘘。下面列出爬虫编写的环境需求和搭建方式:
python3.6:
python分为2.x和3.x两大版本,目前大部分的库已经迁移到了3.x,所以可以放心使用。版本选择的话觉得3.5/3.6都可以。可参照:python3.6——windows环境 来安装python。
selenium
selenium 是一个web的自动化测试工具。简单的说就是一个可以用代码操所浏览器的工具,我我们可以通过selenium进行搜索关键字,点击按钮等等操作。该工具主要提供python和java库支持。我们选择python3.x版本的 selenium 进行安装即可。安装参照:Python3.x下Selenium3.x之安装篇
pytesser3:
pytesser3 是一个python的图像识别库,可用于简单的图像中的英文字母、符号、数字的识别,也可以用于中文等其他语言的识别,具体参照:wxPython利用pytesser模块实现图片文字识别。我们这里安装它主要用于网页中验证码的识别。
pytesser3的安装参照如下:
Python3 OCR技术(pytesser3)
造新轮子啦,让pytesser支持3.x啦~
pycharm:
PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发。我们也可以使用该IDE来写爬虫。
安装参照:Windows平台下PyCharm的安装

二、selenium方法介绍

这里简单下selenium的常用方法,以便于我们理解爬虫代码:
打开浏览器,加载页面:

 

from selenium import webdriver


driver = webdriver.Firefox()   # Firefox浏览器
driver = webdriver.Chrome()    # Chrome浏览器

driver.get(www.jianshu.com) # 打开简书

元素定位:
Selenium提供了8种定位方式。

id
name
class name
tag name
link text
partial link text
xpath
css selector

这8种定位方式在Python selenium中所对应的方法为:

find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath()
find_element_by_css_selector()

举例来说:

elem = driver.find_element_by_id('search')  # 找到一个id为search的元素
xpath = ''
title = driver.find_element_by_xpath(xpath).text   # 通过xpath找到一个元素的text
# 同样可以定位一组元素,注意是elements,结果为一个list
texts = driver.find_elements_by_xpath('//div/h3/a')

浏览器操作:

driver.back() # 后退
driver.forward() # 前进
driver.quit()  #关闭所有窗口
driver.close()  # 关闭单个窗口
driver.refresh() #刷新页面

WebDriver方法:

input = driver.find_element_by_id("kw")
input.clear()  # 清除文本
input.send_keys("selenium")  # 模拟按键输入,在输入框中输入值
driver.find_element_by_id("su").click()  # 单击元素
attribute = driver.find_element_by_id("tag_a").get_attribute('href') # 获取标签属性

键盘操作:

# 删除多输入的一个 字符
driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE)
# ctrl+a 全选输入框内容
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a')
# 通过回车键来代替单击操作
driver.find_element_by_id("su").send_keys(Keys.ENTER)

设置元素等待:
WebDriverWait类是由WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
driver.get("http://www.baidu.com")

element = WebDriverWait(driver, 5, 0.5).until(EC.presence_of_element_located((By.ID, "kw")))
element.send_keys('selenium')
driver.quit()

表单切换:
比如126邮箱登录,想要操作登录框必须要先切换到登录框的iframe表单。

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("http://www.126.com")

driver.switch_to.frame('x-URS-iframe') # 切换到登录表单
driver.find_element_by_name("email").clear()
driver.find_element_by_name("email").send_keys("username")
driver.find_element_by_name("password").clear()
driver.find_element_by_name("password").send_keys("password")
driver.find_element_by_id("dologin").click()
driver.switch_to.default_content() # switch_to.default_content()跳回最外层的页面

driver.quit()

窗口切换:

from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get("http://www.baidu.com")

# 获得当前窗口句柄
sreach_windows = driver.current_window_handle

driver.find_element_by_link_text('登录').click()
driver.find_element_by_link_text("立即注册").click()

# 获得当前所有打开的窗口的句柄
all_handles = driver.window_handles

# 进入注册窗口
for handle in all_handles:
    if handle != sreach_windows:
        driver.switch_to.window(handle)  #用于切换到相应的窗口
        print('now register window!')
        driver.find_element_by_name("account").send_keys('username')
        driver.find_element_by_name('password').send_keys('password')
        time.sleep(2)
        # ……


driver.quit()

警告框处理:

# switch_to_alert()方法获取当前页面上的警告框,accept()方法接受警告框
driver.switch_to.alert.accept()

当然,selenium还有文件上传、获取cookie、执行js等方法,如果需要使用可以自己搜索。
selenium参考链接:http://www.testclass.net/selenium_python/

三、知网爬虫实例


比如我们目前有一些学术期刊的标题信息,想从知网上爬取这些期刊对应的摘要、关键词、作者等信息用于分析,详注释爬虫源码如下:

import csv
import codecs
import os
import time
# 引入selenium库
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC


# 获取原论文数据
path = 'C:\\base_data.csv'
csv_reader = csv.reader(open(path))
rows = []

# 去除表格里表头,start_index 表示除去表头,从第几行数据开始
start_index = 1

for row in csv_reader:
    rows.append(row)
header = rows[0]
rows = rows[start_index:len(rows)]

driver = webdriver.Chrome()

# 打开知网搜索界面,此条用于测试浏览器驱动正常
driver.get('http://kns.cnki.net/kns/brief/default_result.aspx')


# 打开首页,当页面出现等待延迟不加载的时候,等待10s,重新加载
def open_base_page():
    try:
        driver.get('http://kns.cnki.net/kns/brief/default_result.aspx')
        time.sleep(3)
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'iframeResult')), 'timeout')
    except TimeoutException:
        time.sleep(10)
        open_base_page()


# 设定好所需数据路径,这里拿摘要举例
abstracts_path = 'C:\\abstracts\\'
if not os.path.exists(abstracts_path):
    os.makedirs(abstracts_path)

# result.txt 记录爬虫情况:no hit表示未找到,hit表示搜索到,
# no Abstract表示论文页面没有该信息
result_file = codecs.open('C:\\result.txt', 'w', 'utf-8')

for i in range(0, len(rows)):
    # i 为rows下标序号,num为当前起始序号,代码如果半途中断,修改start_index即可继续爬取剩下数据
    num = i + start_index
    try:
        # 打开搜索页
        open_base_page()
        # 输入期刊标题
        elem = driver.find_element_by_id("txt_1_value1")
        elem.send_keys(rows[i][1])
        # 设置等待延时,使爬虫更加拟人,防止反爬虫技术干扰
        time.sleep(10)
        # 进行搜索
        elem.send_keys(Keys.RETURN)

        flag = False

        # 文件名称:序号_类型
        filename = str(num) + '_abstract'
        # 用于存储爬取的摘要
        ab_info = codecs.open(abstracts_path + filename, 'w', 'utf-8')  # 论文摘要

        # 切换到搜索结果的iframe,id为iframeResult
        driver.switch_to.frame('iframeResult')
        # 设置跳转延时5秒,太快可能无法加载出来
        time.sleep(5)
        paper_results = driver.find_elements_by_xpath("//table[@class='GridTableContent']/tbody/tr")

        # temp参数用于搜索结果计数,temp为1时,为表头
        temp = 1
        find_flag = False  # 记录是否找到
        paper_link = ''  # 记录匹配结果链接
        # 检验搜索结果
        for paper_detail in paper_results:
            if temp == 1:
                temp = temp + 1
                continue
            else:
                # 先获取元素的xpath路径,再查取值
                base_xpath = '// *[ @ id = "ctl00"] / table / tbody / tr[2] / td / table / tbody /'
                xpath = base_xpath + 'tr[' + str(temp) + ']'
                temp = temp + 1
                paper_title = paper_detail.find_element_by_xpath(xpath + "/td[2]/a").text
                paper_li = paper_detail.find_element_by_xpath(xpath + "/td[2]/a").get_attribute('href')

                # 进行匹配:
                # 拿专利名匹配,如果完全匹配,则break
                if paper_title == rows[i][1]:
                    paper_link = paper_li
                    find_flag = True
                    break

        if flag:
            # 打开匹配结果的链接
            driver.get(paper_link)
            time.sleep(5)

            # 获取摘要  
            abstract = driver.find_element_by_id('ChDivSummary').text
            ab_info.write(abstract + '\n')

            # 打印爬取结果
            print(str(num) + '  hit')
            result_file.write(str(num) + '  hit' + '\n')
        else:
            print(str(num) + '  no hit')
            result_file.write(str(num) + '  no hit' + '\n')
            
        ab_info.close()
        
    except Exception as err:
        # 将所有异常项先统一打印,最后再分析整理,从而不影响爬虫爬取
        print(err)
        print(str(num) + '  Exception!')
        result_file.write(str(num) + ' Exception! ' + str(err) + '\n')
        continue

result_file.close()
driver.quit() 


四、处理爬虫页面中的验证码

知网网站是没有验证码的,有些数据网站页面中,爬取时含有验证码,如图:

 

验证码界面

这种时候就要使用pytesser3了。轻松实现验证码识别。处理方式如下:

# 循环输入验证码,因为一遍可能不能正确识别,直到正确识别,再进行其他操作
accept = False
while not accept:
    try:
        # 打开含有验证码的搜索页
        driver.get('your link')
        time.sleep(3)
        # 找到输入框input,输入你的搜索关键词
        input = driver.find_element_by_id('keyword')
        input.send_keys('your keyword')
        time.sleep(5)

        # 验证码识别
        # 先对浏览器当前页面截图,并存储
        driver.get_screenshot_as_file('C:\\screenshot.jpg')
        im = Image.open('C:\\screenshot.jpg')

        # 用box裁剪出截图中验证码的所在区域
        box = [100, 100, 200, 200]  # 设置要裁剪的区域
        region = im.crop(box)  # 此时,region是一个新的图像对象
        region.save('C:\\codeImage.jpg')

        time.sleep(3)  # 防止由于网速,可能图片还没保存好,就开始识别

        im = Image.open('C:\\codeImage.jpg')
        imgry = im.convert('L')  # 图像加强,二值化
        sharpness = ImageEnhance.Contrast(imgry)  # 对比度增强
        sharp_img = sharpness.enhance(2.0)

        # 将处理后的验证码图片存在code.jpg中
        sharp_img.save('C:\\code.jpg')
        # sharp_img.show() #这是分布测试时候用的,整个程序使用需要注释掉

        # 调用pytesser3方法,变量code即为识别出的图片数字str类型
        code = pytesser3.image_file_to_string('C:\\code.jpg', graceful_errors=True)
        print('code:' + code)

        # 在页面的验证码输入框中输入识别出的code
        code_input = driver.find_element_by_id('keyword2')
        code_input.send_keys(code)
        time.sleep(2)

        # 然后进行搜索和后续操作
        driver.find_element_by_class_name('search').click()
        time.sleep(2)

        # 如果验证码没有识别正确,可能会弹出提示框,这里我们需要对提示框进行处理

        # 在页面中寻找提示框
        res = EC.alert_is_present()(driver)

        # 如果弹出提示框
        if res:
            # 点击提示框的确认,从新搜索一遍
            res.accept()
            time.sleep(5)
        else:
            # 说明已经识别成功并搜索成功,跳出循环进行下一步操作
            accept = True
    except UnicodeDecodeError:
        accept = False
        time.sleep(3)

五、反爬虫技术


robots.txt
大多数网站都会定义robots.txt文件,让爬虫了解爬取网站的限制。robots.txt虽然是作为建议给出,但作为良好的网络公民都应遵守这些限制。robots.txt放置在一个站点的根目录下,而且文件名必须全部小写。例如:
https://jingyan.baidu.com/robots.txt。一般robots.txt中会规定什么能爬什么不可以,以及每分钟访问的次数限制等等。如果网站规定了robots.txt,并且我们遵守其规则编写爬虫,爬虫是不会被限制的,可以正常获取我们所需要的数据。

反爬虫介绍
反爬虫的技术介绍很多,我贴三篇写的很好的博客和回答,有兴趣的可以阅读。
关于反爬虫,看这一篇就够了
Web Crawler with Python - 07.反爬机制
如何应对网站反爬虫策略?如何高效地爬大量数据?

我们写爬虫时需要注意什么
作为一个良好的网络公民,我不想在博客中介绍反反爬虫技术的相关内容,爬虫和反爬虫从技术上说是程序员的博弈。其交锋只会在发生利益碰撞时产生。所有我们搜取数据时仅 合理、适当 的搜取我们所需要的数据,就不会被反爬虫技术所困扰。
Tips:
1.注意设置访问频率,多添加等待,降低网站访问压力。
2.尽量减少请求次数,能抓列表页就不抓详情页。
3.如果真的对性能要求很高,可以考虑多线程(一些成熟的框架如 scrapy都已支持),甚至分布式

 

 

你可能感兴趣的:(python3.6,selenium)