首次写爬虫!,requests库加beautifulsoup(美味汤)爬取学校教室课程表

一个学校朋友让我帮他做一个查询空余教室的小程序,然而又没有现成的数据,只有自己动手找了.

不过我也是第一次写爬虫

我们学校教务网中有一个栏目是关于全校课程的汇总,其中有一个就是按教室进行分类,每一间教室都有一张独立的课程表,其中记录了该教室一个周要上的课和上课周数.

找到了数据,那就开始动手爬吧!


高能! 由于学校的教务系统不知道是哪个年代做的了,所以里面可能会出现各种各样的坑.

首先选定我们的数据库,我习惯于使用mysql数据库,而Python也有很成熟的类库可以使用.

然后设计存储结构,首先我们需要看一下我们可以得到的数据和需要的数据:

       

从这里可以看到,我们可以从这个表格中获取到教室位置, 校区, 容纳人数, 教室类别这几种数据

从教室的课表中可以得到上课周时间,单双周上课情况,上课人数,上课课程等等. 目前我们需要的数据只有上课周,教室位置,校区,容纳人数,单双周上课情况,教室种类.

现在可以建立数据表了:

    {

int id,
//主键
varchar address,
//教室位置
varchar odd,
//单周
varchar even,
//双周
varchar area,
//校区
varchar category,
//种类
varchar population,
//容纳人数
 }

我们先在数据库中建立好数据表,具体步骤我就不写了.

现在打开第一张图的网页,查看请求头和源码

呃,好像有点儿长,就这样吧.

开始动手写

import requests as req
from bs4 import BeautifulSoup as bs
import re
import MySQLdb

构建initReq函数:

def initReq(url, header):
    response = req.get(url, headers=header)
    content = bs(response.content, "html.parser")
    return content
首先获取教室表格
header={
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299",
    "Referer": "http://jiaowu.xxxxxx.edu.cn/web/web/lanmu/kebiao.asp",
    "Cookie": "Hm_lvt_20274609f261feba8dcea77ff3f7070c=1523195886; ASPSESSIONIDCCAADCDT=GPKEFCGBHNKNLHIJBDMHBMGD; jcrj%5Fuser=web; jcrj%5Fpwd=web; jcrj%5Fauth=True; jcrj%5Fsession=jwc%5Fcheck%2Cauth%2Cuser%2Cpwd%2Cjwc%5Fcheck%2Ctymfg%2Csf%2C; jcrj%5Fjwc%5Fcheck=y; jcrj%5Ftymfg=%C0%B6%C9%AB%BA%A3%CC%B2; jcrj%5Fsf=%D1%A7%C9%FA"}
    response=res.initReq("http://jiaowu.xxxxx.edu.cn/web/web/lanmu/jshi.asp",header=header)
    res.getClassRoom(response)

构建GetClassRoom函数,从已经获取到html中获取教室信息:

def getClassRoom(html):
    table = html.find_all("table")
    tr = table[-2].find_all("tr")#find_all方法会遍历出所有的指定元素,返回一个列表
    for i in tr[1:]:
        roomInfo = i.find_all("td")
        area = roomInfo[1].get_text()
        category = roomInfo[4].get_text()
        address = roomInfo[0].find("input")
        address = address["value"]
        link = roomInfo[-1].find("a")
        link = link["href"]
        population = roomInfo[3].get_text()
        getRoomInfo(link=link, address=address, category=category, area=area, population=population)
def getTable(url):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299",
        "Referer": "http://jiaowu.xxxxx.edu.cn/web/web/lanmu/jshi.asp",
        "Cookie": "Hm_lvt_20274609f261feba8dcea77ff3f7070c=1523195886; ASPSESSIONIDCCAADCDT=GPKEFCGBHNKNLHIJBDMHBMGD;jcrj%5Fuser=web; jcrj%5Fpwd=web; jcrj%5Fauth=True;jcrj%5Fsession=jwc%5Fcheck%2Cauth%2Cuser%2Cpwd%2Cjwc%5Fcheck%2Ctymfg%2Csf%2C;jcrj%5Fjwc%5Fcheck=y;jcrj%5Ftymfg=%C0%B6%C9%AB%BA%A3%CC%B2;jcrj%5Fsf=%D1%A7%C9%FA"}
    result = initReq("http://jiaowu.xxxx.edu.cn/web/web/lanmu/" + url, headers)
    return result

defTable是为了得到课程表


