likes
comments
collection
share

【干货】快速爬取某招聘站点的几个思路

作者站长头像
站长
· 阅读数 42

严正声明

本文仅用于记录爬虫技术研究学习,不提供爬取脚本,所爬数据已删除,读者用于非法用途造成损失,与本文无关。爬虫技术本身并无违法违规之处,爬什么,怎么爬才是导致锒铛入狱的罪魁祸首。

0x0、引言

感觉距离上次发文已经过了很久,结果一看才25天,得益于 娃的呱(gū)呱坠地,每天的时间被延长了许多。当然期间最深的感受莫过于:上班比在家奶娃要轻松太多

【干货】快速爬取某招聘站点的几个思路

认识杰哥的读者都知道,Python实战类的文章灵感都来源于生活,休完陪产假没多久,素材就来了。一位 资深产品兼原创音乐人 朋友在群里@我:

【干货】快速爬取某招聘站点的几个思路

秒懂,就是想搞多点数据,恰逢金三银四,杰哥也想了解下Android岗位的行情,索性就折腾一下~

【干货】快速爬取某招聘站点的几个思路


0x1、思路一:浏览器模拟访问

打开招聘站点,搜索 "产品经理",滑动到底部发现果然只有10页:

【干货】快速爬取某招聘站点的几个思路

尝试切到第2页,页面URL发生变化:

https://xxx/web/geek/job?query=产品经理&city=101280600&page=2

尝试把page改成11,能请求,但页面数据和第10页是相同的,果然 一次搜索结果最多搞到300条数据

不过注意,它是 条件搜索,这个条件能搜300条,换个条件又能搜到300条。

换而言之,我们可以通过 设置不同的条件,来采集更多的数据,留意网页顶部的 修改城市和区域

【干货】快速爬取某招聘站点的几个思路

很明显,我们只要覆盖尽可能多的 城市-区-地名 组合,就可以采集到大量数据。接着就是批量爬取这三层条件的可选值。F12打开 开发者工具 开始抓包,对应的接口都很好抓,顺带写出爬取代码,先是 → 城市编码

https://xxx/wapi/zpCommon/data/cityGroup.json

【干货】快速爬取某招聘站点的几个思路

然后到 → 区编码和地名编码

https://xxx/wapi/zpgeek/businessDistrict.json?cityCode={}

【干货】快速爬取某招聘站点的几个思路

可以,万事俱备,只欠拿这些参数去批量调 搜索岗位的接口 了:

https://xxx/wapi/zpgeek/search/joblist.json?scene=1&query=产品经理&city={}&experience=&degree=&industry=&scale=&stage=&position=&jobType=&salary=&multiBusinessDistrict={}:25&multiSubway=&page=1&pageSize=30

【干货】快速爬取某招聘站点的几个思路

正当我以为可以挂机去泡杯茶喝喝,结果一运行:

【干货】快速爬取某招聘站点的几个思路

笑死,调这个接口直接触发站点的 反爬,尝试解决:

  • 复制粘贴浏览器请求的所有 请求头,设置到requests中 → 没用
  • ip访问次数限制?上 代理隧道没用,淦,我还以为设置代理没生效;
  • Cookies问题?复制浏览器请求里的Cookies塞requests中 → 没用

又细看了一下,发现是 Cookies加密,每次请求Cookies中的 zp_stoken 都是变化的。

【干货】快速爬取某招聘站点的几个思路

又试了下 M端接口,一样会触发反爬,逆向js太磨人,朋友要得也急,木得时间慢慢玩破解,直接上传统艺能 浏览器自动化。无脑 pyppeteer 模拟,先跳转登录页,预留足够的时间登陆:

【干货】快速爬取某招聘站点的几个思路

登陆完就可以关页面了,这一步主要是让浏览器处于 登录态,接着就是 拼接url访问获得页面源码提取数据

【干货】快速爬取某招聘站点的几个思路

爬取后的数据:

【干货】快速爬取某招聘站点的几个思路

遍历文件统计下总共爬取到多少条:

【干货】快速爬取某招聘站点的几个思路

18663条,还凑合,不过混进来一些 脏东西

【干货】快速爬取某招聘站点的几个思路

需要对数据进行清洗,这里判断职位包含"**产品"**字眼为有效数据,朋友说最好是Excel,帮人帮到底:

【干货】快速爬取某招聘站点的几个思路

运行后总共输出有效数据13928条,同时区分城市,写入到excel中~

【干货】快速爬取某招聘站点的几个思路【干货】快速爬取某招聘站点的几个思路

时间关系,只爬取到 ,而且只包含了北上广深杭的数据,不过朋友说够用了,就先酱吧~

