Python--校园网爬虫记

 

查成绩,算分数,每年的综合测评都是个固定的过程,作为软件开发者,这些过程当然可以交给代码去做,通过脚本进行网络请求获取数据,然后直接进行计算得到基础分直接填表就好了,查成绩再手动计算既容易出错也繁琐,所以本篇的内容就是开发一个爬虫脚本取抓取成绩表,至于综合测评计算,这个没什么意义这里就不说了,分数都有了就都够了。

我们的目的就是通过编写脚本,模仿浏览器进行请求获取源码,再进行解析本地化(或者直接计算)

要抓取到数据,其实方案不止一种,这里会介绍两种不同的方案,达到同样的目的:

  • 模仿浏览器进行请求(速度快)
  • 操作浏览器进行请求(速度慢)

先说第一种,这种方案是普遍的爬虫技术,因为爬取的内容不多,对速度要求也不够,所以就是很简单的一个爬虫过程:

  1. 分析请求
  2. 模仿请求

对于普通的校园网,一般不做流量限制,所以就算请求频繁,也基本不用担心IP被封禁,所以编写爬虫代码可以不用太过担心。先说我所在学校的校园网,是杭州方正软件公司开发的。

① 分析请求

分析请求很简单,就是使用浏览器进行请求,然后分析每个请求所发送和接收的信息,这里最简单应该是使用chrome的开发者模式(F12打开)

输入用户名和密码,勾选已认真阅读,接着点击登陆,这样右边的网络窗口中会检查到所有的网络请求,我们只需要找到对应登陆的一个(这里会带有表单):

这个时候,我们可以通过一些测试工具,尝试进行请求对应的这个地址,并且把表单提交上去试试登陆能否成功,如果成功的话,脚本也就可以模拟这个请求,这里用的是chrome商店的一个工具Postman,用法很简单:

登陆成功之后,我们再进行查询成绩:

这里可以看到这次得到了两个新的请求(上图红框的前两个)

仔细观察会发现,第一个请求头中的Referer指向的是第二个请求的地址,所以可以知道,第二个请求是先于第一个请求发送的。其次,我们发现这个请求中也有表单。

再看第二个请求:

它的Referer指向第三个请求,而这个第三个请求实际上登陆成功之后,就已经存在了,它就是请求到主界面的,而这个请求的类型是Get,所以也表明,第三个请求没有传递任何信息给这个请求。

整理可以知道,流程是这样的:

登陆成功后跳转:http://202.192.72.4/xs_main.aspx?xh=2013034743130

点击查询成绩按钮请求:http://202.192.72.4/xscj_gc.aspx?xh=2013034743130&xm=%B3%C2%D6%BE%B7%AB&gnmkdm=N121605 (Get)

点击查询在校成绩请求:http://202.192.72.4/xscj_gc.aspx?xh=2013034743130&xm=%u9648%u5fd7%u5e06&gnmkdm=N121605 (Post)

所以,我们先来模拟第二个,这个请求是Get类型,所以直接请求即可,但是会发现请求会失败,原因是服务器不能知道我们已经进行登陆了:

所以最先想到的办法是带上第一个请求得到的Cookie,但是也是不行,这个时候要用到上面说的Referer标识,这个标识会告诉服务器请求来源,因为登陆成功会在服务器进行登记,这个标记会让服务器知道请求来源于登陆成功的账号:

此时请求返回正常,我们在源码中可以发现有两个隐藏的<input>标签:

这两个标签传递的,其实是第三个请求的参数,这个时候,模拟第三个请求,并且添加对应的Referer(第二个请求的URL),会发现请求也成功了:

这个请求中的url中的一个参数xm被我更改为1了,原本使用的是一种unicode加密编码,把用户名编码过去了,但是实际上这个参数并没有实际意义,%u的格式会破坏Python程序,所以这里直接改成1了。

② 模仿请求

请求分析完毕,就可以开始写代码了:

用到的包:

1 import requests, xlwt, os
2 from bs4 import BeautifulSoup