我先一步一步的对html进行解剖,然后提取相应的数据.

在这一步可以得到每一间教室的课程表连接,现在还需要一个获取课标信息的操作,也就是上面我们call的getRoomInfo函数.

def getRoomInfo(link, category, area, address, population):
    result = getTable(link)
    table = result.find_all("table")
    table = table[2].find_all("table")
    tr = table[2].find_all("tr")
    # print(tr[1].find_all("tr"))
    l = 0
    tr = tr[1].find_all("tr")
    for i in tr:
        l += 1
        tds = i.find_all("td")
        w = 0
        for td in tds[-7:]:
            w += 1
            result = getLeason(td)
            leason = str(w) + "-" + str(l)
            db.con(category=category, area=area, address=address, curLeasonInfo=result, population=population,
                   leason=leason)

现在提取出整张课程表,


从htm中可以看出tr的子节点td中就是我们需要的数据信息,我们可以通过find_all方法提出所有的tr,然后再遍历tr提取出其中td,最后使用get_text()方法得到节点中的数据

但是根据课表可以看出,tr中存在合并后的单元格,那么在提取数据时如果从右向左进行遍历肯定是不行的,我们需要截取出真正蕴含信息的部分.


在这里的时候我遇到了一个问题


这里存在一个空tr,这个空tr为啥会出现呢?有什么意义?我不知道,但是我知道他给我接下来的操作造成了麻烦.

我用find_all获取tr标记的时候发现出现了问题,最后数据提取出来全都是错乱的,课程时间完全不对,甚至一天多出来了十节课. 等我输出这个tr的时候发现, tr中原本应该只有七条的列表多出来好几条.然后我想到了这个多出来的tr标记.


这里看不清,放个大图


现在可以看到,这个tr中蕴含了我需要的所有tr. 接下来,我要验证我的猜想(教务网编写人员的失误----tr未结束):


这是原始html.

这是在火狐中开发者模式中的.

测试代码

from bs4 import BeautifulSoup as bs
text="""
<table>
<tbody>
	<tr><td>a</td></tr>	<tr>  <tr><td>a</td></tr>	<tr><td>a</td></tr>	<tr><td>a</td></tr>
</tbody>
"""
html=bs(text,"html.parser")
print(html.find_all("tr"))
结果:


从结果中我们可以看到,这里我们原本未结束的tr在find_all执行的时候自动把它给补全了!!!!!而之前的浏览器开发者模式中显示的又是浏览器自动优化后的结果,这误导直接造成了我们最后的结果错误.

def getTr(html):
    tr = []
    j = 0
    for i in html[1:]:
        j += 1
        k = 0
        trTemp = i.find_all("tr")
        if trTemp:
            for z in trTemp:
                if len(z) > 3:  # 此处存在判定问题,因为教务网中一个tr错误,这里trtemp列表中会多出一条包含所有的剩余tr的项
                    k += 1
                    tr.append(z)
                    pass
                pass
            pass
    return tr

上面给出的代码是改造好的代码. 我们可以tr列表中看到列表第二项就已经包含了所有信息,所以我们只需要得到第二项就可以了

def getLeason(td):
    text = td.get_text()
    mask = re.compile("[1-9]-[1-9]{2}")
    week = mask.findall(text)
    single = 0  # 是否单周有课
    double = 0
    if len(week) != 0:
        if re.search(r"\(单\)", text):
            temp = re.compile("\(单\)\n[1-9]-[0-9]{2}")  # 编译寻找单周上课时间的上课周的正则
            text1 = temp.search(text)
            if text1:
                result = mask.search(text1.group())  # 找出上课周
                single = result.group()  # 单周上课时间
            pass
        elif re.search(r"\(双\)", text):
            double = 1
            temp = re.compile("\(双\)\n[1-9]-[0-9]{2}")  # 编译寻找单周上课时间的上课周的正则
            text1 = temp.search(text)
            if text1:
                result = mask.search(text1.group())  # 找出上课周
                double = result.group()  # 双周上课时间
            pass
        else:
            single = double = week[0]  # 上课不分单双周
            pass
    else:
        print("本堂没课")
        pass
    return {"single": single, "double": double}

使用re库提取处对应信息,这里需要区分单双周上课,所以正则表达式

\(单\)\n[1-9]-[0-9]{2}

这一步为了得到整个单周上课信息,然后再提出上课时间

[1-9]-[0-9]{2}
End





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值