最近開始用簡書記錄學(xué)習(xí)過程的點(diǎn)滴。等以后自己學(xué)會(huì)建博客再移到博客上去吧~
工具:
python3.6.3
requests
BeautifulSoup
xlwt
爬取學(xué)校教務(wù)處
學(xué)了python基礎(chǔ)語法码党,requests和beautsoup赫舒,沒有練手過,遂到教務(wù)處一展拳腳闽瓢。我學(xué)校的教務(wù)處登錄比較簡單接癌,沒有驗(yàn)證碼,然而我還是不會(huì)用post扣讼。缺猛。。椭符。荔燎。
1:構(gòu)造請(qǐng)求
這里需要偽造一下瀏覽器:
這里盡顯我爬蟲渣本色,直接在谷歌瀏覽器里開發(fā)者工具中復(fù)制的header
header = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"}
此外销钝,登錄需要密碼賬號(hào)有咨,我試圖用requests的post方法,構(gòu)建賬號(hào)密碼信息蒸健,然而一知半解座享,在此記錄過程,希望有高手解答:
a.打開登錄頁面-F12-Network抓包似忧,如圖:
b. 抓包
抓包還看了不少博客渣叛,學(xué)的三腳貓的功夫:先故意輸錯(cuò)密碼試試看
可以看到network中抓取到數(shù)據(jù)流,點(diǎn)開Form Data, userName和pwd應(yīng)該就是賬號(hào)和密碼盯捌,但是pwd后面這串是什么鬼按狙谩?饺著?箫攀?加密?幼衰?靴跛?還有這個(gè)sign后面那串?dāng)?shù)字,我也看不懂塑顺,所以post方法暫時(shí)行不通汤求,可以留著以后慢慢研究俏险,或者有大神解釋解釋那是極好的严拒。
窮則思變扬绪,變則通達(dá),post不行裤唠,我就不學(xué)了嗎挤牛?那肯定不會(huì)。在網(wǎng)上看到了另外一招种蘸,對(duì)新手及其友好的墓赴,利用cookies進(jìn)行登錄。
a. 輸入賬號(hào)密碼航瞭,登錄教務(wù)處頁面诫硕,還是打開網(wǎng)絡(luò)監(jiān)聽F12-Network,點(diǎn)擊第一個(gè)選項(xiàng)刊侯,它是此頁中第一個(gè)數(shù)據(jù)流(或者其他叫法章办,我極其不專業(yè)。滨彻。藕届。),在右側(cè)找到cookies亭饵,如下圖
關(guān)于cookies的原理我也不懂休偶,看文章說是用戶與服務(wù)器之間的一種憑證,應(yīng)該就是短時(shí)間生成的一種憑證辜羊,只要這個(gè)憑證有效踏兜,那么用戶就可以不用登陸就可以訪問服務(wù)器上面的數(shù)據(jù)“送海看它這么亂庇麦,應(yīng)該是用方法加密了,暫且就這么理解吧喜德。
b. 復(fù)制這段cookies山橄,我們就可以制作cookies了。使用如下代碼:
cookies = {}
raw_cookies = "ASP.NET_SessionId=2gq0mryer************; UserTokeID=f716cffe-************-a84d-**********c"
for line in raw_cookies.split(';'): #用';'對(duì)字符串分割
key, value = line.split('=', 1)
cookies[key] = value
這個(gè)方法我是網(wǎng)上學(xué)到的舍悯,不過它的作用其實(shí)就是將原生的cookies構(gòu)造成以鍵值對(duì)形式的字典航棱,所以你也可以直接這樣:
cookies = {'ASP.NET_SessionId': '2gq0mryer************', 'UserTokeID': 'f716cffe-************-a84d-**********c'}
這樣手動(dòng)將復(fù)制下來的raw_cookies構(gòu)造成cookies。
接下來是獲取URL萌衬,我們打開成績頁面饮醇,同樣也是用F12去捕捉成績頁面的URL:
或者其實(shí)可以直接從網(wǎng)址那里復(fù)制?秕豫?朴艰?不是很懂观蓄,因?yàn)橛芯W(wǎng)友說其實(shí)獲取真實(shí)URL會(huì)有點(diǎn)麻煩。祠墅。侮穿。這里也要回頭復(fù)習(xí)被碗,什么是真實(shí)URL长赞?怎么獲认埂空民?嗯壶谒,記一下黔姜。
到這里我們已經(jīng)把所有請(qǐng)求頁面要包含的信息都準(zhǔn)備好甘有,可以開始寫請(qǐng)求代碼了律适。我用的是requests庫腔长,感覺要比urllib2方便袭祟。
import requests
from bs4 import BeautifulSoup
header = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36"}
cookies = {}
raw_cookies = "ASP.NET_SessionId=2gq0mryer************; UserTokeID=f716cffe-************-a84d-**********c"
for line in raw_cookies.split(';'): #用';'對(duì)字符串分割
key, value = line.split('=', 1)
cookies[key] = value
url = "http://202.115.133.***:805/********/Score/ScoreList.aspx"
r = requests.get(url, headers = header, cookies = cookies)
這樣我們就完成了請(qǐng)求,接下來解析頁面捞附。
2:解析頁面
打開成績頁面巾乳,F(xiàn)12下看Element選項(xiàng),或者網(wǎng)頁源代碼故俐,我們可以看出成績頁面的結(jié)構(gòu):如下圖:
右鍵審查元素想鹰,定位到元素標(biāo)簽。哇药版,這么多<li class="item">...</li>標(biāo)簽辑舷,應(yīng)該就是每門課的成績信息了。我們點(diǎn)開具體看一下:
果然槽片,沒毛病老鐵何缓。
接下來就好辦了,思路是:用靚湯找到所有的<li class="item">...</li>標(biāo)簽还栓,然后循對(duì)每一個(gè)<li class="item">...</li>里面的信息提取碌廓。嗯,上代碼:
soup = BeautifulSoup(r.text, "html.parser")
items = soup.findAll('li', attrs = {'class': 'item'}
for item in items:
print(item.text)
我們先來試試看輸入結(jié)果:
你妹J:小9绕拧!什么鬼辽聊?<涂妗!跟匆!這一大堆的空格我可不能輸進(jìn)Excel耙彀馈!B瓯邸烤蜕!
試試用split()切開:
soup = BeautifulSoup(r.text, "html.parser")
items = soup.findAll('li', attrs = {'class': 'item'}
for item in items:
print(item.text.split())
out:
嗯~~~很好封孙,列表形式的數(shù)據(jù)就好操作多了。為了能更加愉快地玩耍讽营,我們可以把這些列表全放到一個(gè)新的列表里:
data_list = []
for item in items:
data_list.append(item.text.split())
看看輸出:
print(data_list)
很好虎忌,接下來就只要把數(shù)據(jù)寫進(jìn)Excel里面就成啦
3:保存數(shù)據(jù)
寫入Excel操作我用到了xlwt,安裝這個(gè)庫也是有點(diǎn)小坑,具體解決方案網(wǎng)上有很多斑匪,不再贅述呐籽。上代碼:
book = xlwt.Workbook()
sheet1 = book.add_sheet('sheet1', cell_overwrite_ok = True)
#列表的每一行寫入一門課成績锋勺,最開始的行為0行蚀瘸,寫入表格頭:
heads = ['學(xué)期', '課程編碼', '課程名稱', '教師', '學(xué)分', '成績', '成績類型', '績點(diǎn)', '入庫人', '入庫時(shí)間']
#這里write方法接受3個(gè)參數(shù),分別為行庶橱、列贮勃、值。表格頭部信息在第0行
i = 0
for head in heades:
#依次將heades列表的每一個(gè)元素寫入表格中
sheet1.write(0, i, head)
i += 1
#從第1行開始寫入成績信息
m = 1
#對(duì)大列表里每一個(gè)小列表進(jìn)行遍歷苏章,小列表保存有一門課的成績信息寂嘉,這樣就能把所有課程成績信息循環(huán)到。
for list in data_list:
n = 0
#對(duì)小列表里每個(gè)元素進(jìn)行遍歷
for info in list:
#將每個(gè)元素寫入表格
sheet1.write(m, n, info)
n += 1
m += 1
book.save('grade.xls')
這樣全部代碼就完成啦枫绅!下面是完整代碼泉孩,本水筆代碼代碼像屎一樣,沒有一丁點(diǎn)封裝并淋。寓搬。。县耽。句喷。后面有時(shí)間再慢慢優(yōu)化
import requests
from bs4 import BeautifulSoup
import xlwt
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'}
raw_cookies = "ASP.NET_SessionId=2gq0mryer0o34wrmurek2dm3; UserTokeID=37efd17b-5a15-409f-93c5-8f9566d2b21d"
cookie = {}
for line in raw_cookies.split(';'):
key, value = line.split("=", 1)
cookie[key] = value
url = "http://202.115.133.173:805/SearchInfo/Score/ScoreList.aspx"
r = requests.get(url, headers=header, cookies = cookie)
r.encoding = 'utf8'
data_list = []
soup = BeautifulSoup(r.text, "html.parser")
items = soup.find_all('li', attrs={'class': 'item'})
for item in items:
data_list.append(items.text.split())
book = xlwt.Workbook()
sheet1 = book.add_sheet('sheet1', cell_overwrite_ok = True)
heads = ['學(xué)期', '課程編碼', '課程名稱', '教師', '學(xué)分', '成績', '成績類型', '績點(diǎn)', '入庫人', '入庫時(shí)間']
i = 0
for head in heads:
sheet1.write(0, i, head)
i += 1
m = 1
for list in data_list:
n = 0
for data in list:
sheet1.write(m, n, data)
n += 1
m += 1
book.save("chenzhida.xls")
print("錄入成功")
成果如下:
遺留問題:
有些課程的信息不全,比如沒有教師的名字等等兔毙,這樣錄進(jìn)Excel的時(shí)候信息會(huì)對(duì)不上表頭唾琼。應(yīng)該可以用某種替換方法,給空格占個(gè)位置澎剥,這樣錄進(jìn)去的格式就能對(duì)的上表頭锡溯。后面再處理吧~
以下是本次爬取過程中遇到的一些坑,還有困惑哑姚,寫下來提醒自己以后要解決:
- 使用cookies模擬登錄自然會(huì)省事點(diǎn)祭饭,但是由于這個(gè)憑證只能短期有效,當(dāng)需要爬取數(shù)據(jù)很多的時(shí)候蜻懦,可能需要爬取十幾天或者更久甜癞,這時(shí)cookies就不好用了,容易掛掉宛乃。這里需要學(xué)習(xí)post悠咱,學(xué)習(xí)分析賬號(hào)密碼提交蒸辆,要是有加密怎么破解?遇到驗(yàn)證碼怎么辦析既?FROM DATA中每一項(xiàng)數(shù)據(jù)代表什么躬贡?遇到動(dòng)態(tài)頁面怎么破?(有大神教嗎眼坏?)
- 如何獲取真實(shí)URL拂玻,真實(shí)URL是什么?
- 表格數(shù)據(jù)有缺失怎么破
- 保存數(shù)據(jù)到本地宰译。要學(xué)習(xí)常用的mongoDB檐蚜。
- 學(xué)習(xí)其他的解析工具,Xpath
- 學(xué)習(xí)scrapy沿侈。
另外闯第,此次保存數(shù)據(jù)還花了老子大部分時(shí)間,記錄一下犯蠢的過程:
一開始我的思路是缀拭,把所有的課程成績信息提取出來咳短,放到一個(gè)總的表格里,然而當(dāng)我實(shí)際做的時(shí)候蛛淋,我發(fā)現(xiàn):
items = soup.findAll(r.text, "html.parser)
data_list = []
for item in items:
data_list.append(item.text)
print(data_list)
返回的是:
后來發(fā)現(xiàn)是輸出的地方錯(cuò)了:應(yīng)該是
items = soup.findAll(r.text, "html.parser)
data_list = []
for item in items:
data_list.append(item.text)
print(data_list)
for循環(huán)沒掌握好咙好。。褐荷。勾效。
但是這樣輸出的是:
還是不行呀,這些“ \xa0\r\n诚卸,\n\r\n ”是什么鬼葵第??合溺?(其實(shí)可以用在末尾追加.spilt()方法卒密,可以實(shí)現(xiàn)去掉空格和換行符)
然后我又想到了可不可以用正則表達(dá)式提取內(nèi)容?把所有內(nèi)容提取出來放到一個(gè)表格中,和原來不同的是棠赛,這里大表格里面是沒有小表格的哮奇。我們可以取10個(gè)元素取10個(gè)元素這樣去做小表格。
又開始折騰了:
infos = re.findall(r'<div.*?">(.*?)</div>', str(items))
print(infos)
結(jié)果:
emmmmmm......exo me????
后來仔細(xì)一看網(wǎng)頁源代碼:
哦睛约,原來有空格鼎俘,而 . 是除了空格以外的任意字符,一般都要加re.S
infos = re.findall(r'<div.*?">(.*?)</div>', str(items), re.S)
print(infos)
結(jié)果:
what the 你妹辩涝!這不和原來一樣了嗎贸伐?不過確實(shí)是一個(gè)大表格,但是
\xa0\r\n \r\n 腫么破怔揩?機(jī)智的我想到用replace()試試看:
infos = re.findall(r'<div.*?">(.*?)</div>', str(items), re.S)
for info in infos:
print(str(info).replace('\xa0\r\n', '').replace('\r\n', '').strip())
我好像發(fā)現(xiàn)了什么W叫稀8俊!原來的遺留問題貌似可以解決的!!!
list = []
infos = re.findall(r'<div.*?">(.*?)</div>', str(items), re.S)
for info in infos:
list.append(str(info).replace('\xa0\r\n', '').replace('\r\n', '').strip())
#把list按照每次取10個(gè)的數(shù)量伏伐,做成小表格宠进,然后把這些小表格添加到new_list里面方便操作,這樣的好處是藐翎,會(huì)把缺失信息的地方給站位材蹬,后面錄進(jìn)Excel的時(shí)候就不會(huì)對(duì)不上表頭。
new_list = [list[i: i+10] for i in range(0, len(list), 10)]
print(new_list)
結(jié)果:
哈哈哈那我直接放完整代碼了
# -*- coding: utf-8 -*-
import requests
import re
from bs4 import BeautifulSoup
import xlwt
header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'}
raw_cookies = "ASP.NET_SessionId=2gq0mryer0o34wrmurek2dm3; UserTokeID=6412ebc3-5fde-44f5-b5e0-d0de28125591"
url = "http://202.115.133.173:805/SearchInfo/Score/ScoreList.aspx"
cookies = {}
for line in raw_cookies.split(";"):
key, value = line.split("=", 1)
cookies[key] = value
r = requests.get(url, headers = header, cookies = cookies)
print(r.status_code)
r.encoding = 'utf8'
data_list = []
soup = BeautifulSoup(r.text, "html.parser")
items = soup.find_all('li', attrs = {'class': 'item'})
list = []
infos = re.findall(r'<div.*?">(.*?)</div>', str(items), re.S)
for info in infos:
list.append(str(info).replace('\xa0\r\n', '').replace('\r\n', '').strip())
new_list = [list[i: i+10] for i in range(0, len(list), 10)]
print(new_list)
book = xlwt.Workbook()
sheet1 = book.add_sheet('shee1', cell_overwrite_ok=True)
heads = ['學(xué)期', '課程編碼', '課程名稱', '教師', '學(xué)分', '成績', '成績類型', '績點(diǎn)', '入庫人', '入庫時(shí)間']
s = 0
for head in heads:
sheet1.write(0, s, head)
s += 1
m = 1
for lists in new_list:
n = 0
for data in lists:
sheet1.write(m, n, data)
n += 1
m += 1
book.save("stander.xls")
print("錄入成功")
成果:
這樣有些沒有信息的地方也可以用空白來站位吝镣。
finish堤器!