在上一篇51job职位信息的爬取中,对岗位信息div下各式各样杂乱的标签,简单的Xpath效果不佳,加上string()函数后,也不尽如人意。因此这次我们跳过桌面web端,选择移动端进行爬取。

一、代码结构

按照下图所示的爬虫基本框架结构,我将此份代码分为四个模块——URL管理、HTML下载、HTML解析以及数据存储。

二、URL管理模块

这个模块负责搜索框关键词与对应页面URL的生成,以及搜索结果不同页数的管理。首先观察某字段(大数据, UTF-8为'E5A4A7 E695B0 E68DAE') 全国范围内的结果,前三页结果的URL如下:

URL前半部分:

这部分中我们可以看到两处处不同,第一处为编码后'2,?.html'中间的数字,这是页数。另一处为参数stype的值,除第一页为空之外,其余都为1。另外,URL中有一连串的数字,这些是搜索条件,如地区、行业等,在这儿我没有用上。后面的一连串字符则为搜索关键词的字符编码。值得注意的是,有些符号在URL中是不能直接传输的,如果需要传输的话,就需要对它们进行编码。编码的格式为'%'加上该字符的ASCII码。因此在该URL中,%25即为符号'%'。

URL后半部分:

后半部分很明显的就能出首页与后面页面的URL参数相差很大,非首页的URL后半部分相同。

因此我们需要对某关键字的搜索结果页面分两次处理,第一次处理首页,第二次可使用循环处理后续的页面。

  1. if __name__ == '__main__':  
  2.     key = '数据开发'  
  3.     # 第一页  
  4.     url = 'https://search.51job.com/list/000000,000000,0000,00,9,99,'+key+',2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99°reefrom=99&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare='  
  5.     getUrl(url)  
  6.     # 后页[2,100)  
  7.     urls = ['https://search.51job.com/list/000000,000000,0000,00,9,99,'+key+',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='.format(i) for i in range(2,30)]  
  8.     for url in urls:  
  9.         getUrl(url)  

三、HTML下载模块

下载HTMl页面分为两个部分,其一为下载搜索结果某一页的HTML页面,另一部分为下载某一岗位具体页面。由于页面中具体岗位URL需要从搜索结果页面中获取,所以将下载搜索结果页面及获取具体岗位URL放入一个函数中,在提取到具体岗位URL后,将其传入至另一函数中。

3.1搜索结果页面下载与解析

下载页面使用的是requests库的get()方法,得到页面文本后,通过lxml库的etree将其解析为树状结构,再通过Xpath提取我们想要的信息。在搜索结果页面中,我们需要的是具体岗位的URL,打开开发者选项,找到岗位名称。

我们需要的是<a>标签里的href属性。右键,复制——Xpath,得到该属性的路径。

  1. //*[@id="resultList"]/div/p/span/a/@href  

由于xpath返回值为一个列表,所以通过一个循环,将列表内URL依次传入下一函数。

  1. def getUrl(url):  
  2.     print('New page')  
  3.     res = requests.get(url)  
  4.     res.encoding = 'GBK'  
  5.     if res.status_code == requests.codes.ok:  
  6.         selector = etree.HTML(res.text)  
  7.         urls = selector.xpath('//*[@id="resultList"]/div/p/span/a/@href')  
  8.         #                      //*[@id="resultList"]/div/p/span/a  
  9.         for url in urls:  
  10.             parseInfo(url)  
  11.             time.sleep(random.randrange(1, 4))  

3.2具体岗位信息页面下载

该函数接收一个具体岗位信息的参数。由于我们需要对移动端网页进行处理,所以在发送请求时需要进行一定的伪装。通过设置headers,使用手机浏览器的用户代理,再调用get()方法。

  1. def parseInfo(url):  
  2.     headers = {  
  3.         'User-Agent': 'Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/ADR-1301071546) Presto/2.11.355 Version/12.10'  
  4.     }  
  5.     res = requests.get(url, headers=headers)  

四、HTML解析模块

在3.2中,我们已经得到了岗位信息的移动端网页源码,因此再将其转为etree树结构,调用Xpath即可得到我们想要的信息。

需要注意的是页面里岗位职责div里,所有相关信息都在一个<article>标签下,而不同页面的<article>下层标签并不相同,所以需要将该标签下所有文字都取出,此处用上了string()函数。

  1. selector = etree.HTML(res.text)  
  2.     
  3. title = selector.xpath('//*[@id="pageContent"]/div[1]/div[1]/p/text()')  
  4. salary = selector.xpath('//*[@id="pageContent"]/div[1]/p/text()')  
  5. company = selector.xpath('//*[@id="pageContent"]/div[2]/a[1]/p/text()')  
  6. companyinfo = selector.xpath('//*[@id="pageContent"]/div[2]/a[1]/div/text()')  
  7. companyplace = selector.xpath('//*[@id="pageContent"]/div[2]/a[2]/span/text()')  
  8. place = selector.xpath('//*[@id="pageContent"]/div[1]/div[1]/em/text()')  
  9. exp = selector.xpath('//*[@id="pageContent"]/div[1]/div[2]/span[2]/text()')  
  10. edu = selector.xpath('//*[@id="pageContent"]/div[1]/div[2]/span[3]/text()')  
  11. num = selector.xpath('//*[@id="pageContent"]/div[1]/div[2]/span[1]/text()')  
  12. time = selector.xpath('//*[@id="pageContent"]/div[1]/div[1]/span/text()')  
  13. info = selector.xpath('string(//*[@id="pageContent"]/div[3]/div[2]/article)')  
  14. info = str(info).strip()  

五、数据存储模块