登录:

 1 def login(s, number, password):
 2     print '正在登录账号:'+number
 3     url = 'http://202.192.72.4/default_gdsf.aspx'
 4     data = {'__EVENTTARGET': 'btnDL',
 5             'TextBox1': number,
 6             'TextBox2': password,
 7             '__VIEWSTATE': '/wEPDwULLTExNzc4NDAyMTBkGAEFHl9fQ29udHJvbHNSZXF1aXJlUG9zdEJhY2tLZXlfXxYBBQVjaGtZRIgvS19wi/UKxQv2qDEuCtWOjJdl',
 8             'chkYD': 'on',
 9             '__EVENTVALIDATION': '/wEWCgKFvrvOBQLs0bLrBgLs0fbZDAK/wuqQDgKAqenNDQLN7c0VAuaMg+INAveMotMNAuSStccFAvrX56AClqUwdU9ySl1Lo85TvdUwz0GrJgI='}
10     s.post(url, data)
11     return s.cookies

登录操作没有给后面的请求传递任何参数,这里的Cookies不是必须的,但是登录是必须的,这样告诉服务器我们后面的请求才是合法的。

点击查询成绩按钮:

 1 def get_data_for_grade(s, number, password):
 2     url = 'http://202.192.72.4/xscj_gc.aspx?xh=' + number + '&xm=%B3%C2%D6%BE%B7%AB&gnmkdm=N121605'
 3     referer = 'http://202.192.72.4/xs_main.aspx?xh=' + number
 4     cookies = login(s, number, password)
 5     response = s.get(url=url, headers={'Referer': referer}, allow_redirects=False, cookies=cookies)
 6     source = response.text
 7     soup = BeautifulSoup(source, 'html.parser')
 8     view_state = soup.find('input', attrs={'id': '__VIEWSTATE'})['value']
 9     event_validation = soup.find('input', attrs={'id': '__EVENTVALIDATION'})['value']
10     states = {'view_state': view_state, 'event_validation': event_validation, 'cookies': cookies, 'origin': url}
11     return states

第五行队请求设置Referer,接着通过BeautifulSoup解析源码得到两个隐藏的<input>标签里面value值,第三个请求要用到。

查询所有成绩请求:

 1 def check_info(s, number, password):
 2     url = 'http://202.192.72.4/xscj_gc.aspx?xh=' + number + '&xm=1&gnmkdm=N121605'
 3     states = get_data_for_grade(s, number, password)
 4     print '登录成功,正在拉取成绩'
 5     data = {
 6         '__VIEWSTATE': states['view_state'],
 7         'ddlXN': '',
 8         'ddlXQ': '',
 9         'Button2': '',
10         '__EVENTVALIDATION': states['event_validation']
11     }
12     response = s.post(url, data=data, cookies=states['cookies'], headers={'Referer': states['origin']},
13                       allow_redirects=False)
14     return response.text

得到成绩单源码之后,就可以进行解析了,这里解析存放到xls表格中:

 1 def writeToFile(source):
 2     print '正在写入文档'
 3     wb = xlwt.Workbook(encoding='utf-8', style_compression=0)
 4     soup = BeautifulSoup(source, "html.parser")
 5     span = soup.find('span', attrs={'id': 'Label5'})
 6     sheet = wb.add_sheet('成绩单', cell_overwrite_ok=True)
 7     table = soup.find(attrs={'id': 'Datagrid1'})
 8     lines = table.find_all('tr')
 9     for i in range(len(lines)):
10         tds = lines[i].find_all('td')
11         for j in range(len(tds)):
12             sheet.write(i, j, tds[j].text)
13     try:
14         os.remove(span.text + '.xls')
15     except:
16         pass
17     wb.save(span.text + '.xls')

最后遍历学号进行爬取,这里只爬取默认账号密码的成绩:

 1 for i in range(1, 55):
 2     num = '2013034743001'
 3     s = requests.session()
 4     try:
 5         if i <= 9:
 6             writeToFile(check_info(s, num[:12] + str(i), num[:12] + str(i)))
 7         else:
 8             writeToFile(check_info(s, num[:11] + str(i), num[:11] + str(i)))
 9     except:
10         pass
11     s.close()

 


第二种方案,是通过模拟浏览器来进行登录,点击按钮等操作获取成绩,这里用到的是自动化测试框架Selenium

这种方案的优点是我们不需要像第一种那样要去分析请求,只需要告诉浏览器要怎么做就行了,但是缺点是速度慢。

  1 # -*- coding: utf-8 -*-
  2 from selenium import webdriver
  3 from selenium.webdriver.common.by import By
  4 from selenium.webdriver.support.wait import WebDriverWait
  5 from selenium.webdriver.common.action_chains import ActionChains
  6 from selenium.webdriver.support import expected_conditions as EC
  7 from selenium.common.exceptions import NoSuchElementException
  8 from selenium.common.exceptions import NoAlertPresentException
  9 from bs4 import BeautifulSoup
 10 import xlwt
 11 import os
 12 
 13 
 14 class Script():
 15     def setUp(self):
 16         self.driver = webdriver.Chrome()
 17         self.driver.implicitly_wait(10)
 18         # self.driver.maximize_window()
 19         self.base_url = "http://202.192.72.4/"
 20         self.verificationErrors = []
 21         self.accept_next_alert = True
 22 
 23     def test_jb(self, num):
 24         driver = self.driver
 25         driver.get(self.base_url + "/default_gdsf.aspx")
 26         driver.find_element_by_id("TextBox1").clear()
 27         driver.find_element_by_id("TextBox1").send_keys(num)
 28         driver.find_element_by_id("TextBox2").clear()
 29         driver.find_element_by_id("TextBox2").send_keys(num)
 30         driver.find_element_by_id("chkYD").click()
 31         driver.find_element_by_id("btnDL").click()
 32         WebDriverWait(driver, 5).until(EC.alert_is_present()).accept()
 33         self.open_and_click_menu(driver)
 34         retry = 0
 35         while retry <= 2:
 36             try:
 37                 driver.switch_to.frame(driver.find_element_by_id('iframeautoheight'))
 38                 WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.XPATH, "//input[@id='Button2']")))
 39                 break
 40             except:
 41                 print '重新点击按钮'
 42                 driver.switch_to.parent_frame()
 43                 self.open_and_click_menu(driver)
 44             retry += 1
 45         else:
 46             print '重试失败'
 47 
 48         source = driver.page_source
 49         driver.find_element_by_xpath("//input[@id='Button2']").click()
 50 
 51         def source_change(driver):
 52             if source == driver.page_source:
 53                 return False
 54             else:
 55                 return driver.page_source
 56 
 57         self.writeToFile(WebDriverWait(driver, 10).until(source_change))
 58         driver.quit()
 59 
 60     def open_and_click_menu(self, driver):
 61         menu1 = WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.XPATH, "//ul[@class='nav']/li[5]")))
 62         menu2 = driver.find_element_by_xpath("//ul[@class='nav']/li[5]/ul/li[3]")
 63         ActionChains(driver).move_to_element(menu1).move_to_element(menu2).click(menu2).perform()
 64 
 65     def is_element_present(self, how, what):
 66         try:
 67             self.driver.find_element(by=how, value=what)
 68         except NoSuchElementException as e:
 69             return False
 70         return True
 71 
 72     def is_alert_present(self):
 73         try:
 74             self.driver.switch_to_alert()
 75         except NoAlertPresentException as e:
 76             return False
 77         return True
 78 
 79     def close_alert_and_get_its_text(self):
 80         try:
 81             alert = self.driver.switch_to_alert()
 82             alert_text = alert.text
 83             if self.accept_next_alert:
 84                 alert.accept()
 85             else:
 86                 alert.dismiss()
 87             return alert_text
 88         finally:
 89             self.accept_next_alert = True
 90 
 91     def tearDown(self):
 92         self.driver.quit()
 93         self.assertEqual([], self.verificationErrors)
 94 
 95     @staticmethod
 96     def writeToFile(source):
 97         wb = xlwt.Workbook(encoding='utf-8', style_compression=0)
 98         soup = BeautifulSoup(source, "html.parser")
 99         span = soup.find('span', attrs={'id': 'Label5'})
100         sheet = wb.add_sheet('成绩单', cell_overwrite_ok=True)
101         table = soup.find(attrs={'id': 'Datagrid1'})
102         lines = table.find_all('tr')
103         for i in range(len(lines)):
104             tds = lines[i].find_all('td')
105             for j in range(len(tds)):
106                 sheet.write(i, j, tds[j].text)
107         try:
108             os.remove(span.text + '.xls')
109         except:
110             pass
111         wb.save(span.text + '.xls')
112 
113 
114 if __name__ == "__main__":
115     # unittest.main()
116     s = Script()
117 
118     for i in range(1, 50):
119         num = '2013034743101'
120         s.setUp()
121         try:
122             if i <= 9:
123                 s.test_jb(num[:12] + str(i))
124             else:
125                 s.test_jb(num[:11] + str(i))
126         except:
127             pass