【干货】快速爬取某招聘站点的几个思路


0x2、思路二:手机adb模拟访问

其实中途还想过从移动端入手,抓了一波包:

【干货】快速爬取某招聘站点的几个思路

2333,接口加密 + 返回数据加密,最便捷的爬取思路还是自动化啊,在《杰哥带你玩转Android自动化》 中提到过:

所有Android自动化框架和工具中 操作Android设备的功能实现 都基于 adb无障碍服务AccessibilityService

这里直接用adb来模拟,思路也很简单:无限向上滑动,同时解析布局xml提取所需数据

先写个导出当前布局xml的工具代码:

【干货】快速爬取某招聘站点的几个思路

接着写个根据resourc_id递归,获取目标结点的方法:

【干货】快速爬取某招聘站点的几个思路

打开导出的布局xml:

【干货】快速爬取某招聘站点的几个思路

可以看到结点对应的资源id,岗位列表 → xxx:id/recyclerView_list,岗位名称 → xxx:id/tv_position_name。

接着干嘛?加上死循环滑动+解析xml提取数据,一步到位?兄嘚想多了,有个难搞的问题:

如何精确控制Recyclerview滑动距离?

怎么说?

【干货】快速爬取某招聘站点的几个思路

你得保证每次滑动准确滑动到 下三项,而且每一项都要 显示完整,因为 uiautomator dump 出的是当前界面的xml。如果滑动到的位置不对,就会出现这种缺失结点的错误数据:

【干货】快速爬取某招聘站点的几个思路

滑太多又会丢失数据,看了一圈没有找到计算RecyclerView滑动距离的公式,想搞清楚,估计得去啃Recyclerview的源码。

【干货】快速爬取某招聘站点的几个思路

这里笔者取下巧:每次滑动尽量少的距离 (不超过一项) + 完整数据校验 (节点数>10) + 结点去重(key:岗位+薪资),直接肝出代码:

【干货】快速爬取某招聘站点的几个思路

挂机午睡,睡醒发现脚本停了,一看数据量:

【干货】快速爬取某招聘站点的几个思路

擦,APP端也限制了搜索结果的条数,只能查询30页,而且总数据还不够300条。

【干货】快速爬取某招聘站点的几个思路

想采集更多数据,可以像思路一一样,通过切换不同的 搜索条件 来达成,检测到数据很久没增加了,就模拟点击切换城市:

【干货】快速爬取某招聘站点的几个思路


0x3、思路三:Xposed Hook

说实话,思路二这种模拟访问解析xml的方式不太靠谱,而且还 。我们可以换个角度:

请求返回的数据是 加密 的,但设置到UI控件上的数据是 解密 过的。

所以最简单的思路就是 → Hook UI控件设置数据的方法,拿到解密过的数据

【干货】快速爬取某招聘站点的几个思路

看了下APK,没加密,直接 jadx反编译,adb获取当前页面的包名和类名:

【干货】快速爬取某招聘站点的几个思路

定位到 GeekSearchActivity

【干货】快速爬取某招聘站点的几个思路

结合APP页面,不难看出 GeekSearchResultFragment 用于显示搜索结果,跟下:

【干货】快速爬取某招聘站点的几个思路

SearchPositionFragment 明显用于显示搜索到的岗位,尝试定位布局xml,搜 R.layout. 没找着,看来是做了 资源混淆

在Fragment初始化布局,一般会把代码写到 onCreateView() 中,但没在这个类里找到,估计是在父类里进行了封装,跟一下:SearchBaseFragmentBaseAwareFragmentLazyLoadFragment

【干货】快速爬取某招聘站点的几个思路

吼,定义了抽象方法 getLayoutResId() 用于设置布局,回到 SearchPositionFragment 搜下这个方法:

【干货】快速爬取某招聘站点的几个思路

打开布局文件:

【干货】快速爬取某招聘站点的几个思路

定位到了Recyclerview,跟上面我们adb导出的xml id一致,哈哈,从控件资源id着手定位目标代码也是一个小技巧。

【干货】快速爬取某招聘站点的几个思路

Recyclerview设置数据,肯定是通过 Adapter,搜一下,发现了 SearchPositionAdapter,不过源码中没找到设置数据的方法,又是封装了好几层。

溯源后发现,顶层父类是 com.chad.library.adapter.base.BaseQuickAdapter,哈,这不就是开源库CymChad/BaseRecyclerViewAdapterHelper 么?这个库设置数据的方法很简单 setNewData()addData(),搜一下发现这里调用到了:

【干货】快速爬取某招聘站点的几个思路

Hook这两个方法,就可以拿到数据啦,这里要注意一点:

因为子类SearchPositionAdapter并没有重写这两个方法,调用的是父类方法,所以需要 Hook父类

直接写出Hook代码:

【干货】快速爬取某招聘站点的几个思路

运行后打开招聘软件,可以看到陆续输出一些日志,毕竟Hook的是父类,几乎所有列表设置数据都会调这两个方法。

来到搜索页,输入搜索词搜索,上拉加载更多,可以看到岗位相关的数据被打印出来:

【干货】快速爬取某招聘站点的几个思路

但都是自定义数据类型,我们需要的是它里面岗位信息的字段,直接定位 SearchPositionItemModel

【干货】快速爬取某招聘站点的几个思路

明显具体数据类型是这个泛型父类:

【干货】快速爬取某招聘站点的几个思路

data是父类的私有属性,这里通过 反射 来获取,需要修改下访问权限:

【干货】快速爬取某招聘站点的几个思路

运行后重复之前的操作,发现还套了一层:

【干货】快速爬取某招聘站点的几个思路

打开 SearchPositionBean

【干货】快速爬取某招聘站点的几个思路

嗯哼,这就是岗位信息相关的字段,可以按需反射获取,也可以直接遍历获取,这里直接打印出来:

【干货】快速爬取某招聘站点的几个思路

运行操作后的输出日志:

【干货】快速爬取某招聘站点的几个思路

可以看到具体的岗位信息了,但依旧有嵌套的Bean,按上面的方法还得打开对应Bean的源码,抠字段,有些蠢了。完全可以写一个 递归获取字段的方法,这里顺带将输出信息组合成Json的形式,方便采集到的数据保存。

【干货】快速爬取某招聘站点的几个思路

运行操作后的输出日志:

【干货】快速爬取某招聘站点的几个思路

看着还不是很整齐,不过Copy到Json格式化工具是没问题的:

【干货】快速爬取某招聘站点的几个思路

如果有数据采集需求,调个第三方json库格式化下,然后 导出文件 或者 调用自己写的数据上传接口(推荐,直接入库美滋滋)

【干货】快速爬取某招聘站点的几个思路

接着解决第二个问题 → 单次搜索结果30页限制,从哪里入手呢?现象:

列表滑动到底部,触发上拉加载Loading,然后列表添加数据,当加载到30页后,直接不显示上拉Loading。

跟下这个上拉加载的组件:ZPUIRefreshLayoutcom.scwang.smart.refresh.layout.SmartRefreshLayout

哟,这不是刷新库么 scwang90/SmartRefreshLayout,老朋友了,它提供了一个禁用上拉加载的API:

refreshLayout.setEnableLoadMore(false); //是否启用上拉加载功能

因为SmartRefreshLayout自带混淆,所以没法直接搜这个方法,先找到控件实例:SearchPositionFragmentSearchBaseFragment

【干货】快速爬取某招聘站点的几个思路

接着回到 SearchPositionFragment,开启 正则搜索,搜 this.d.*?(false)

【干货】快速爬取某招聘站点的几个思路

就两个匹配结果,很明显是第一个,判断 geekSearchCardResponse == null 就禁用上拉加载,而它是由 gVar.f41043b 赋值的,参数类型是 g,跟下:

【干货】快速爬取某招聘站点的几个思路

哦吼,不知道都是些啥,hook下这个方法,把入参的字段打印出来:

【干货】快速爬取某招聘站点的几个思路

运行后,第一次加载和上拉加载更多输出日志如下:

【干货】快速爬取某招聘站点的几个思路

不难看出:a → 当前页数,e → 数据总条数,每次加载15条数据。

【干货】快速爬取某招聘站点的几个思路

滑动到30页时,b这个字段为空,有两种可能:后台真的没有返回 或者 后台有返回但客户端做了限制

我在原先打日志的基础上,又加了判空,到第30页时,我发现有数据:

【干货】快速爬取某招聘站点的几个思路

搜了下<30,<=30,<31,<=31,>30 都没匹配到想要的结果,em...不知道具体做了什么操作。

那就粗暴点,直接Hook SmartRefreshLayout#b(Boolean),把入参设置为true,即:禁止关闭上拉加载

【干货】快速爬取某招聘站点的几个思路

接着adb无限滚动:

【干货】快速爬取某招聘站点的几个思路

可以数据不止30页,看了下数据也没有重复,问题二解决~

【干货】快速爬取某招聘站点的几个思路

非常简单,不过本节也到这了,读者感兴趣的话,可以自己试着往下扒,比如:请求的构造规则响应数据的解密 等,谢谢~

【干货】快速爬取某招聘站点的几个思路

转载自:https://juejin.cn/post/7205508989161013303
评论
请登录