用selenium制作爬虫爬取教务课程信息

https://apriljia.com/2018/09/26/%E7%94%A8selenium%E5%88%B6%E4%BD%9C%E7%88%AC%E8%99%AB%E7%88%AC%E5%8F%96%E6%95%99%E5%8A%A1%E8%AF%BE%E7%A8%8B%E4%BF%A1%E6%81%AF/

https://github.com/plutojia/Crawler-with-checkcode-recognition-

前段时间在选课,而我们的教务系统又十分蛋疼。先是在选课时不停崩溃,进不去,选课结束要打印选课单时又因为它自己系统太老而不支持64位浏览器打印课表。。。没有办法我就写了一个爬取教务课程信息并将其存储在MongoDB中的程序,这个程序稍微改改就可以变成抢课脚本了。内容有:

  • 使用selenium驱动chrome浏览器
  • 用pytesseract识别验证码
  • MongoDB存储

首先选课系统长这个样子:

用selenium制作爬虫爬取教务课程信息

需要做的是通过脚本输入用户名和密码,并识别验证码登陆教务系统,跳转到选课页面,获取选课信息,最后将信息存储于MongoDB中。

打开开发者工具,定位登陆表单各元素:

用selenium制作爬虫爬取教务课程信息

依次获取元素填写相应字段,最后模拟点击登陆即可,代码如下:


def login():
    browser.get('http://gsmis.graduate.buaa.edu.cn/gsmis/main.do')
    browser.maximize_window()
    input_id=browser.find_element_by_xpath('//input[@name="id"]')
    input_password =browser.find_element_by_xpath('//input[@name="password"]')
    input_checkcode=browser.find_element_by_xpath('//input[@name="checkcode"]')
    img_checkcode=browser.find_element_by_xpath('//img[@src="/gsmis/Image.do"]')

    location = img_checkcode.location
    size = img_checkcode.size
    browser.save_screenshot('checkcode.png')
    checkcode=getCheckcode('checkcode.png',location,size)

    input_id.send_keys(data['id'])
    input_password.send_keys(data['password'])
    input_checkcode.send_keys(checkcode)

    lg_button=browser.find_element_by_xpath('//img[@onclick="document.forms[0].submit()"]')

    time.sleep(1)
    lg_button.click()

其中有识别验证码的部分,识别函数采取的是将验证码截图,保存,交给pytesseract识别的方法,为了提高识别准确度,对验证码图片进行了二值化处理,并在OCR识别后对一些容易混淆为字母的数字进行了纠正,代码如下:

def initTable(threshold=140):           # 二值化函数
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)

    return table

def getCheckcode(savepath,location,size):
    rep = {'O': '0',  # replace list
           'I': '1', 'L': '1',
           'Z': '2',
           'S': '8'
           };
    im = Image.open(savepath)
    location['x']=825
    location['y']=495
    left = location['x']
    top = location['y']
    right = location['x'] + size['width']
    bottom = location['y'] + size['height']
    im = im.crop((left, top, right, bottom))
    im.save(savepath)
    im = im.convert('L')
    binaryImage = im.point(initTable(), '1')
    # binaryImage.show()
    checkcode = pytesseract.image_to_string(binaryImage, config='-psm 7')
    for r in rep:
        checkcode = checkcode.replace(r, rep[r])
    print(checkcode)
    return checkcode

这样就登陆进入了教务系统主页,还需要打开选课页面。这里注意frame的切换,在父frame里无法定位子frame的元素,所以要根据情况switch到子frame:

用selenium制作爬虫爬取教务课程信息

def navigate():
    time.sleep(1)
    browser.get("http://gsmis.graduate.buaa.edu.cn/gsmis/toModule.do?prefix=/py&page=/pySelectCourses.do?do=xsXuanKe")
    browser.switch_to.frame('frmme')
    browser.switch_to.frame('leftFrame')
    xuanke=browser.find_element_by_xpath('//a[@href="/gsmis/py/pySelectCourses.do?do=xuanBiXiuKe"]')
    xuanke.click()
    browser.switch_to.parent_frame()
    browser.switch_to.frame('mainFrame')

 

现在到了这一步,获取课程信息并保存即可:

用selenium制作爬虫爬取教务课程信息

def prase():
    lessons=browser.find_elements_by_class_name("tablefont2")
    for lesson in lessons:
        infos=lesson.find_elements_by_xpath("./td")
        is_selected = infos[0].find_element_by_xpath("./input").is_selected()
        if(is_selected):
            info={
                'when&where':infos[1].text,
                'classification': infos[3].text,
                'name': infos[4].text,
                'teacher': infos[10].text,
                'remian': infos[12].text,
            }
            yield(info)


MONGO_URL = 'localhost'
MONGO_DB = 'jiaowu'
MONGO_COLLECTION = 'Curriculum'
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(result):
    """
    保存至MongoDB
    :param result: 结果
    """
    try:
        if db[MONGO_COLLECTION].insert(result):
            print('存储到MongoDB成功')
    except Exception:
        print('存储到MongoDB失败')

 

运行结果如下:

用selenium制作爬虫爬取教务课程信息

用selenium制作爬虫爬取教务课程信息

完整代码:

import pytesseract
import requests
from PIL import Image
import os
from selenium import webdriver
import time
from selenium.webdriver.support.wait import WebDriverWait
import pymongo

data={
    'id':'xxxxxxxx',
    'password':'xxxxxxxxx',
    'checkcode':''
}
browser = webdriver.Chrome()
wait = WebDriverWait(browser, 10)

def initTable(threshold=140):           # 二值化函数
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)

    return table

def getCheckcode(savepath,location,size):
    rep = {'O': '0',  # replace list
           'I': '1', 'L': '1',
           'Z': '2',
           'S': '8'
           };
    im = Image.open(savepath)
    location['x']=825
    location['y']=495
    left = location['x']
    top = location['y']
    right = location['x'] + size['width']
    bottom = location['y'] + size['height']
    im = im.crop((left, top, right, bottom))
    im.save(savepath)
    im = im.convert('L')
    binaryImage = im.point(initTable(), '1')
    # binaryImage.show()
    checkcode = pytesseract.image_to_string(binaryImage, config='-psm 7')
    for r in rep:
        checkcode = checkcode.replace(r, rep[r])
    print(checkcode)
    return checkcode

def login():
    browser.get('http://gsmis.graduate.buaa.edu.cn/gsmis/main.do')
    browser.maximize_window()
    input_id=browser.find_element_by_xpath('//input[@name="id"]')
    input_password =browser.find_element_by_xpath('//input[@name="password"]')
    input_checkcode=browser.find_element_by_xpath('//input[@name="checkcode"]')
    img_checkcode=browser.find_element_by_xpath('//img[@src="/gsmis/Image.do"]')

    location = img_checkcode.location
    size = img_checkcode.size
    browser.save_screenshot('checkcode.png')
    checkcode=getCheckcode('checkcode.png',location,size)

    input_id.send_keys(data['id'])
    input_password.send_keys(data['password'])
    input_checkcode.send_keys(checkcode)

    lg_button=browser.find_element_by_xpath('//img[@onclick="document.forms[0].submit()"]')

    time.sleep(1)
    lg_button.click()

def navigate():
    time.sleep(1)
    browser.get("http://gsmis.graduate.buaa.edu.cn/gsmis/toModule.do?prefix=/py&page=/pySelectCourses.do?do=xsXuanKe")
    browser.switch_to.frame('frmme')
    browser.switch_to.frame('leftFrame')
    xuanke=browser.find_element_by_xpath('//a[@href="/gsmis/py/pySelectCourses.do?do=xuanBiXiuKe"]')
    xuanke.click()
    browser.switch_to.parent_frame()
    browser.switch_to.frame('mainFrame')

def prase():
    lessons=browser.find_elements_by_class_name("tablefont2")
    for lesson in lessons:
        infos=lesson.find_elements_by_xpath("./td")
        is_selected = infos[0].find_element_by_xpath("./input").is_selected()
        if(is_selected):
            info={
                'when&where':infos[1].text,
                'classification': infos[3].text,
                'name': infos[4].text,
                'teacher': infos[10].text,
                'remian': infos[12].text,
            }
            yield(info)


MONGO_URL = 'localhost'
MONGO_DB = 'jiaowu'
MONGO_COLLECTION = 'Curriculum'
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(result):
    """
    保存至MongoDB
    :param result: 结果
    """
    try:
        if db[MONGO_COLLECTION].insert(result):
            print('存储到MongoDB成功')
    except Exception:
        print('存储到MongoDB失败')

login()
navigate()
for res in prase():
    print(res)
    save_to_mongo(res)