这种方法的意义只是熟悉一下自动化测试框架,因为速度实在太慢了,也就不详细介绍了,这里粗略说一下,其实原理就是通过查到网页中对应的控件,进行点击或者悬浮于上面等等的操作,一步一步的到达最后的成绩单,要做的是控制整个流程,明确在什么时候应该停一下等控件出现,什么时候要去点击。

而且到目前为止,这个框架还是有一些Bug的,比如火狐浏览器的驱动无法实现在一个按钮上Hover的操作等等。

 

转载于:https://www.cnblogs.com/Fndroid/p/6531776.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371344.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring–添加SpringMVC –第1部分

欢迎来到本教程的第四部分。 在这一部分中&#xff0c;我们将使用Spring MVC编写控制器和视图&#xff0c;并考虑我们的REST模型。 我们必须做的第一件事&#xff0c;就是根据目前的情况制作一个Web应用程序。 我们将web / WEB-INF文件夹添加到我们的项目根目录。 在WEB-INF内创…

[Linux] 权限与指令间的关系

我们知道权限对于使用者帐号来说是非常重要的&#xff0c;因为他可以限制使用者能不能读取/创建/删除/修改文件或目录&#xff01; 在这一章我们介绍了很多文件系统的管理指令&#xff0c;第五章则介绍了很多文件权限的意义。在这个小节当中&#xff0c; 我们就将这两者结合起来…

access month函数用法_学会了这7个EXCEL日期函数技巧,老板再让你加班,你找我!...

日期函数&#xff0c;常用年月日&#xff0c;时分秒&#xff0c;星期&#xff0c;季度&#xff0c;求差值等&#xff0c;学会以下几个函数&#xff0c;老板再让你加班&#xff0c;你找我&#xff01;1、记录当前时间(不随系统时间变化)NOW()函数与数据有效性结合&#xff0c;记…

css样式表的选择器与分类

css 样式表的作用&#xff1a; 主要用于结构,样式与行为,CSS主要的作用就是美化网页的一个语言,它的特点: 1.结构与样式分离的方式,便于后期维护与改版; 2.样式定义精确到像素的级别; css样式表的结构&#xff1a;CSS 称为层叠样式表 用于给网页设置各种样式 css样式的语法由3部…

Spring 3.1缓存和@Cacheable

缓存在软件领域已经存在很长时间了。 它们是那些真正有用的东西之一&#xff0c;一旦您开始使用它们&#xff0c;您会想知道如果没有它们&#xff0c;您是如何相处的&#xff0c;所以似乎让Spring的家伙们只是在版本中向Spring核心添加缓存实现有点奇怪。 3.1。 我猜想以前没有…

pytorchyolov4训练_使用pytorch-yolov5 訓練自己的數據集-2020.6.15

