Rumah pembangunan bahagian belakang Tutorial Python Python爬虫模拟登陆教务处并且保存数据到本地

Python爬虫模拟登陆教务处并且保存数据到本地

Apr 04, 2017 pm 01:30 PM
python

刚开始接触Python,看很多人玩爬虫我也想玩,找来找去发现很多人用网络爬虫干的第一件事就是模拟登陆,增加点难度就是模拟登陆后在获取数据,但是网上好少有Python 3.x的模拟登陆Demo可以参考,加上自己也不怎么懂Html,所以这第一个Python爬虫写的异常艰难,不过最终结果还是尽如人意的,下面把这次学习的过程整理一下。

工具

  • 系统:win7 64位系统

  • 浏览器:Chrome

  • Python版本:Python 3.5 64-bit

  • IDE:JetBrains PyCharm (貌似很多人都用这个)

我把目标瞄准了我们的教务处,这次爬虫的目的是从教务处获取成绩并且把成绩输入Excel表格中保存起来,我们学校教务处的地址是:http://jwc.ecjtu.jx.cn/ ,往常每次我们获取成绩都需要先进入教务处,然后点击成绩查询,输入公共的账号密码进入,最后输入相关信息获取成绩表格,这里登陆不需要验证码省了我一番功夫,这样我们先进入成绩查询系统登陆界面,先看看怎么模拟登陆这个过程,在Chrome浏览器下按F12打开开发者面板:

Python爬虫模拟登陆教务处并且保存数据到本地

开发者面板

这里我们学校的教务处查询系统的密码是公共的jwc也就是拼音缩写,我们输入用户名和密码点击登陆,这时候注意POST请求

Python爬虫模拟登陆教务处并且保存数据到本地

注意post请求

发现了什么,好像Chrome并没有把Post提交的表单信息保留下来直接跳转到了另一个界面然后展示另一个界面的数据,这里就需要我们自己动手操作一下,注意开发者面板左上角的小红点表示这时候正在抓取数据,如果点击一下就会变成灰色,就可以变相地保存下当时抓取到的包,我在点击登陆后新界面未刷新出来之前点击了这个小红点,如愿以偿的得到了Post的表单数据:

Python爬虫模拟登陆教务处并且保存数据到本地

得到post表单数据

这样就获取了浏览器在登陆时候向服务器传递的表单数据,看一下这个表单都有些什么:

Python爬虫模拟登陆教务处并且保存数据到本地

查看表单数据

这里看到我们需要传递三个参数,分别是:user、pass、Submit,可以很容易的理解这几个单词的字面意思,这样有了思路,我们就可以写出这次代码的第一步:模拟登陆教务处直接上代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
url = 'http://jwc.ecjtu.jx.cn/mis_o/login.php'
datas = {'user': 'jwc',
         'pass': 'jwc',
         'Submit': '%CC%E1%BD%BB'
         }
headers = {'Referer': 'http://jwc.ecjtu.jx.cn/mis_o/login.htm',
           'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 '
                         '(KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
           'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
           'Accept-Language': 'zh-CN,zh;q=0.8',
           }
sessions = requests.session()
response = sessions.post(url, headers=headers, data=datas)
print(response.status_code)
Salin selepas log masuk

代码输出:

200
Salin selepas log masuk

说明我们模拟登陆成功了,这里用到了Requests模块,还不会使用的可以查看中文文档 ,它给自己的定义是:HTTP for Humans,因为简单易用易上手,我们只需要传入Url地址,构造请求头,传入post方法需要的数据,就可以模拟浏览器登陆了,这里因为有进一步获取成绩的操作所以使用了session来保持连接,这里单看最后的返回码的话我们是成功了的,具体如何还要看下一步操作,接下来:

Python爬虫模拟登陆教务处并且保存数据到本地

抓包

这里为了简便代码我们设定输入学号查询所有成绩,减少其他判断,同样对Post数据进行抓包:

Python爬虫模拟登陆教务处并且保存数据到本地

对post数据抓包

同样查看Post的数据:

Python爬虫模拟登陆教务处并且保存数据到本地

查看post数据

