本文作者 @东东哥。项目整体思路和最终结果展示参见文章:
东东哥:用Python爬了一线城市的房租与工资,一线城市的房租在工资的占比真的很高吗?这一篇是对其中收入数据的获取做具体讲解。
上一篇:计算房租收入比(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的编程教室
这里还有更多精彩。一起学,走得远