make yolov5 pytorch train datasets训练所需环境 python3.5, pytorch1.3, torchvision 0.4.1 , tensorboard 1.14.0 , tensorflow-gpu1.14.0本例制作yolov5数据集 并进行数据训练从VOC数据集转为训练所需的coco数据集代码有待改进包含文件夹voc2coco/(Annotations/ JPEGImages…

meta 的作用 搜集

Meta标签中的format-detection属性及含义 format-detection翻译成中文的意思是“格式检测”&#xff0c;顾名思义&#xff0c;它是用来检测html里的一些格式的&#xff0c;那关于meta的format-detection属性主要是有以下几个设置&#xff1a;<meta name"format-detecti…

Web服务安全性和SOA路线图的人为维度

在大多数非平凡的SOA环境中&#xff0c;很难跟踪系统之间不断发展的集成&#xff0c;除非有明确的发布和查找适当信息的方法。 概述IT环境&#xff0c;定义当前或将要连接的内容&#xff0c;是维护环境的先决条件。 缺少这种情况通常会导致“面向意大利面条的环境”的感觉&…

pccad自定义图框_(PCCAD自定义标题栏详细方法.doc

PCCAD2011自定义标题栏详细方法下面以图3-1为例说明标题栏的自定义过程。图3-11&#xff0e;新建文件(用New 命令)。2&#xff0e;用绘图和文字中的相关命令设计出图3-1所示的图形。其中在使用中不变的内容&#xff0c;如厂名等均用“文字”命令标出&#xff0c;而需临时填充的…

ThinkPHP 3.2.x 集成极光推送指北

3.2版本已经过了维护生命周期&#xff0c;官方已经不再维护&#xff0c;请及时更新至5.0版本 —— ThinkPHP 官方仓库 以上&#xff0c;如果有条件&#xff0c;请关闭这个页面&#xff0c;然后升级至 ThinkPHP 5&#xff0c;如果由于各种各样的原因无法升级至 TP 5 &#xff0c…

Java:选择正确的集合

这是在应用程序中选择Set &#xff0c; List或Map的正确实现的快速指南。 最好的通用或“主要”实现可能是ArrayList&#xff0c;LinkedHashMap和LinkedHashSet。 它们的整体性能更好&#xff0c;除非您需要其他实现提供的特殊功能&#xff0c;否则应使用它们。 该特殊功能通常…

Java多线程——不变性与安全发布

1、不变性 某个对象在被创建后其状态就不能被修改&#xff0c;那么这个对象就称为不可变对象&#xff0c;不可变对象一定是线程安全的。不可变对象很简单。他们只有一种状态&#xff0c;并且该状态由构造函数来控制。 当满足以下条件时&#xff0c;对象才是不可变的&#xff1a…

中tr不能显示字符_垃圾文本识别中基本操作指南和错误总结,第三部分

创建模型需要用到机器学习的库&#xff0c;所以我们先下载sklearn库sklearn库下载完成后再输入库文件&#xff0c;就可以完美运行。然后就是划分测试集和训练集&#xff0c;需要注意的是&#xff0c;在从数据处理函数中导入数据时&#xff0c;足足运行了有将近30多秒&#xff0…

mvc后台post发送json数据

代码比较简单&#xff0c;不废话&#xff0c;直接上代码&#xff1a; /// <summary>/// 提交json数据&#xff0c;并接收/// </summary>/// <param name"apipath">要请求的服务器地址</param>/// <param name"jsonStr">要提…

(转载)20分钟读懂程序集

转自&#xff1a;http://www.cnblogs.com/damonlan/p/3221347.html 说到程序集&#xff0c;我刚开始对这个名词特别的郁闷&#xff01;~。然后 前些天花了些时间 好好读了一下&#xff0c;现在比较清晰了&#xff0c;把一些书上看到的 记下来&#xff0c;以飨读者。希望没浪费你…

大数据胸_喂母乳会导致胸下垂?!你被这个谣言骗了多少年?

很多人认为&#xff0c;给宝宝喂奶会导致胸下垂。有些爱美的妈妈&#xff0c;甚至在宝宝出生6个月后就着急断奶。那么&#xff0c;喂奶真的会导致胸下垂么&#xff1f;给大家讲两个真实的调查结果哈~2004年的一次针对496名新妈妈的调查结果显示&#xff0c;有75%的母乳喂养母亲…

Spring Shell项目发布

Spring Source昨天发布了Spring Shell 。 Spring Shell是一个交互式Shell&#xff0c;可以使用基于Spring的编程模型轻松地通过命令进行扩展。 它是通过除去OSGi依赖项从Spring Roo项目中提取的&#xff0c;并变成了一个独立项目。 这使得那些只希望使用交互式外壳功能的人更容…

自制ACL+DHCP实验(初版)

&#xff08;实验用gns模拟器&#xff09; ACL 实验拓扑&#xff1a; 实验要求&#xff1a; 1.1.1.1→3.3.3.3 不通 11.11.11.11→3.3.3.3 通 2.2.2.2→3.3.3.3 通 实验步骤&#xff1a; 步骤一&#xff1a;基本配置 R1&#xff1a; R1#conf t R1(config)#int lo0 R1(config-if…

pil 图像最大值_第97天:图像库 PIL(二)

上节我们讲了 Python 的图像处理库 PIL 的基本图像处理功能&#xff0c;打开了 PIL 的神秘面纱。这节我们接着讲 PIL 的 Image 模块的常用方法。Image 模块的方法convertImage.convert(modeNone, matrixNone, ditherNone, palette0, colors256)参数说明&#xff1a;mode&#x…

c#的委托用法delegate

转载于:https://www.cnblogs.com/douzujun/p/6555886.html