因为这里就分析输入学号的情况所以其他都为空,这样我们就可以写出查询成绩的代码:

    score_healders = {'Connection': 'keep-alive',
                      'User - Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
                                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
                      'Content - Type': 'application / x - www - form - urlencoded',
                      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                      'Content - Length': '69',
                      'Host': 'jwc.ecjtu.jx.cn',
                      'Referer': 'http: // jwc.ecjtu.jx.cn / mis_o / main.php',
                      'Upgrade - Insecure - Requests': '1',
                      'Accept - Language': 'zh - CN, zh;q = 0.8'
                      }
    score_url = 'http://jwc.ecjtu.jx.cn/mis_o/query.php?start=' + str(
        pagenum) + '&job=see&=&Name=&Course=&ClassID=&Term=&StuID=' + num
    score_data = {'Name': '',
                  'StuID': num,
                  'Course': '',
                  'Term': '',
                  'ClassID': '',
                  'Submit': '%B2%E9%D1%AF'
                  }

    score_response = sessions.post(score_url, data=score_data, headers=score_healders)
    content = score_response.content
Salin selepas log masuk

这里解释一下上面的代码,上面的score_url 并不是浏览器上显示的地址,我们要获取真正的地址,在Chrome下右键--查看网页源代码,找到这么一行:

a href=query.php?start=1&job=see&=&Name=&Course=&ClassID=&Term=&StuID=xxxxxxx
Salin selepas log masuk

这个才是真正的地址,点击这个地址转入的才是真正的界面,因为这里成绩数据较多,所以这里采用了分页显示,这个start=1说明是第一页,这个参数是可变的需要我们传入,还有StuID后面的是我们输入的学号,这样我们就可以拼接出Url地址:

score_url = 'http://jwc.ecjtu.jx.cn/mis_o/query.php?start=' + str(pagenum) + '&job=see&=&Name=&Course=&ClassID=&Term=&StuID=' + num
Salin selepas log masuk

同样使用Post方法传递数据并获取响应的内容:

score_response = sessions.post(score_url, data=score_data,headers=score_healders)
content = score_response.content
Salin selepas log masuk

这里采用Beautiful Soup 4.2.0来解析返回的响应内容,因为我们要获取的是成绩,这里到教务处成绩查询界面,查看获取到的成绩在网页中是以表格的形式存在:

Python爬虫模拟登陆教务处并且保存数据到本地

网页源代码


观察表格的网页源代码:

<table align=center border=1>
<tr><td bgcolor=009999>学期</td>
<td bgcolor=009999>学号</td>
<td bgcolor=009999>姓名</td>
<td bgcolor=009999>课程</td>
<td bgcolor=009999>课程要求</td>
<td bgcolor=009999>学分</td>
<td bgcolor=009999>成绩</td>
<td bgcolor=009999>重考一</td>
<td bgcolor=009999>重考二</td></tr>
...
...
</tr></table>
Salin selepas log masuk

这里拿出第一行举例,虽然我不太懂Html但是从这里可以看出来<tr> 代表的是一行,而<td>应该是代表这一行中的每一列,这样就好办了,取出每一行然后分解出每一列,打印输出就可以得到我们要的结果:

from bs4 import BeautifulSoup
soup = BeautifulSoup(content, 'html.parser')
# 找到每一行
target = soup.findAll('tr')
Salin selepas log masuk

这里分解每一列的时候要小心,因为这里表格分成了三页显示,每页最多显示30条数据,这里因为只是收集已经毕业的学生的成绩数据所以不对其他数据量不足的学生成绩的情况做统计,默认收集的都是大四毕业的学生成绩数据。这里采用两个变量ij分别代表行和列:

