python爬去学校_利用Python如何爬取自己学校的官网?用这招就行!
查成績(jī),算分?jǐn)?shù),每年的綜合測(cè)評(píng)都是個(gè)固定的過(guò)程,作為軟件開發(fā)者,這些過(guò)程當(dāng)然可以交給代碼去做,通過(guò)腳本進(jìn)行網(wǎng)絡(luò)請(qǐng)求獲取數(shù)據(jù),然后直接進(jìn)行計(jì)算得到基礎(chǔ)分直接填表就好了,查成績(jī)?cè)偈謩?dòng)計(jì)算既容易出錯(cuò)也繁瑣,所以本篇的內(nèi)容就是開發(fā)一個(gè)爬蟲腳本取抓取成績(jī)表,至于綜合測(cè)評(píng)計(jì)算,這個(gè)沒(méi)什么意義這里就不說(shuō)了,分?jǐn)?shù)都有了就都?jí)蛄恕?/p>
我們的目的就是通過(guò)編寫腳本,模仿瀏覽器進(jìn)行請(qǐng)求獲取源碼,再進(jìn)行解析本地化(或者直接計(jì)算)
要抓取到數(shù)據(jù),其實(shí)方案不止一種,這里會(huì)介紹兩種不同的方案,達(dá)到同樣的目的:模仿瀏覽器進(jìn)行請(qǐng)求(速度快)
操作瀏覽器進(jìn)行請(qǐng)求(速度慢)
先說(shuō)第一種,這種方案是普遍的爬蟲技術(shù),因?yàn)榕廊〉膬?nèi)容不多,對(duì)速度要求也不夠,所以就是很簡(jiǎn)單的一個(gè)爬蟲過(guò)程:
分析請(qǐng)求
模仿請(qǐng)求
對(duì)于普通的校園網(wǎng),一般不做流量限制,所以就算請(qǐng)求頻繁,也基本不用擔(dān)心IP被封禁,所以編寫爬蟲代碼可以不用太過(guò)擔(dān)心。先說(shuō)我所在學(xué)校的校園網(wǎng),是杭州方正軟件公司開發(fā)的。
① 分析請(qǐng)求
分析請(qǐng)求很簡(jiǎn)單,就是使用瀏覽器進(jìn)行請(qǐng)求,然后分析每個(gè)請(qǐng)求所發(fā)送和接收的信息,這里最簡(jiǎn)單應(yīng)該是使用chrome的開發(fā)者模式(F12打開)
輸入用戶名和密碼,勾選已認(rèn)真閱讀,接著點(diǎn)擊登陸,這樣右邊的網(wǎng)絡(luò)窗口中會(huì)檢查到所有的網(wǎng)絡(luò)請(qǐng)求,我們只需要找到對(duì)應(yīng)登陸的一個(gè)(這里會(huì)帶有表單):
這個(gè)時(shí)候,我們可以通過(guò)一些測(cè)試工具,嘗試進(jìn)行請(qǐng)求對(duì)應(yīng)的這個(gè)地址,并且把表單提交上去試試登陸能否成功,如果成功的話,腳本也就可以模擬這個(gè)請(qǐng)求,這里用的是chrome商店的一個(gè)工具Postman,用法很簡(jiǎn)單:
登陸成功之后,我們?cè)龠M(jìn)行查詢成績(jī):
這里可以看到這次得到了兩個(gè)新的請(qǐng)求(上圖紅框的前兩個(gè))
仔細(xì)觀察會(huì)發(fā)現(xiàn),第一個(gè)請(qǐng)求頭中的Referer指向的是第二個(gè)請(qǐng)求的地址,所以可以知道,第二個(gè)請(qǐng)求是先于第一個(gè)請(qǐng)求發(fā)送的。其次,我們發(fā)現(xiàn)這個(gè)請(qǐng)求中也有表單。
再看第二個(gè)請(qǐng)求:
它的Referer指向第三個(gè)請(qǐng)求,而這個(gè)第三個(gè)請(qǐng)求實(shí)際上登陸成功之后,就已經(jīng)存在了,它就是請(qǐng)求到主界面的,而這個(gè)請(qǐng)求的類型是Get,所以也表明,第三個(gè)請(qǐng)求沒(méi)有傳遞任何信息給這個(gè)請(qǐng)求。
整理可以知道,流程是這樣的:
所以,我們先來(lái)模擬第二個(gè),這個(gè)請(qǐng)求是Get類型,所以直接請(qǐng)求即可,但是會(huì)發(fā)現(xiàn)請(qǐng)求會(huì)失敗,原因是服務(wù)器不能知道我們已經(jīng)進(jìn)行登陸了:
所以最先想到的辦法是帶上第一個(gè)請(qǐng)求得到的Cookie,但是也是不行,這個(gè)時(shí)候要用到上面說(shuō)的Referer標(biāo)識(shí),這個(gè)標(biāo)識(shí)會(huì)告訴服務(wù)器請(qǐng)求來(lái)源,因?yàn)榈顷懗晒?huì)在服務(wù)器進(jìn)行登記,這個(gè)標(biāo)記會(huì)讓服務(wù)器知道請(qǐng)求來(lái)源于登陸成功的賬號(hào):
此時(shí)請(qǐng)求返回正常,我們?cè)谠创a中可以發(fā)現(xiàn)有兩個(gè)隱藏的標(biāo)簽:
這兩個(gè)標(biāo)簽傳遞的,其實(shí)是第三個(gè)請(qǐng)求的參數(shù),這個(gè)時(shí)候,模擬第三個(gè)請(qǐng)求,并且添加對(duì)應(yīng)的Referer(第二個(gè)請(qǐng)求的URL),會(huì)發(fā)現(xiàn)請(qǐng)求也成功了:
這個(gè)請(qǐng)求中的url中的一個(gè)參數(shù)xm被我更改為1了,原本使用的是一種unicode加密編碼,把用戶名編碼過(guò)去了,但是實(shí)際上這個(gè)參數(shù)并沒(méi)有實(shí)際意義,%u的格式會(huì)破壞Python程序,所以這里直接改成1了。
② 模仿請(qǐng)求
請(qǐng)求分析完畢,就可以開始寫代碼了:
用到的包:
1 import requests, xlwt, os
2 from bs4 import BeautifulSoup
登錄:
1 def login(s, number, password):
2 print '正在登錄賬號(hào):'+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
登錄操作沒(méi)有給后面的請(qǐng)求傳遞任何參數(shù),這里的Cookies不是必須的,但是登錄是必須的,這樣告訴服務(wù)器我們后面的請(qǐng)求才是合法的。
點(diǎn)擊查詢成績(jī)按鈕:
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
第五行隊(duì)請(qǐng)求設(shè)置Referer,接著通過(guò)BeautifulSoup解析源碼得到兩個(gè)隱藏的標(biāo)簽里面value值,第三個(gè)請(qǐng)求要用到。
查詢所有成績(jī)請(qǐng)求:
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 '登錄成功,正在拉取成績(jī)'
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
得到成績(jī)單源碼之后,就可以進(jìn)行解析了,這里解析存放到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('成績(jī)單', 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')
最后遍歷學(xué)號(hào)進(jìn)行爬取,這里只爬取默認(rèn)賬號(hào)密碼的成績(jī):
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()
第二種方案,是通過(guò)模擬瀏覽器來(lái)進(jìn)行登錄,點(diǎn)擊按鈕等操作獲取成績(jī),這里用到的是自動(dòng)化測(cè)試框架Selenium。
這種方案的優(yōu)點(diǎn)是我們不需要像第一種那樣要去分析請(qǐng)求,只需要告訴瀏覽器要怎么做就行了,但是缺點(diǎn)是速度慢。
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 '重新點(diǎn)擊按鈕'
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('成績(jī)單', 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
這種方法的意義只是熟悉一下自動(dòng)化測(cè)試框架,因?yàn)樗俣葘?shí)在太慢了,也就不詳細(xì)介紹了,這里粗略說(shuō)一下,其實(shí)原理就是通過(guò)查到網(wǎng)頁(yè)中對(duì)應(yīng)的控件,進(jìn)行點(diǎn)擊或者懸浮于上面等等的操作,一步一步的到達(dá)最后的成績(jī)單,要做的是控制整個(gè)流程,明確在什么時(shí)候應(yīng)該停一下等控件出現(xiàn),什么時(shí)候要去點(diǎn)擊。
而且到目前為止,這個(gè)框架還是有一些Bug的,比如火狐瀏覽器的驅(qū)動(dòng)無(wú)法實(shí)現(xiàn)在一個(gè)按鈕上Hover的操作等等。
總結(jié)
以上是生活随笔為你收集整理的python爬去学校_利用Python如何爬取自己学校的官网?用这招就行!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 淘宝购物全流程
- 下一篇: Wireshark 使用教程