文章目录
受到疫情影响,很多高校还在放假中,但毕业生工作却并没有停下来。这里以国内某C9高校为例,通过公示信息网页进行学位申请信息的爬取。
一、确定业务需求
1、网页爬取目标:
获取公示名单中所有人的关键公示信息
2、目标网页结构:
首先来看一下目标网页:
结构很简单,公示网页如下
点击姓名进入超链接网页,可以看到博士、学硕、专硕三类学位的公示信息有明显差异
3、进一步明确目标信息:
选择共有且价值的信息:学号、姓名、所在院系、申请专业/工程领域、论文题目
二、关键功能点拆解
1、计算运行时间:datetime库
由于爬取网页有300多个,这里我用datetime库计算运行时间,方便优化代码后性能比较
import datetime
starttime = datetime.datetime.now()
......
endtime = datetime.datetime.now()
cost=endtime - starttime
print('The time cost: ',cost)
2、读写文档
f_res=open("C:/Users/....../get_ID.csv","w",encoding='gb18030',errors='ignore') #解决文本编码问题
title="学号"+","+"姓名"+","+"所在院系"+","+"申请专业/工程邻域"+","+"论文题目"
f_res.writelines(title+"\n") #打印标题行,增加文档可读性
......
f_res.close()
“UnicodeEncodeError”编码错误的解决方案
由于网页文本编码问题,我原始代码读取文件时出现“UnicodeEncodeError”编码错误,如下图:
这里的最终解决方案是:f_res=open(“C:/Users/…/get_ID.csv”,“w”,encoding=‘gb18030’,errors=‘ignore’)
使用Python读文件,我们一般使用utf-8编码,有时会出现gbk,utf-8的编码格式读取都不对,运行出错。
解决方案:
1.选择编码范围更广的gb18030
file = open(‘abc.txt’,encoding=‘gb18030’)
2.仍不能解决,可以使用‘ignore’属性进行忽略
file = open(‘abc.txt’,encoding=‘gb18030’,errors=‘ignore’)
此处参考博客:
(1)Python文本编码问题
(2)Python文本文件读写操作时的字符编码问题
3、增加互动效果
有时候我们代码运行过程中我们可能不确定代码运行状态,这里我引入print打印运行状态。这对等待程序运行中的我们非常有趣
print("数据获取开始......")
......
print("找到学号"+str(http)+"的信息如下:")
......
print("继续爬取下一条信息......")
......
print("网页信息采集完成")
效果如下:
4、网页数据获取:requests库
(1)公示名单网页(单一网页):
url = "http://......gongShiList.asp"
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36'}
res = requests.get(url, headers=headers)
(2)公示人具体信息网页(批量网页):
首先分析网页网址特征,发现变化项为公示人学号:
那么如何获取学号信息?
这里想到直接在公示名单网页获取每一个超链接的网址,这里发现网址为不完全显示,那么我这里只取最后的学号信息。
具体操作方法我在上一篇博客python抓取页面文本及图片超链接中讲到了。在下面我也会在简单讲一下
利用我们提取的学号信息生成目标url,如下方案一
另外,因为学号是固定的,我们可以利用for循环对学号进行逐个搜索,如下方案二。但是这种方法比较笨,而且存在大量无效项,花费时间也很长,在这里不适合使用。
方案一:网页中变动项用列表存储,for循环读取每一条,如:
for item in soup_item:
url = "http://....../gongShiShowStudentInfo.asp?studentId="+str(item) #这里一定要以str类型表示
headers={......}
res = requests.get(url, headers=headers)
方案二:对于按一定规律进行变化,for循环.如:
list_co=["BA","BO","BZ","SA","SB","SM"] #学号代码
for l_c in range(len(list_co)): #循环数为列表长度
for i in range(16,19):
for j in range(225001,225500):#注意Python不能处理以0开头的数字字符串,也就无法识别range(001,500),所以前面数字也需要加上
url = "http://....../gongShiShowStudentInfo.asp?studentId="+list_co[LC]+str(i)+str(j)
headers = {......}
res = requests.get(url, headers=headers)
4、网页数据解析:BeautifulSoup库
(1)公示网页超链接提取:
这里实际上我们提取的是超链接中的学号信息,在上面已经讲到了原因
url_id = "......"
headers = {......}
res_id = requests.get(url_id, headers=headers)
soup_id = BeautifulSoup(res_id.text,'lxml') #将爬取的网页信息(res_id.text)用BS库进行解析
soup_name_herf=soup_id.find_all("a") #精准的解析标签,在数据中查找超链接标签"a",并存入新变量
for herf_http in soup_name_herf: #从新变量中逐个取出其中的标签"a"
http = herf_http.get('href')[-10:] #获取超链接标签中的herf网址信息,并提取其中倒数第十个及之后的数据
print(http)
爬取结果:
推荐参考学习资料
(1)Python爬虫之解析网页:常用的类库为lxml, BeautifulSoup, re(正则)
(2)python爬虫基础–获取并解析网页
(2)网页中公示人信息提取:
首先爬取每一个网页的数据,并用bs库解析
url = "http://....../gongShiShowStudentInfo.asp?studentId="+str(http)
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text,'lxml')
然后分析网页信息:
分析发现:除了博士的论文信息在中,其他目标信息都存在标签中。
这里使用**soup.find_all(“tag”)[n].get_text()**获取tag标签的指定位置n中的内容
可以用如下方法查找指定位置
import requests
from bs4 import BeautifulSoup
import datetime
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36'}
url = "http://....../gongShiShowStudentInfo.asp?studentId=BA07002016"
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text,'lxml')
s=soup.find_all('td', colspan="3")
print(s)
for i in range(len(s)):
s_no=soup.find_all('td', colspan="3")[i].get_text()
print("位置:",i,s_no)
由于博士和硕士的论文题目所在标签不一致,所以这里需要分开处理论文信息。
具体代码方法如下:
try:
print("找到学号"+str(http)+"的信息如下:")
s_no=soup.find_all('td', colspan="3")[0].get_text() #找到学号信息
s_name=soup.find_all('td', colspan="3")[1].get_text() #找到姓名信息
s_coll=soup.find_all('td', colspan="3")[2].get_text() #找到所在院系信息
s_en=soup.find_all('td', colspan="3")[3].get_text() #找到申请专业/工程邻域信息
if soup.find_all('td', colspan="3")[4].get_text()=="主要创新点": #识别出博士
s_pap=soup.find_all('td', colspan="2")[0].get_text() #找到博士的论文题目信息
print("学号:"+s_no+","+"姓名:"+s_name+","+"所在院系:"+s_coll+","+"申请专业:"+s_en+","+"论文题目:"+s_pap) #打印信息,同时增加代码运行中的交互感
txt=s_no+","+s_name+","+s_coll+","+s_en+","+s_pap #将结果以字符串连接
f_res.writelines(txt+"\n") #将结果写入文档,并换行
else: #找到博士外的网页格式
s_pap=soup.find_all('td', colspan="3")[4].get_text() #找到论文题目信息
print("学号:"+s_no+","+"姓名:"+s_name+","+"所在院系:"+s_coll+","+"申请专业/工程邻域:"+s_en+","+"论文题目:"+s_pap)
txt=s_no+","+s_name+","+s_coll+","+s_en+","+s_pap
f_res.writelines(txt+"\n")
print("继续爬取下一条信息......")
except:
continue
这里try … except…是为了防止报错。
到这里数据爬取就完成了。
这部分也可用正则表达式进行分析
三、结果展示
1、Jupyter界面结果
一共用时38秒
2、【爬取效果动态展示】:
结果展示,一共获取342条数据:
验证名单完整性:
直接爬取公示名单页面所有的人名,利用excel的突出显示重复项与前面的数据进行对比,发现全部都爬取到了
四、完整代码
import requests
from bs4 import BeautifulSoup
import datetime
def get_res():
url_id = "http://....../gongShiList.asp"
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36'}
res_id = requests.get(url_id, headers=headers)
soup_id = BeautifulSoup(res_id.text,'lxml')
soup_name_herf=soup_id.find_all("a")
for herf_http in soup_name_herf:
http = herf_http.get('href')[-10:]
url = "http://....../gongShiShowStudentInfo.asp?studentId="+str(http)
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text,'lxml')
try:
print("找到学号"+str(http)+"的信息如下:")
s_no=soup.find_all('td', colspan="3")[0].get_text()
s_name=soup.find_all('td', colspan="3")[1].get_text()
s_coll=soup.find_all('td', colspan="3")[2].get_text()
s_en=soup.find_all('td', colspan="3")[3].get_text()
if soup.find_all('td', colspan="3")[4].get_text()=="主要创新点":
s_pap=soup.find_all('td', colspan="2")[0].get_text()
print("学号:"+s_no+","+"姓名:"+s_name+","+"所在院系:"+s_coll+","+"申请专业:"+s_en+","+"论文题目:"+s_pap)
txt=s_no+","+s_name+","+s_coll+","+s_en+","+s_pap
f_res.writelines(txt+"\n")
else:
s_pap=soup.find_all('td', colspan="3")[4].get_text()
print("学号:"+s_no+","+"姓名:"+s_name+","+"所在院系:"+s_coll+","+"申请专业/工程邻域:"+s_en+","+"论文题目:"+s_pap)
txt=s_no+","+s_name+","+s_coll+","+s_en+","+s_pap
f_res.writelines(txt+"\n")
print("继续爬取下一条信息......")
except:
continue
if __name__=="__main__":
starttime = datetime.datetime.now()
f_res=open("C:/....../get_ID.csv","w",encoding='gb18030',errors='ignore')
title="学号"+","+"姓名"+","+"所在院系"+","+"申请专业/工程邻域"+","+"论文题目"
f_res.writelines(title+"\n")
print("数据获取开始......")
get_res()
print("网页信息采集完成")
f_res.close()
endtime = datetime.datetime.now()
cost=endtime - starttime
print('The time cost: ',cost)