# 注:这里的print单纯是我为了验证结果打印在PyCharm的控制台上而已
i=0, j=0
for tag in target[1:]:
            tds = tag.findAll('td')
            # 每一次都是从列头开始获取
            j = 0
            # 学期
            semester = str(tds[0].string)
            if semester == 'None':
                break
            else:
                print(semester.ljust(6) + '\t\t\t', end='')
            # 学号
            studentid = tds[1].string
            print(studentid.ljust(14) + '\t\t\t', end='')
            j += 1
            # 姓名
            name = tds[2].string
            print(name.ljust(3) + '\t\t\t', end='')
            j += 1
            # 课程
            course = tds[3].string
            print(course.ljust(20, ' ') + '\t\t\t', end='')
            j += 1
            # 课程要求
            requirments = tds[4].string
            print(requirments.ljust(10, ' ') + '\t\t', end='')
            j += 1
            # 学分
            scredit = tds[5].string
            print(scredit.ljust(2, ' ') + '\t\t', end='')
            j += 1
            # 成绩
            achievement = tds[6].string
            print(achievement.ljust(2) + '\t\t', end='')
            j += 1
            # 重考一
            reexaminef = tds[7].string
            print(reexaminef.ljust(2) + '\t\t', end='')
            j += 1
            # 重考二
            reexamines = tds[8].string
            print(reexamines.ljust(2) + '\t\t')
            j += 1
            i += 1
Salin selepas log masuk

这里查了很多别人的博客都是用正则表达式来分解数据,表示自己的正则写的并不好也尝试了但是没成功,所以无奈选择这种方式,如果有人有测试成功的正则欢迎跟我说一声,我也学习学习。

把数据保存到Excel

因为已经清楚了这个网页保存成绩的具体结构,所以顺着每次循环解析将数据不断加以保存就是了,这里使用xlwt写入数据到Excel,因为xlwt模块打印输出到Excel中的样式宽度偏小,影响观看,所以这里还加入了一个方法去控制打印到Excel表格中的样式:

file = xlwt.Workbook(encoding='utf-8')
table = file.add_sheet('achieve')
# 设置Excel样式
def set_style(name, height, bold=False):
    style = xlwt.XFStyle()  # 初始化样式
    font = xlwt.Font()  # 为样式创建字体
    font.name = name  # 'Times New Roman'
    font.bold = bold
    font.color_index = 4
    font.height = height
    style.font = font
    return style
Salin selepas log masuk

运用到代码中:

for tag in target[1:]:
            tds = tag.findAll('td')
            j = 0
            # 学期
            semester = str(tds[0].string)
            if semester == 'None':
                break
            else:
                print(semester.ljust(6) + '\t\t\t', end='')
                table.write(i, j, semester, set_style('Arial', 220))
            # 学号
            studentid = tds[1].string
            print(studentid.ljust(14) + '\t\t\t', end='')
            j += 1
            table.write(i, j, studentid, set_style('Arial', 220))
            table.col(i).width = 256 * 16
            # 姓名
            name = tds[2].string
            print(name.ljust(3) + '\t\t\t', end='')
            j += 1
            table.write(i, j, name, set_style('Arial', 220))
            # 课程
            course = tds[3].string
            print(course.ljust(20, ' ') + '\t\t\t', end='')
            j += 1
            table.write(i, j, course, set_style('Arial', 220))
            # 课程要求
            requirments = tds[4].string
            print(requirments.ljust(10, ' ') + '\t\t', end='')
            j += 1
            table.write(i, j, requirments, set_style('Arial', 220))
            # 学分
            scredit = tds[5].string
            print(scredit.ljust(2, ' ') + '\t\t', end='')
            j += 1
            table.write(i, j, scredit, set_style('Arial', 220))
            # 成绩
            achievement = tds[6].string
            print(achievement.ljust(2) + '\t\t', end='')
            j += 1
            table.write(i, j, achievement, set_style('Arial', 220))
            # 重考一
            reexaminef = tds[7].string
            print(reexaminef.ljust(2) + '\t\t', end='')
            j += 1
            table.write(i, j, reexaminef, set_style('Arial', 220))
            # 重考二
            reexamines = tds[8].string
            print(reexamines.ljust(2) + '\t\t')
            j += 1
            table.write(i, j, reexamines, set_style('Arial', 220))
            i += 1

file.save('demo.xls')
Salin selepas log masuk

最后稍加整合,写成一个方法:

# 获取成绩
# 这里num代表输入的学号,pagenum代表页数,总共76条数据,一页30条所以总共有三页
def getScore(num, pagenum, i, j):
    score_healders = {'Connection': 'keep-alive',
                      'User - Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
                                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
                      'Content - Type': 'application / x - www - form - urlencoded',
                      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                      'Content - Length': '69',
                      'Host': 'jwc.ecjtu.jx.cn',
                      'Referer': 'http: // jwc.ecjtu.jx.cn / mis_o / main.php',
                      'Upgrade - Insecure - Requests': '1',
                      'Accept - Language': 'zh - CN, zh;q = 0.8'
                      }
    score_url = 'http://jwc.ecjtu.jx.cn/mis_o/query.php?start=' + str(
        pagenum) + '&job=see&=&Name=&Course=&ClassID=&Term=&StuID=' + num
    score_data = {'Name': '',
                  'StuID': num,
                  'Course': '',
                  'Term': '',
                  'ClassID': '',
                  'Submit': '%B2%E9%D1%AF'
                  }

    score_response = sessions.post(score_url, data=score_data, headers=score_healders)
    # 输出到文本
    with open('text.txt', 'wb') as f:
        f.write(score_response.content)
    content = score_response.content
    soup = BeautifulSoup(content, 'html.parser')
    target = soup.findAll('tr')
    try:
        for tag in target[1:]:
            tds = tag.findAll('td')
            j = 0
            # 学期
            semester = str(tds[0].string)
            if semester == 'None':
                break
            else:
                print(semester.ljust(6) + '\t\t\t', end='')
                table.write(i, j, semester, set_style('Arial', 220))
            # 学号
            studentid = tds[1].string
            print(studentid.ljust(14) + '\t\t\t', end='')
            j += 1
            table.write(i, j, studentid, set_style('Arial', 220))
            table.col(i).width = 256 * 16
            # 姓名
            name = tds[2].string
            print(name.ljust(3) + '\t\t\t', end='')
            j += 1
            table.write(i, j, name, set_style('Arial', 220))
            # 课程
            course = tds[3].string
            print(course.ljust(20, ' ') + '\t\t\t', end='')
            j += 1
            table.write(i, j, course, set_style('Arial', 220))
            # 课程要求
            requirments = tds[4].string
            print(requirments.ljust(10, ' ') + '\t\t', end='')
            j += 1
            table.write(i, j, requirments, set_style('Arial', 220))
            # 学分
            scredit = tds[5].string
            print(scredit.ljust(2, ' ') + '\t\t', end='')
            j += 1
            table.write(i, j, scredit, set_style('Arial', 220))
            # 成绩
            achievement = tds[6].string
            print(achievement.ljust(2) + '\t\t', end='')
            j += 1
            table.write(i, j, achievement, set_style('Arial', 220))
            # 重考一
            reexaminef = tds[7].string
            print(reexaminef.ljust(2) + '\t\t', end='')
            j += 1
            table.write(i, j, reexaminef, set_style('Arial', 220))
            # 重考二
            reexamines = tds[8].string
            print(reexamines.ljust(2) + '\t\t')
            j += 1
            table.write(i, j, reexamines, set_style('Arial', 220))
            i += 1
    except:
        print('出了一点小Bug')
    file.save('demo.xls')
Salin selepas log masuk

在模拟登陆操作后增加一个判断:

# 判断是否登陆
def isLogin(num):
    return_code = response.status_code
    if return_code == 200:
        if re.match(r"^\d{14}$", num):
            print('请稍等')
        else:
            print('请输入正确的学号')
        return True
    else:
        return False
Salin selepas log masuk

最后在main中这么调用:

if name == 'main':
    num = input('请输入你的学号:')
    if isLogin(num):
        getScore(num, pagenum=0, i=0, j=0)
        getScore(num, pagenum=1, i=31, j=0)
        getScore(num, pagenum=2, i=62, j=0)
Salin selepas log masuk

在PyCharm下按alt+shift+x快捷键运行程序:

Python爬虫模拟登陆教务处并且保存数据到本地

控制台输出

控制台会有如下输出(这里只截取部分,不要吐槽没有对齐,这里我也用了格式化输出还是不太行,不过最起码出来了结果,而且我们的目的是输出到Excel中不是吗)

