剛開始接觸Python,看很多人玩爬蟲我也想玩,找來找去發現很多人用網路爬蟲幹的第一件事就是模擬登陸,增加點難度就是模擬登陸後在獲取數據,但是網上好少有Python 3.x的模擬登陸Demo可以參考,加上自己也不怎麼懂Html,所以這第一個Python爬蟲寫的異常艱難,不過最終結果還是盡如人意的,下面把這次學習的過程整理一下。 #工具
系統:win7 64位元系統
- #瀏覽器: Chr
- omePython版本:Python 3.5 64-bit
- #IDE:JetBrains PyCharm (貌似很多人都用這個)
- #我把目標瞄準了我們的教務處,這次爬蟲的目的是從教務處獲取成績並且把成績輸入Excel 表格
查詢,輸入公共的帳號密碼進入,最後輸入相關資訊取得成績表格,這裡登陸不需要驗證碼省了我一番功夫,這樣我們先進入成績查詢系統登陸介面,先看看怎麼模擬登陸這個過程,在Chrome瀏覽器下按F12打開開發者面板:
開發者面板
POST請求
:#注意post請求
##發現了什麼,好像Chrome並沒有把Post提交的表單資訊保留下來直接跳到了另一個介面然後展示另一個介面的數據,這裡就需要我們自己動手操作一下,注意開發者面板左上角的小紅點表示這時候正在抓取數據,如果點擊一下就會變成灰色,就可以變相地保存下當時抓取到的包,我在點擊登陸後新界面未刷新出來之前點擊了這個小紅點,如願以償的得到了Post的表單數據:
得到post表單數據
#這樣就獲取了瀏覽器在登陸時候向伺服器傳遞的表單數據,看一下這個表單都有些什麼:
檢視表單資料
這裡看到我們需要傳遞三個參數,分別是: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)
程式碼輸出:200
說明我們模擬登陸成功了,這裡用到了Requests模組,還不會使用的可以查看中文文檔,它給自己的定義是:HTTP
Humans,因為簡單易用易上手,我們只需要傳入Url地址,構造請求頭,傳入post方法需要的數據,就可以模擬瀏覽器登陸了,這裡因為有進一步獲取成績的操作所以使用了session來保持連接,這裡單看最後的返回碼的話我們是成功了的,具體如何還要看下一步操作,接下來:
抓包
這裡為了簡單程式碼我們設定輸入學號查詢所有成績,減少其他判斷,同樣對Post資料進行抓包:
對post資料抓包
同樣查看Post的資料:
#查看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
这里解释一下上面的代码,上面的score_url
并不是浏览器上显示的地址,我们要获取真正的地址,在Chrome下右键--查看网页源代码,找到这么一行:
a href=query.php?start=1&job=see&=&Name=&Course=&ClassID=&Term=&StuID=xxxxxxx
这个才是真正的地址,点击这个地址转入的才是真正的界面,因为这里成绩数据较多,所以这里采用了分页显示,这个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
同样使用Post方法传递数据并获取响应的内容:
score_response = sessions.post(score_url, data=score_data,headers=score_healders) content = score_response.content
这里采用Beautiful Soup 4.2.0来解析返回的响应内容,因为我们要获取的是成绩,这里到教务处成绩查询界面,查看获取到的成绩在网页中是以表格的形式存在:
网页源代码
观察表格的网页源代码:
学期 | 学号 | 姓名 | 课程 | 课程要求 | 学分 | 成绩 | 重考一 | 重考二 |
这里拿出第一行举例,虽然我不太懂Html但是从这里可以看出来<tr> 代表的是一行,而<code><td>应该是代表这一行中的每一列,这样就好办了,取出每一行然后分解出每一列,打印输出就可以得到我们要的结果:<pre class="brush:php;toolbar:false">from bs4 import BeautifulSoup
soup = BeautifulSoup(content, 'html.parser')
# 找到每一行
target = soup.findAll('tr')</pre>
<p>这里分解每一列的时候要小心,因为这里表格分成了三页显示,每页最多显示30条数据,这里因为只是收集已经毕业的学生的成绩数据所以不对其他数据量不足的学生成绩的情况做统计,默认收集的都是大四毕业的学生成绩数据。这里采用两个<a href="http://www.php.cn/wiki/70.html" target="_blank">变量</a><code>i
和j
分别代表行和列:
# 注:这里的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
这里查了很多别人的博客都是用正则表达式来分解数据,表示自己的正则写的并不好也尝试了但是没成功,所以无奈选择这种方式,如果有人有测试成功的正则欢迎跟我说一声,我也学习学习。
把数据保存到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
运用到代码中:
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')
最后稍加整合,写成一个方法:
# 获取成绩 # 这里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')
在模拟登陆操作后增加一个判断:
# 判断是否登陆 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
最后在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)
在PyCharm下按alt
+shift
+x
快捷键运行程序:
控制台输出
控制台会有如下输出(这里只截取部分,不要吐槽没有对齐,这里我也用了格式化输出还是不太行,不过最起码出来了结果,而且我们的目的是输出到Excel中不是吗)
控制台输出
然后去程序根目录找看看有没有生成一个叫demo.xls
的文件,我的程序就放在桌面,所以去桌面找:
桌面图标
点开查看是否成功获取:
最终获取结果
至此,大功告成
以上是Python爬蟲模擬登陸教務處並且儲存資料到本地的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和C 各有優勢,選擇應基於項目需求。 1)Python適合快速開發和數據處理,因其簡潔語法和動態類型。 2)C 適用於高性能和系統編程,因其靜態類型和手動內存管理。

選擇Python還是C 取決於項目需求:1)如果需要快速開發、數據處理和原型設計,選擇Python;2)如果需要高性能、低延遲和接近硬件的控制,選擇C 。

通過每天投入2小時的Python學習,可以有效提升編程技能。 1.學習新知識:閱讀文檔或觀看教程。 2.實踐:編寫代碼和完成練習。 3.複習:鞏固所學內容。 4.項目實踐:應用所學於實際項目中。這樣的結構化學習計劃能幫助你係統掌握Python並實現職業目標。

在兩小時內高效學習Python的方法包括:1.回顧基礎知識,確保熟悉Python的安裝和基本語法;2.理解Python的核心概念,如變量、列表、函數等;3.通過使用示例掌握基本和高級用法;4.學習常見錯誤與調試技巧;5.應用性能優化與最佳實踐,如使用列表推導式和遵循PEP8風格指南。

Python適合初學者和數據科學,C 適用於系統編程和遊戲開發。 1.Python簡潔易用,適用於數據科學和Web開發。 2.C 提供高性能和控制力,適用於遊戲開發和系統編程。選擇應基於項目需求和個人興趣。

Python更適合數據科學和快速開發,C 更適合高性能和系統編程。 1.Python語法簡潔,易於學習,適用於數據處理和科學計算。 2.C 語法複雜,但性能優越,常用於遊戲開發和系統編程。

每天投入兩小時學習Python是可行的。 1.學習新知識:用一小時學習新概念,如列表和字典。 2.實踐和練習:用一小時進行編程練習,如編寫小程序。通過合理規劃和堅持不懈,你可以在短時間內掌握Python的核心概念。

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

記事本++7.3.1
好用且免費的程式碼編輯器