首先创建.csv文件,将不同列名称写入首行。

  1. fp = open('51job.csv','wt',newline='',encoding='GBK',errors='ignore')  
  2. writer = csv.writer(fp)  
  3. writer.writerow(('职位','薪资','公司','公司信息','公司地址','地区','工作经验','学历','人数','时间','岗位信息'))  

再在解析某一页面数据后,将数据按行写入.csv文件。

  1. writer.writerow((title,salary,company,companyinfo,companyplace,place,exp,edu,num,time,info))  

源码:爬取51job移动端源码(12月)

相关:智联招聘源码分析

贪吃蛇链表实现及部分模块优化

爬虫实战(二) 51job移动端数据采集的更多相关文章

  1. Python爬虫实战二之爬取百度贴吧帖子

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 前言 亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不 ...

  2. 转 Python爬虫实战二之爬取百度贴吧帖子

    静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...

  3. 爬虫实战(二) 用Python爬取网易云歌单

    最近,博主喜欢上了听歌,但是又苦于找不到好音乐,于是就打算到网易云的歌单中逛逛 本着 "用技术改变生活" 的想法,于是便想着写一个爬虫爬取网易云的歌单,并按播放量自动进行排序 这篇 ...

  4. 自学Python九 爬虫实战二(美图福利)

    作为一个新世纪有思想有文化有道德时刻准备着的屌丝男青年,在现在这样一个社会中,心疼我大慢播抵制大百度的前提下,没事儿上上网逛逛YY看看斗鱼翻翻美女图片那是必不可少的,可是美图虽多翻页费劲!今天我们就搞 ...

  5. Python网络爬虫实战(二)数据解析

    上一篇说完了如何爬取一个网页,以及爬取中可能遇到的几个问题.那么接下来我们就需要对已经爬取下来的网页进行解析,从中提取出我们想要的数据. 根据爬取下来的数据,我们需要写不同的解析方式,最常见的一般都是 ...

  6. Puppeteer爬虫实战(二)

    连接浏览器 上一篇说到了Puppeteer本质是使用了Chrome Devtools协议控制浏览器,本篇就说说连接方式. 常规Hook浏览器 此方式其实就是需要一个浏览器可执行文件(不同平台需要下载对 ...

  7. python网络爬虫实战PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书

    点击获取提取码:vg1y python网络爬虫实战帮助读者学习Python并开发出符合自己要求的网络爬虫.网络爬虫,又被称为网页蜘蛛,网络机器人,是一种按照一定的规则,自动地抓取互联网信息的程序或者脚 ...

  8. Python爬虫实战(4):豆瓣小组话题数据采集—动态网页

    1, 引言 注释:上一篇<Python爬虫实战(3):安居客房产经纪人信息采集>,访问的网页是静态网页,有朋友模仿那个实战来采集动态加载豆瓣小组的网页,结果不成功.本篇是针对动态网页的数据 ...

  9. 基于C#.NET的高端智能化网络爬虫(二)(攻破携程网)

    本篇故事的起因是携程旅游网的一位技术经理,豪言壮举的扬言要通过他的超高智商,完美碾压爬虫开发人员,作为一个业余的爬虫开发爱好者,这样的言论我当然不能置之不理.因此就诞生了以及这一篇高级爬虫的开发教程. ...

随机推荐

  1. Win & Mac 系统之间U盘传递的U盘文件格式选取问题

    Win & Mac 系统之间U盘传递的U盘文件格式选取问题 1. Win系统与Mac系统之间可以通过 exFat U盘文件系统传递 exFAT(Extended File Allocation ...

  2. Eclipse的使用技巧

    Eclipse有强大的编辑功能, 工欲善其事,必先利其器, 掌握Eclipse快捷键,可以大大提高工作效率. 小坦克我花了一整天时间, 精选了一些常用的快捷键操作,并且精心录制了动画, 让你一看就会. ...

  3. Delphi for iOS开发指南(1):在Mac上配置你的开发环境

    http://cache.baiducontent.com/c?m=9d78d513d99516f11ab7cf690d678c3b584380122ba7a0020fd18438e4732b4050 ...

  4. delphi执行一个外部程序,当外部程序结束后,delphi程序立即响应

    //需要引用 ShellAPI 单元;procedure TForm1.Button1Click(Sender: TObject); var SEInfo: TShellExecuteInfo; Ex ...

  5. ReactJS 官网案例分析

    案例一.聊天室案例 /** * This file provided by Facebook is for non-commercial testing and evaluation * purpos ...

  6. 拒绝“高冷”词汇!初学C#中的委托

    有一天,你写了好多好多带“形参”的构造函数(就是“方法”,同义),而且需要向这些构造函数里传递同样的“实参”,然后你就憨憨地一个一个函数的调用并赋予同样的“实参”,这一天就这么过去了... 又过了几天 ...

  7. 【WPF】UserControl 的 Load事件

    经过查看MSDN,总结下 UserControl 的 Load 事件: Q1:Load事件什么时候发生? 在控件第一次变为可见之前发生. Load事件发生在创建 UserControl 时,因此有些情 ...

  8. Unity/C#基础复习(3) 之 String与StringBuilder的关系

    参考资料 [1] @毛星云[<Effective C#>提炼总结] https://zhuanlan.zhihu.com/p/24553860 [2] <C# 捷径教程> [3 ...

  9. VSCode开发(一)——入门基础

    8天掌握EF的Code First开发之Entity Framework介绍   返回<8天掌握EF的Code First开发>总目录 本篇目录 Entity Framework概要 什么 ...

  10. Python 将字典的元素按照键或者值的大小进行排序

    在开发的过程中有时遇到这样的需求,一个字典里保存了一份完整的数据,其中键是一个id,值是时间,需要获取最新的5条数据,处理方式如下: 假设字典数据的变量名为my_dict data_list = so ...