Python爬虫模拟登陆教务处并且保存数据到本地

控制台输出

然后去程序根目录找看看有没有生成一个叫demo.xls的文件,我的程序就放在桌面,所以去桌面找:

Python爬虫模拟登陆教务处并且保存数据到本地

桌面图标

点开查看是否成功获取:

Python爬虫模拟登陆教务处并且保存数据到本地

最终获取结果

至此,大功告成

Atas ialah kandungan terperinci Python爬虫模拟登陆教务处并且保存数据到本地. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk mengintegrasikan perkhidmatan Node.js atau Python dengan cekap di bawah seni bina lampu? Bagaimana untuk mengintegrasikan perkhidmatan Node.js atau Python dengan cekap di bawah seni bina lampu? Apr 01, 2025 pm 02:48 PM

Ramai pemaju laman web menghadapi masalah mengintegrasikan perkhidmatan node.js atau python di bawah seni bina lampu: lampu sedia ada (Linux Apache MySQL PHP) Laman web seni bina memerlukan ...

Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Apr 01, 2025 pm 05:09 PM

Penyelesaian kepada Isu Kebenaran Semasa Melihat Versi Python di Terminal Linux Apabila anda cuba melihat versi Python di Terminal Linux, masukkan Python ...

Apakah sebabnya mengapa fail penyimpanan berterusan saluran paip tidak dapat ditulis apabila menggunakan crawler scapy? Apakah sebabnya mengapa fail penyimpanan berterusan saluran paip tidak dapat ditulis apabila menggunakan crawler scapy? Apr 01, 2025 pm 04:03 PM

Apabila menggunakan crawler scapy, sebab mengapa fail penyimpanan berterusan paip tidak boleh ditulis? Perbincangan Ketika belajar menggunakan Crawler Scapy untuk Crawler Data, anda sering menemui ...

Python Hourglass Graph Lukisan: Bagaimana untuk mengelakkan kesilapan yang tidak ditentukan? Python Hourglass Graph Lukisan: Bagaimana untuk mengelakkan kesilapan yang tidak ditentukan? Apr 01, 2025 pm 06:27 PM

Bermula dengan Python: Lukisan Grafik Hourglass dan Pengesahan Input Artikel ini akan menyelesaikan masalah definisi berubah -ubah yang dihadapi oleh pemula python dalam program lukisan grafik Hourglass. Kod ...

Apakah sebabnya mengapa Pool Proses Python mengendalikan permintaan TCP serentak dan menyebabkan pelanggan terjebak? Apakah sebabnya mengapa Pool Proses Python mengendalikan permintaan TCP serentak dan menyebabkan pelanggan terjebak? Apr 01, 2025 pm 04:09 PM

Proses Python Pool mengendalikan permintaan TCP serentak yang menyebabkan pelanggan terjebak. Apabila menggunakan Python untuk pengaturcaraan rangkaian, adalah penting untuk mengendalikan permintaan TCP serentak dengan cekap. …

Bagaimana untuk melihat fungsi asal yang terkandung secara dalaman oleh python funcools.partial Object? Bagaimana untuk melihat fungsi asal yang terkandung secara dalaman oleh python funcools.partial Object? Apr 01, 2025 pm 04:15 PM

Sangat meneroka kaedah tontonan python funcools.partial Object in Funcools.Partial Menggunakan Python ...

Pembangunan Aplikasi Desktop Cross-Platform Python: Perpustakaan GUI mana yang terbaik untuk anda? Pembangunan Aplikasi Desktop Cross-Platform Python: Perpustakaan GUI mana yang terbaik untuk anda? Apr 01, 2025 pm 05:24 PM

Pilihan Perpustakaan Pembangunan Aplikasi Desktop Python Python Banyak pemaju Python ingin membangunkan aplikasi desktop yang boleh dijalankan pada kedua-dua sistem Windows dan Linux ...

Adakah Google dan AWS menyediakan sumber imej Pypi awam? Adakah Google dan AWS menyediakan sumber imej Pypi awam? Apr 01, 2025 pm 05:15 PM

Ramai pemaju bergantung kepada PYPI (PythonPackageIndex) ...

See all articles