最近开始用简书记录学习过程的点滴。等以后自己学会建博客再移到博客上去吧~
工具:
python3.6.3
requests
BeautifulSoup
xlwt
爬取学校教务处
学了python基础语法,requests和beautsoup,没有练手过,遂到教务处一展拳脚。我学校的教务处登录比较简单,没有验证码,然而我还是不会用post。。。。。
1:构造请求
这里需要伪造一下浏览器:
这里尽显我爬虫渣本色,直接在谷歌浏览器里开发者工具中复制的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"}
此外,登录需要密码账号,我试图用requests的post方法,构建账号密码信息,然而一知半解,在此记录过程,希望有高手解答:
a.打开登录页面-F12-Network抓包,如图:
b. 抓包
抓包还看了不少博客,学的三脚猫的功夫:先故意输错密码试试看
可以看到network中抓取到数据流,点开Form Data, userName和pwd应该就是账号和密码,但是pwd后面这串是什么鬼啊???加密???还有这个sign后面那串数字,我也看不懂,所以post方法暂时行不通,可以留着以后慢慢研究,或者有大神解释解释那是极好的。
穷则思变,变则通达,post不行,我就不学了吗?那肯定不会。在网上看到了另外一招,对新手及其友好的,利用cookies进行登录。
a. 输入账号密码,登录教务处页面,还是打开网络监听F12-Network,点击第一个选项,它是此页中第一个数据流(或者其他叫法,我极其不专业。。。),在右侧找到cookies,如下图
关于cookies的原理我也不懂,看文章说是用户与服务器之间的一种凭证,应该就是短时间生成的一种凭证,只要这个凭证有效,那么用户就可以不用登陆就可以访问服务器上面的数据。看它这么乱,应该是用方法加密了,暂且就这么理解吧。
b. 复制这段cookies,我们就可以制作cookies了。使用如下代码:
cookies = {}
raw_cookies = "ASP.NET_SessionId=2gq0mryer************; UserTokeID=f716cffe-************-a84d-**********c"
for line in raw_cookies.split(';'): #用';'对字符串分割
key, value = line.split('=', 1)
cookies[key] = value
这个方法我是网上学到的,不过它的作用其实就是将原生的cookies构造成以键值对形式的字典,所以你也可以直接这样:
cookies = {'ASP.NET_SessionId': '2gq0mryer************', 'UserTokeID': 'f716cffe-************-a84d-**********c'}
这样手动将复制下来的raw_cookies构造成cookies。
接下来是获取URL,我们打开成绩页面,同样也是用F12去捕捉成绩页面的URL:
或者其实可以直接从网址那里复制???不是很懂,因为有网友说其实获取真实URL会有点麻烦。。。这里也要回头复习,什么是真实URL?怎么获取?嗯,记一下。
到这里我们已经把所有请求页面要包含的信息都准备好,可以开始写请求代码了。我用的是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(';'): #用';'对字符串分割
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)
这样我们就完成了请求,接下来解析页面。
2:解析页面
打开成绩页面,F12下看Element选项,或者网页源代码,我们可以看出成绩页面的结构:如下图:
右键审查元素,定位到元素标签。哇,这么多<li class="item">...</li>标签,应该就是每门课的成绩信息了。我们点开具体看一下:
果然,没毛病老铁。
接下来就好办了,思路是:用靓汤找到所有的<li class="item">...</li>标签,然后循对每一个<li class="item">...</li>里面的信息提取。嗯,上代码:
soup = BeautifulSoup(r.text, "html.parser")
items = soup.findAll('li', attrs = {'class': 'item'}
for item in items:
print(item.text)
我们先来试试看输入结果:
你妹!!!什么鬼?!!!这一大堆的空格我可不能输进Excel啊!!!
试试用split()切开:
soup = BeautifulSoup(r.text, "html.parser")
items = soup.findAll('li', attrs = {'class': 'item'}
for item in items:
print(item.text.split())
out:
嗯~~~很好,列表形式的数据就好操作多了。为了能更加愉快地玩耍,我们可以把这些列表全放到一个新的列表里:
data_list = []
for item in items:
data_list.append(item.text.split())
看看输出:
print(data_list)
很好,接下来就只要把数据写进Excel里面就成啦
3:保存数据
写入Excel操作我用到了xlwt,安装这个库也是有点小坑,具体解决方案网上有很多,不再赘述。上代码:
book = xlwt.Workbook()
sheet1 = book.add_sheet('sheet1', cell_overwrite_ok = True)
#列表的每一行写入一门课成绩,最开始的行为0行,写入表格头:
heads = ['学期', '课程编码', '课程名称', '教师', '学分', '成绩', '成绩类型', '绩点', '入库人', '入库时间']
#这里write方法接受3个参数,分别为行、列、值。表格头部信息在第0行
i = 0
for head in heades:
#依次将heades列表的每一个元素写入表格中
sheet1.write(0, i, head)
i += 1
#从第1行开始写入成绩信息
m = 1
#对大列表里每一个小列表进行遍历,小列表保存有一门课的成绩信息,这样就能把所有课程成绩信息循环到。
for list in data_list:
n = 0
#对小列表里每个元素进行遍历
for info in list:
#将每个元素写入表格
sheet1.write(m, n, info)
n += 1
m += 1
book.save('grade.xls')
这样全部代码就完成啦!下面是完整代码,本水笔代码代码像屎一样,没有一丁点封装。。。。。后面有时间再慢慢优化
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 = ['学期', '课程编码', '课程名称', '教师', '学分', '成绩', '成绩类型', '绩点', '入库人', '入库时间']
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("录入成功")
成果如下:
遗留问题:
有些课程的信息不全,比如没有教师的名字等等,这样录进Excel的时候信息会对不上表头。应该可以用某种替换方法,给空格占个位置,这样录进去的格式就能对的上表头。后面再处理吧~
以下是本次爬取过程中遇到的一些坑,还有困惑,写下来提醒自己以后要解决:
- 使用cookies模拟登录自然会省事点,但是由于这个凭证只能短期有效,当需要爬取数据很多的时候,可能需要爬取十几天或者更久,这时cookies就不好用了,容易挂掉。这里需要学习post,学习分析账号密码提交,要是有加密怎么破解?遇到验证码怎么办?FROM DATA中每一项数据代表什么?遇到动态页面怎么破?(有大神教吗?)
- 如何获取真实URL,真实URL是什么?
- 表格数据有缺失怎么破
- 保存数据到本地。要学习常用的mongoDB。
- 学习其他的解析工具,Xpath
- 学习scrapy。
另外,此次保存数据还花了老子大部分时间,记录一下犯蠢的过程:
一开始我的思路是,把所有的课程成绩信息提取出来,放到一个总的表格里,然而当我实际做的时候,我发现:
items = soup.findAll(r.text, "html.parser)
data_list = []
for item in items:
data_list.append(item.text)
print(data_list)
返回的是:
后来发现是输出的地方错了:应该是
items = soup.findAll(r.text, "html.parser)
data_list = []
for item in items:
data_list.append(item.text)
print(data_list)
for循环没掌握好。。。。
但是这样输出的是:
还是不行呀,这些“ \xa0\r\n,\n\r\n ”是什么鬼???(其实可以用在末尾追加.spilt()方法,可以实现去掉空格和换行符)
然后我又想到了可不可以用正则表达式提取内容?把所有内容提取出来放到一个表格中,和原来不同的是,这里大表格里面是没有小表格的。我们可以取10个元素取10个元素这样去做小表格。
又开始折腾了:
infos = re.findall(r'<div.*?">(.*?)</div>', str(items))
print(infos)
结果:
emmmmmm......exo me????
后来仔细一看网页源代码:
哦,原来有空格,而 . 是除了空格以外的任意字符,一般都要加re.S
infos = re.findall(r'<div.*?">(.*?)</div>', str(items), re.S)
print(infos)
结果:
what the 你妹!这不和原来一样了吗?不过确实是一个大表格,但是
\xa0\r\n \r\n 肿么破?机智的我想到用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())
我好像发现了什么!!!原来的遗留问题貌似可以解决的!!!
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个的数量,做成小表格,然后把这些小表格添加到new_list里面方便操作,这样的好处是,会把缺失信息的地方给站位,后面录进Excel的时候就不会对不上表头。
new_list = [list[i: i+10] for i in range(0, len(list), 10)]
print(new_list)
结果:
哈哈哈那我直接放完整代码了
# -*- 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 = ['学期', '课程编码', '课程名称', '教师', '学分', '成绩', '成绩类型', '绩点', '入库人', '入库时间']
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!