Python纪实:用爬虫明确求职路上的技能树(一)
来源:互联网 发布:淘宝新手入门教程视频 编辑:程序博客网 时间:2024/06/06 07:40
一直在关注各大招聘网站的招聘信息,这样做的目的是明确市场要什么样的我,怎样学习才能满足市场的需求,毕竟要在低头拉车的同时不时地抬头看路嘛!
在浏览职位列表中各个职位的时候,如果想知道具体的招聘要求,就需要点击进去,进入二级页面,查看具体的职位信息和技术要求。这给我们带来了十足的不便,一遍遍地进入返回,很容易造成疲惫,并且不能给出总的市场要求最多的技能。这时我们可以是用爬虫,爬取每个相关职位的要求技能,并统计出市场上对于某一职位大致要求。
在爬取时,我选择前程无忧和 python 为分析实例。
先观察页面结构,右键查看网页源码发现以下信息:
1.每个职位都包裹在 class="el" 的 div 中2.每个职位的职称存在于包裹在 class="t1" 的 p 中的 a 中3.每个职位的对应具体描述页面的链接既是2中 a 的 href4.每个职位的其他信息(公司、地区、薪资等)分别在类名为"t2""t3"等的 span 中5.发出请求后得到的是完整的页面,职位列表就存在于 html 中,并不像有些网站是动态加载的,所以我们要抓取的是一个静态页面
这样就可以大致确定 main 函数要做的事:
def main(): jobList = downPage(searchUrl) #下载页面并解析得到职位列表 for job in jobList[1:len(jobList)-1]: jD = parseJob(job) #解析每一个职位
函数具体实现如下:
def downPage(url): html = requests.get(url,headers = headers).content bsObj = BeautifulSoup(html, "html.parser") #这里使用 requests 和 BeautifulSoup 库解析网页 searchResult = bsObj.find("div", {"id": "resultList"}) jobList = searchResult.findAll("div", {"class": "el"}) return jobListdef parseJob(job): jobPosition = job.find("p").find("a").getText() jobPosition = re.sub(r'\s+', '', jobPosition) companyName = job.find("span", {"class": "t2"}).find("a").get("title") jobLocation = job.find("span", {"class": "t3"}).getText() jobPrice = job.find("span", {"class": "t4"}).getText() jobDate = job.find("span", {"class": "t5"}).getText() jD = [jobPosition, companyName, jobLocation, jobPrice, jobDate] return jD
要讲一下 parseJob 中的
jobPosition = re.sub(r'\s+', '', jobPosition)
这句话是得到职位职称之后进行文本过滤,在源码中是这样的:
<a target="_blank" title="python开发工程师(Odoo)" href="http://jobs.51job.com/shanghai-xhq/62630612.html?s=01&t=0" onmousedown=""> python开发工程师(Odoo) </a>
最开始我以为文本前后只有换行符和空格,我就用
jobPosition = job.find("p").find("a").getText()#.replace(" ", "").replace("\n","")
做了一个简单地过滤。最后在导入数据库的时候,发现字符串拼接之后竟然显示不全,导致 Mysql 总是报语句错误,这时我才意识到,该文本前后,不仅仅有换行符和空格,可能还存在制表符和回车符,于是我就用
r’\s+’ 来做文本过滤,
\s 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
这样下来我们只是得到了简单的职位信息,我们还需要做的是:获取多个页面的职位、得到每个职位的二级链接并分析。
针对第一个任务,再次分析页面。
<li class="bk"><a href="http://search.51job.com/list/020000,000000,0000,00,9,99,Python,2,2.html?lang=c&stype=1&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=%22">下一页</a></li>
每一个下一页被包裹起来,并且如果能够点击的话会有一个 a ,a 的 href 是对应的下一页。据此我们改造 downPage 函数,让他返回下一页的地址:
def downPage(url): html = requests.get(url,headers = headers).content bsObj = BeautifulSoup(html, "html.parser") searchResult = bsObj.find("div", {"id": "resultList"}) jobList = searchResult.findAll("div", {"class": "el"}) next = bsObj.findAll("li", {"class": "bk"})[1] nextLink = next.find("a") if nextLink: nextUrl = nextLink.get("href") return nextUrl,jobList #如果下一页存在,获取并返回下一页的地址和本页职位列表 return None,jobList #如果不存在,则只返回职位列表
这样,我们还需改造 main :
def main(): nextUrl = searchUrl count = 0 flagStop = False with open("jobList.txt","w+",encoding="utf-8") as f: while nextUrl and not flagStop: nextUrl, jobList = downPage(nextUrl) #count += 1 for job in jobList[1:len(jobList)-1]: jD = parseJob(job) result = re.search(r'python',jD[0],re.I) if not result: flagStop = True
这里使用 nextUrl 作为是否循环进入下一页获取职位列表的条件。至于另一个条件 flagStop 的添加,原因是这样的:起初我只是以列表数为获取依据,比如搜索结果有350页,我就开始解析前面的35页,然后我发现得到的技能树中竟然包括 c 这样的技能要求,然后我发现,搜索的结果中包括自动化工程师、嵌入式工程师等等,这些并不是 python 对应的正确搜索结果,他们只是相关职位,所以我决定之解析那些职位名称上包含 python 字样的职位,这样的道德结果更准确些。所以我对职位名称进行了过滤,如果不包含 python 字样,就不需要继续循环解析:
result = re.search(r'python',jD[0],re.I) if not result: flagStop = True
在第二个任务中,我们需要解析每个职位的链接页面的职位描述信息。
第一步是得到子页面 url :
def getJobLink(job): return job.find("p").find("a").get("href")
第二步是解析子页面,得到职位描述中的编程技能,并储存统计每个关键词出现的频率(这里解析职位描述和之前相同):
def getSkillTree(url): html = requests.get(url, headers=headers).content bsObj = BeautifulSoup(html, "html.parser") jobDesc = bsObj.find("div", {"class": "job_msg"}) if jobDesc: jobDesc = jobDesc.getText() jobDesc = re.sub(r'\s+', '', jobDesc) p = re.compile('([a-zA-Z]+)') #编程技能基本是英文单词,所以只过滤出英文单词 keys = p.findall(jobDesc) skillTree = list(set([s.lower() for s in keys])) #技能关键词去重 for skill in skillTree: if skill not in Skill: Skill[skill] = 0 Skill[skill] += 1 #这里的 Skill 是一个全局字典,用于记录每一个关键词和期出现的频率,全部页面的都要统计于此
这两个函数我们就添加在 parseJob 函数中:
def parseJob(job): jobPosition = job.find("p").find("a").getText()#.replace(" ", "").replace("\n","") jobPosition = re.sub(r'\s+', '', jobPosition) jobLink = getJobLink(job) getSkillTree(jobLink) companyName ……
最后我们在 main 的结尾对 Skill 技能字典进行排序打印:
sortedSkillTree = sorted(Skill.items(),key = itemgetter(1),reverse=True)print(sortedSkillTree)
整个爬虫的运行结果(我们只取前几个有意义的):
[(‘python’, 277), (‘linux’, 160), (‘mysql’, 124), (‘django’, 102),
(‘web’, 87), (‘redis’, 72), (‘flask’, 64), (‘java’, 64), (‘mongodb’,
58), (‘c’, 56), (‘css’, 53), (‘html’, 52), (‘tornado’, 50),
(‘javascript’, 49), (‘git’, 45), (‘shell’, 40), (‘sql’, 37)]
对这个结果,我们就可以有针对的进行学习了!
其实这个爬虫太过简陋,也没有涉及到反爬虫的知识,ip地址切换等等,也没有用到面向对象,不过结果还是得出来了,对这个爬虫的持续优化,后续再接着进行!
- Python纪实:用爬虫明确求职路上的技能树(一)
- 求职路上一
- 求职路上的点点滴滴
- 求职路上一(完结篇)
- 走在求职路上(一)
- python爬虫需要会的技能
- 求职路上的那些事。。。
- Python爬虫基础技能
- 校招季--献给前端求职路上的你们(H5+C3)
- 校招季--献给前端求职路上的你们(JS)
- 献给前端求职路上的你们(上)
- 献给前端求职路上的你们(下)
- Python爬虫(一)
- Python爬虫(一)
- python爬虫(一)
- Python爬虫(一)
- python爬虫(一)
- python爬虫(一)
- 一天一道leecode 1
- Linux 之 shell 比较运算符
- LinghtOJ1132 Summing up Powers
- brew install rabbitmq
- TCP/IP 三次握手协议与四次放手协议
- Python纪实:用爬虫明确求职路上的技能树(一)
- Poj 3259 Wormholes ( SPFA
- Hadoop(四)----流程
- ssm之Struts(拦截器)
- 自适应6大调整
- oracle笔记
- java学习String
- 自适应
- vue和react的比较