9

计算房租收入比(2)- 100行代码采集招聘职位数据

 3 years ago
source link: https://zhuanlan.zhihu.com/p/126577380
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

计算房租收入比(2)- 100行代码采集招聘职位数据

上海交通大学 计算机应用技术硕士

本文作者 @东东哥。项目整体思路和最终结果展示参见文章:

这一篇是对其中收入数据的获取做具体讲解。

上一篇:计算房租收入比(1)- scrapy 爬取网上租房信息

附代码:https://gitee.com/crossin/snippet/tree/master/rent

一、背景

为了分析一线城市的房价在工资的占比,我用Python分别爬取了自如以及拉勾的数据。前一篇文章用scrapy爬取了自如房源信息,本文接着爬取拉钩的信息。这次我们直接使用requests进行爬取。

数据预览:

二、分析网页结构

拉勾url: https://www.lagou.com/jobs/list_/p-city_0?px=new1. 打开链接,观察网页,职位基本信息都可以看到,因此就不进入职位详情页爬取。点击“下一页”,发现网站的url并没有任何变化,说明整个职位信息都是通过Ajax得到的。所以接下来就是找出勾网的 ajax 请求地址传递参数得到 json 数据,并提取我们所需要的信息。2. 打开浏览器F12,进入开发者工具,选择Network,我们选择XHR(XmlHttpRequest)就可以选出Ajax的请求包,但是url比较多的话就不好找。所以将职位表中任一职位,复制到 2 中查找,双击 3 就可以得到 4,可以看到response中的数据就是以 json 保存的:

3. 点击上图中的 preview可以看到职位信息在result中,且职位数totalcount有221202条,但是在翻页时我们最多只能翻到30页,所以需要先获得 totalcount 后求出页数,再进行抓取。

4. 点进去就能看到header信息

5. 可以发现是一个post。request_url, 就是请求 ajax 文件的 url。formdata中可以看到, 请求参数有 first 测试后发现保持默认即可,pn 是页码数,kd 是关键词,可以是城市,职位,公司。

6. 观察 Cookie

在 user_trace_token 中很容易看出 20200224125912 这部分是时间戳,后面的部分则是 UUID,所以拉勾的cookie 是不断变化的,不能直接复制浏览器中的 cookie。

我采用的方法是先请求这个URL:https://www.lagou.com/jobs/list_/p-city_0?px=new,携带返回的 cookie,再去请求请求 ajax 文件的 url

代码

def get_url(keyword,pn):
    # 浏览器地址栏显示的url
    web_url = "https://www.lagou.com/jobs/list_/p-city_0?px=new" 
    # ajax 请求地址
    headers_url = "https://www.lagou.com/jobs/positionAjax.json?px=new&needAddtionalResult=false"

    headers = {"Accept": "application/json, text/javascript, */*; q=0.01",
               "Referer": web_url,
               "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
               }
    form_data = {
            "first": "true",
            "pn": "{}".format(pn),
            "kd": "{}".format(keyword)
            }
    # 创建 cookie 对象
    session = requests.Session()
    # 发送请求,获得cookies
    session.get(url=web_url,headers=headers)
    # 传递 cookie
    response = session.post(url=headers_url, headers=headers, data=form_data)

    return response

到这里,爬拉勾的难点就已经没有了,接下来从 JSON 中提取我们需要的信息就ok了。

三、代码实现

1. 导入相关库

import requests
import json
from tqdm import trange  # pip install trange 进度条
import pymysql
from pymysql import cursor

2. 得到总页数

def total_Count(response):
    html = response.json()
    # print(page)
    total_count = html['content']['positionResult']['totalCount'] # totalCount为总个数
    pn_count = int(total_count)//15 + 1
    # 页数
    print('职位总数{},共{}页'.format(total_count,pn_count))
    return pn_count

3. 提取数据,并存入MySQL数据库

dbparams = {
            'host': '127.0.0.1',
            'port': 3306,
            'user': 'root',
            'password': '1234',
            'database': 'lagou',
            'charset': 'utf8',
            'cursorclass': cursors.DictCursor
            }
def parse_url(response):
    # 创建连接
    con= pymysql.connect(**dbparams)
    with con.cursor() as cursor:   
        json_data = json.loads(response.text)
        results = json_data['content']['positionResult']['result']
        for result in results:
            info = {
                    "positionName" : result["positionName"],
                    "companyFullName" : result["companyFullName"],
                    "companySize" : result["companySize"],
                    "industryField" : result["industryField"],
                    "financeStage" : result["financeStage"],
                    "firstType" : result["firstType"],
                    "skillLables" :str(result["skillLables"]),
                    "positionLables" : str(result["positionLables"]),
                    "createTime" : result["createTime"],
                    "city" : result["city"],
                    "district" : result["district"],
                    "salary" : result["salary"],
                    "workYear" : result["workYear"],
                    "jobNature" : result["jobNature"],
                    "education" :result["education"],
                    "positionAdvantage" : result["positionAdvantage"]
                    }
            sql = """INSERT INTO info(Id, positionName, companyFullName,companySize,
                            industryField,financeStage,firstType,skillLables,positionLables,createTime,city,district,
                            salary,workYear,jobNature,education,positionAdvantage) 
                        VALUES (null,%s, %s, %s, %s, %s,%s, %s, %s, %s, %s,%s, %s, %s, %s, %s,%s)"""
            cursor.execute(sql, (info['positionName'],info['companyFullName'],
                    info['companySize'],info['industryField'],info['financeStage'],info['firstType'],info['skillLables'],
                    info['positionLables'],info['createTime'],info['city'],info['district'],info['salary'],
                    info['workYear'],info['jobNature'],info['education'],info['positionAdvantage']))
            con.commit()
        con.close()
        # 返回结果
        return results

4. 实现翻页

def main():
    # keyword = input('输入城市, 职位或公司, 如果为空,则代表全国各城市职位 \n') #输入搜索内容
    # 我将拉勾网包含的城市抓取下来,存在了city.txt文件中   
    file = open("city.txt",encoding='utf8')
    for city in file.readlines():  # 读取城市, 也可以注释掉这行代码,用关键词输入

        keyword=city.strip('\n')
        print(keyword)  
        response = get_url(keyword,pn=1)
        num = total_Count(response)  # 获得数据总个数和页数
        for i in trange(1,int(num)+1): # 实现翻页效果
            response = get_url(keyword, pn=i)
            results = parse_url(response)
            # 测试的时候发现可以得到的总页数,但是最多只能抓取到200页
            # 所以判断如果结果为空就结束循环
            # print(results)
            if results == []:
                break

四、总结

网上说有封 IP 的现象,但是我没遇到,爬取的时候也没有设置延迟,只是一个关键词下的职位最多只能爬取200页。整个过程相对容易,我爬了拉钩网中所有城市,一共6w+数据。速度是有点慢,有兴趣的可以自己改成多线程,或者用 scrapy 实现。

附代码:https://gitee.com/crossin/snippet/tree/master/rent

------

欢迎搜索及关注:Crossin的编程教室

这里还有更多精彩。一起学,走得远


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK