Python高级应用程序设计
Posted robot9
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python高级应用程序设计相关的知识,希望对你有一定的参考价值。
Python高级应用程序设计任务要求
用Python实现一个面向主题的网络爬虫程序,并完成以下内容:
(注:每人一题,主题内容自选,所有设计内容与源代码需提交到博客园平台)
一、主题式网络爬虫设计方案(15分)
1.主题式网络爬虫名称
基于requests库抓取实习僧网站进行岗位分析
2.爬虫架构设计
受爬虫中反爬策略影响,为了能够顺利抓取不影响本机ip的情况下选择使用代理的方式进行数据的抓取。于是我将爬虫的抓取部分分为4个部分,通过这4个部分来进行数据
的抓取。
3.主题式网络爬虫爬取的内容与数据特征分析
抓取不同城市的岗位要求信息进行分析。
我们目的是对北上广深杭5个城市进行岗位需求统计,来反应近些年内哪个城市发展最为迅速(发展快对于各个岗位的需求量都会增大)。
4主题式网络爬虫设计方案概述(包括实现思路与技术难点)
实现思路:
(1)利用requests方法请求网页。
(2)利用BeautifuSoup进行网页解析,同时利用其语法获取需要的数据信息。
(3)利用循环实现翻页获取数据。
技术难点:
(1)网站的反爬策略
(2)代理访问本身的IP地址的不稳定性
(3)对数据的解析
遇到难题:
一开始我们是以Boss直聘网站进行的抓取的,但是后来发现Boss直聘网站在今年10月份更新了反爬策略。我们在抓取二级页面时,一直都是在一个等待界面。我百度了一下,又说是因为重定向的问题
会导致这样。我查阅了一些论坛,说重定向有三种,一种是在服务器中做重定向。这种方式的重定向是会返回301或302的状态码以及一个跳转连接,但是我们的返回代码是200。因此不是这个原因导致的。
第二种meta refresh,即网页中的<meta>标签声明了网页重定向的链接,这种重定向由浏览器完成。但是我在meta标签下并没有看到local这个属性,通常会有一个url在标签当中,但是我并没有找到。第三种
是js重定向,但是我们并没有学习过js,于是我多方求助,让别人帮我看了几千行的js代码,终于发现了问题所在。在js代码当中有一个z_token字段,该字段是一个随机生成的字符串,相当长。将该字段放入
cookie中便可以访问该页面。我没有找到方式获取这个字段,同时该字段有效时长仅为2分钟这就断绝了像使用headers的方法来访问。我咨询了学习前后端开发的同学,了解到因为这个字段为所及生成的,即
使使用同样的函数去生成因为不是同一次调用返回的结果也不会相同。这个问题卡住了我们很久。后续了解到selenium库可以模拟浏览器进行访问,这样就可以让浏览器自动的获取到那个字段进行访问。这样
带来的问题是抓取速度得进行限制同时我也不清楚其使用代理的方式。在查找相关资料的过程中,我发现了通过Selenium库进行模拟是,有一个字段是会提示自动化脚本的,一旦网站截取到了这个字段就依然
会被屏蔽 ,且不能通过简单覆盖消除。因为时间的问题,并不允许后续问题出现的太多,所以我们后面更换了爬取网站。不确定因素太多容易导致超出我们的预期。
在换目标网站前,我们完成了抓取方案的设计,对网页的二级页面分析完毕。由于二级页面访问问题卡了3周左右,导致项目进程缓慢,后续工作无法展开。
二、主题页面的结构特征分析(15分)
1.主题页面的结构特征
二级页面:
<a title="上门家教对" class="title ellipsis font" href="https://www.shixiseng.com/intern/inn_5rv7tenkrczo" target="_blank" data-v-6dc0c1eb="">上门家教对</a>
二级页面中的目标岗位详细信息都放在这样的一个a标签中,我们可以通过BeautifulSoup类的find_all方法获取到所有的这个一个a标签。对这样的一个返回参数进行迭代,迭代时对象是tag,通过使用tag[‘key’]的方式获取到其href中的三级页面标签。
三级页面:
三级页面中有多个div标签,岗位信息都在其中。我们通过BeautifulSoup方法获取每一块的标签内容,然后强转类型,通过get_text方法获取其中的字符。
这种方法就无需关注div内部的标签是否一直,获取对应的字符就可了。
三、网络爬虫程序设计(60分)
1.编程环境
我负责的部分是在VS 2017上进行编写的Python程序。
2.流程图
申请IP地址
抓取过程的流程图
抓取源码:
main:
from Boss import * if __name__ == "__main__": Boss1 = Boss() Boss1.run(250)#抓取250页二级页面的岗位信息
Boss:
import re import requests import json from selenium import webdriver from IPPool import * from City import * from bs4 import BeautifulSoup from requests.exceptions import * #针对实习僧的爬取类 class Boss: num=0 #设置user-agent headers = {\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362\',} url="https://www.shixiseng.com/" #初始化,设置url及筛选条件 def __init__(self): #对地址池进行初始化 self.ippool=IPPool(self.url,5) self.ippool.set_IP()#请求页面方法,返回一个response def Get_1(self,href): Error_flag=1 while(Error_flag): Error_flag=0 proxies = self.ippool.get_IP() try:response = requests.get(href,headers=self.headers,proxies=proxies) except (ProxyError,SSLError): Error_flag=1 else: #response = brower.get(self.url) print(response.status_code) return response.content.decode() #保存html字串 def save_html(self,html_str,file_path): with open(file_path,"w",encoding="utf-8")as f: f.write(html_str) self.num+=1 #对标签进行截取函数 def get_tap(self,tag,key,name,soup1): for j in soup1.find_all(tag,class_=name): if(j[key]==name): message = BeautifulSoup(str(j)) return message #运行爬虫 def run(self,MAX): k=250 n=1 message={} buf=\'\' response = self.Get_1("https://www.shixiseng.com/interns?page=1&keyword=&type=intern&area=&months=&days=°ree=%E6%9C%AC%E7%A7%91&official=&enterprise=&salary=-0&publishTime=&sortType=&city=%E5%85%A8%E5%9B%BD&internExtend=")#获取到了页面的HTML代码 if(k>MAX): return 0 file_path2 = \'data\\{}.html\'.format(k)#存放二级页面 with open(file_path2,"w",encoding = \'utf-8\' ) as f: f.write(response) soup = BeautifulSoup(open(file_path2,\'r\',encoding=\'utf-8\')) print(soup.get_text) message1=BeautifulSoup(str(soup.find_all("div",id="__layout"))) message2=BeautifulSoup(str(message1.find_all("div",class_="interns"))) message3=BeautifulSoup(str(message2.find_all("div",class_="result-list clearfix"))) message4=BeautifulSoup(str(message3.find_all("div",class_="primary-content f-l"))) message5=BeautifulSoup(str(message4.find_all("div",style="display:;"))) message=message5.find_all("a",class_="title ellipsis font") for j in message: print(j[\'href\']) file_path3 = \'html3\\第{}页-{}.html\'.format(k,n) buf=j[\'href\'] r=self.Get_1(buf) self.save_html(r,file_path3) n+=1 while(k<MAX): k+=1 n=1 message={} buf=\'\' tag_url="https://www.shixiseng.com/interns?page={}&keyword=&type=intern&area=&months=&days=°ree=%E6%9C%AC%E7%A7%91&official=&enterprise=&salary=-0&publishTime=&sortType=&city=%E5%85%A8%E5%9B%BD&internExtend=".format(k) response = self.Get_1(tag_url)#获取到了页面的HTML代码 if(k>=MAX): return 0 file_path2 = \'data\\{}.html\'.format(k)#存放二级页面 with open(file_path2,"w",encoding = \'utf-8\' ) as f: f.write(response)
#层层解析页面标签 soup = BeautifulSoup(open(file_path2,\'r\',encoding=\'utf-8\')) message1=BeautifulSoup(str(soup.find_all("div",id="__layout"))) message2=BeautifulSoup(str(message1.find_all("div",class_="interns"))) message3=BeautifulSoup(str(message2.find_all("div",class_="result-list clearfix"))) message4=BeautifulSoup(str(message3.find_all("div",class_="primary-content f-l"))) message5=BeautifulSoup(str(message4.find_all("div",style="display:;"))) message=message5.find_all("a",class_="title ellipsis font") for j in message: file_path3 = \'html3\\第{}页-{}.html\'.format(k,n) buf=j[\'href\'] r=self.Get_1(buf) self.save_html(r,file_path3)#保存三级页面 n+=1
IPPool:
import requests import json from requests.exceptions import * #导入requests的异常库 class IPPool: i = 0 #表示当前选中地址池中的哪一个地址 def __init__(self,url,max): self.url=url #地址池要访问的地址 self.max=max #地址池的最大值 self.ipurl="http://webapi.http.zhimacangku.com/getip?num={}&type=2&pro=&city=0&yys=0&port=11&time=1&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=2®ions=".format(max)#获取json格式的地址资源 def set_IP(self): r=requests.get(self.ipurl) data=r.json() #对放回的json包进行解析 self.ip=data[\'data\'] self.max=5 print(self.ip) def get_IP(self): Error_flag=0 time = 10 if(len(self.ip)==0): self.set_IP() #对超出范围的值归零,使地址池变成一个循环的表 if(self.i>=self.max-1): self.i=0 #若当前地址响应时间小于1s则使用该地址否则删除该地址 while(time>9): #若地址池为空,则重新申请一次地址 if(len(self.ip)==0): self.set_IP() self.i=0 continue if(self.i>=self.max): self.set_IP() self.i=0 continue data = self.ip[self.i] print(data[\'ip\']+\':\'+str(data[\'port\']))#输出测试 Ip=str(\'https://\'+data[\'ip\']+\':\'+str(data[\'port\']))#当前返回的ip地址及端口 if(Ip==""): self.set_IP() self.i=0 continue proxies = {\'https\':Ip} print(proxies) #利用该地址尝试访问url try:r=requests.get(self.url,proxies=proxies) except (ProxyError,SSLError):#对遇到的代理异常做处理 del self.ip[self.i] self.max-=1 self.i+=1 if(self.i>=self.max-1): self.i=0 while(1):#直到没有异常 try:r=requests.get(self.url,proxies=proxies) except (ProxyError,SSLError): try:del self.ip[self.i] except IndexError: Error_flag=1 self.set_IP() break; else: self.max-=1 self.i+=1 if(self.i>=self.max-1): self.i=0 else :break else : if(self.i>=self.max-1): self.i=0 #得到响应的时间,单位s time=r.elapsed.total_seconds() #超时的删除 if(time>9): if(self.i<=self.max): del self.ip[self.i] self.max-=1 self.i+=1 #if(self.n>15):#防止地址失效最快的解决方案 地址质量不好时使用 # self.set_IP() # Error_flag==1 # self.n=0 #self.n+=1 if Error_flag==1: Error_flag=0 continue return proxies
结果
以上是我负责部分。后续解析在完整代码中提供。
3.数据分析与可视化
上图是北上广深杭的各类岗位总需求的对比
上图为软件行业在各个地方的分布占比。
4.程序完整代码
爬取部分(分为三个文件)
主函数
from Boss import * from Message import Message import requests if __name__ == "__main__": Boss1 = Boss() #message=Message(\'html3\') #headers = {\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362\'} #解析数据时停用 Boss1.run(250) #message.run()
IPPool
import requests import json from requests.exceptions import * #导入requests的异常库 class IPPool: i = 0 #表示当前选中地址池中的哪一个地址 def __init__(self,url,max): self.url=url #地址池要访问的地址 self.max=max #地址池的最大值 self.ipurl="http://webapi.http.zhimacangku.com/getip?num={}&type=2&pro=&city=0&yys=0&port=11&time=1&ts=0&ys=0&cs=0&lb=1&sb=0&pb=4&mr=2®ions=".format(max)#获取json格式的地址资源 def set_IP(self): r=requests.get(self.ipurl) data=r.json() #对放回的json包进行解析 self.ip=data[\'data\'] self.max=5 print(self.ip) def get_IP(self): Error_flag=0 time = 10 if(len(self.ip)==0): self.set_IP() #对超出范围的值归零,使地址池变成一个循环的表 if(self.i>=self.max-1): self.i=0 #若当前地址响应时间小于1s则使用该地址否则删除该地址 while(time>9): #若地址池为空,则重新申请一次地址 if(len(self.ip)==0): self.set_IP() self.i=0 continue if(self.i>=self.max): self.set_IP() self.i=0 continue data = self.ip[self.i] print(data[\'ip\']+\':\'+str(data[\'port\'])) Ip=str(\'https://\'+data[\'ip\']+\':\'+str(data[\'port\'])) if(Ip==""): self.set_IP() self.i=0 continue proxies = {\'https\':Ip} print(proxies) #利用该地址尝试访问url try:r=requests.get(self.url,proxies=proxies) except (ProxyError,SSLError):#对遇到的代理异常做处理 del self.ip[self.i] self.max-=1 self.i+=1 if(self.i>=self.max-1): self.i=0 while(1):#直到没有异常 try:r=requests.get(self.url,proxies=proxies) except (ProxyError,SSLError): try:del self.ip[self.i] except IndexError: Error_flag=1 self.set_IP() break; else: self.max-=1 self.i+=1 if(self.i>=self.max-1): self.i=0 else :break else : if(self.i>=self.max-1): self.i=0 #得到响应的时间,单位s time=r.elapsed.total_seconds() #超时的删除 if(time>9): if(self.i<=self.max): del self.ip[self.i] self.max-=1 self.i+=1 #if(self.n>15):#防止地址失效最快的解决方案 # self.set_IP() # Error_flag==1 # self.n=0 #self.n+=1 if Error_flag==1: Error_flag=0 continue return proxies
Boss
1 import re 2 import requests 3 import json 4 from selenium import webdriver 5 from IPPool import * 6 from City import * 7 from bs4 import BeautifulSoup 8 from requests.exceptions import * 9 10 #针对实习僧的爬取类 11 class Boss: 12 num=0 13 #设置user-agent 14 headers = {\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362\',} 15 url="https://www.shixiseng.com/" 16 #初始化,设置url及筛选条件 17 def __init__(self): 18 #对地址池进行初始化 19 self.ippool=IPPool(self.url,5) 20 self.ippool.set_IP() 21 #设置登陆账号 22 def login(admin,passwd): 23 self.admin=admin 24 self.passwd=passwd 25 #请求页面方法,返回一个response 26 def Get_1(self,href): 27 Error_flag=1 28 while(Error_flag): 29 Error_flag=0 30 proxies = self.ippool.get_IP() 31 try:response = requests.get(href,headers=self.headers,proxies=proxies) 32 except (ProxyError,SSLError): 33 Error_flag=1 34 else: 35 #response = brower.get(self.url) 36 print(response.status_code) 37 return response.content.decode() 38 #保存html字串 39 def save_html(self,html_str,file_path): 40 with open(file_path,"w",encoding="utf-8")as f: 41 f.write(html_str) 42 self.num+=1 43 44 #对标签进行截取函数 45 def get_tap(self,tag,key,name,soup1): 46 for j in soup1.find_all(tag,class_=name): 47 if(j[key]==name): 48 message = BeautifulSoup(str(j)) 49 return message 50 #运行爬虫 51 def run(self,MAX): 52 k=250 53 n=1 54 message={} 55 buf=\'\' 56 response = self.Get_1("https://www.shixiseng.com/interns?page=1&keyword=&type=intern&area=&months=&days=°ree=%E6%9C%AC%E7%A7%91&official=&enterprise=&salary=-0&publishTime=&sortType=&city=%E5%85%A8%E5%9B%BD&internExtend=")#获取到了页面的HTML代码 57 if(k>MAX): 58 return 0 59 file_path2 = \'data\\{}.html\'.format(k)#存放二级页面 60 with open(file_path2,"w",encoding = \'utf-8\' ) as f: 61 f.write(response) 62 soup = BeautifulSoup(open(file_path2,\'r\',encoding=\'utf-8\')) 63 print(soup.get_text) 64 message1=BeautifulSoup(str(soup.find_all("div",id="__layout"))) 65 message2=BeautifulSoup(str(message1.find_all("div",class_="interns"))) 66 message3=BeautifulSoup(str(message2.find_all("div",class_="result-list clearfix"))) 67 message4=BeautifulSoup(str(message3.find_all("div",class_="primary-content f-l"))) 68 message5=BeautifulSoup(str(message4.find_all("div",style="display:;"))) 69 message=message5.find_all("a",class_="title ellipsis font") 70 for j in message: 71 print(j[\'href\']) 72 file_path3 = \'html3\\第{}页-{}.html\'.format(k,n) 73 buf=j[\'href\'] 74 r=self.Get_1(buf) 75 self.save_html(r,file_path3) 76 n+=1 77 while(k<MAX): 78 k+=1 79 n=1 80 message={} 81 buf=\'\' 82 tag_url="https://www.shixiseng.com/interns?page={}&keyword=&type=intern&area=&months=&days=°ree=%E6%9C%AC%E7%A7%91&official=&enterprise=&salary=-0&publishTime=&sortType=&city=%E5%85%A8%E5%9B%BD&internExtend=".format(k) 83 response = self.Get_1(tag_url)#获取到了页面的HTML代码 84 if(k>=MAX): 85 return 0 86 file_path2 = \'data\\{}.html\'.format(k)#存放二级页面 87 with open(file_path2,"w",encoding = \'utf-8\' ) as f: 88 f.write(response) 89 soup = BeautifulSoup(open(file_path2,\'以上是关于Python高级应用程序设计的主要内容,如果未能解决你的问题,请参考以下文章