一、先上python代码
#coding:utf-8
import requests
import random
import json
import datetime
import pandas as pd
import threading
import time
#创建多线程来执行任务
def create_task(paramsList):
threadList=[] #设置一个线程列表存储创建的线程
print "一个有{}个参数".format(len(paramsList)) #打印出构建了多少个请求参数,有多少个参数就代表要执行多少次请求任务
num=len(paramsList)/50 #每个线程执行50个任务,那需要多少个线程,50可以改变
print '准备创建{}个线程'.format(num+1) #为防止漏掉任务,在上面结果上线程最好再+1
for count in range(num+1): #for循环创建线程,同时要为每个线程分配不同的任务
startParams = paramsList[::50][count] # 按50步长去切割参数列表,可以得到不同切割点的值
startIndex=paramsList.index(startParams) #获取切割点的起始索引位置
endIndex= startIndex + 50 #起始索引位置+50,为结束索引为准
threadParamsList=paramsList[startIndex:endIndex] #根据起点和结束点,为参数列表分配不同任务
print "已为一个线程分配好参数,有{}个参数".format(len(threadParamsList))
thread=threading.Thread(target=get_data,args=(threadParamsList,)) #调用get_data方法,并为每个线程传入不同参数
threadList.append(thread) #将线程添加到线程列表
for thread in threadList:
thread.setDaemon(True) # 设置守护线程
thread.start() #开始线程
for thread in threadList:
thread.join() #所有子线程结束后再退出
#城市ID和pageNum构建请求参数
def create_params(cityIdList,pageNum):
paramsList=[]
for cityId in cityIdList:
for num in range(1,pageNum+1):
params={
'currentPage':num,
'cityId':cityId,
'showCount':18
}
paramsList.append(params)
return paramsList
#请求接口获取数据
def get_data(threadParamsList):
print "当前线程{}在执行任务...".format(threading.current_thread().name)
#这里的list是用来存储每个线程爬取到的数据,存储完成后,再添加到全局变量list里
cityIdList = []
titleList = []
detailDescList = []
subTitleList = []
locationList = []
showPriceList = []
labelsList = []
latList = []
lngList = []
url='https://api.mgzf.com/room-find-web/find/list'
for params in threadParamsList:
randomTime=random.uniform(1,3) #随机的请求时间间隔,防止被封IP
time.sleep(randomTime)
res=requests.post(url,data=params,headers=headers)
resDict=json.loads(res.text) #将json数据转化为dict类型
resList=resDict['content']['list']
labelDataList=[]
for data in resList:
#下面是根据返回的数据格式解析到自己想要的数据,这里具体场景具体分析
cityIdList.append(data['cityId'])
titleList.append(data['title'])
detailDescList.append(data['detailDesc'])
subTitleList.append(data['subTitle'])
locationList.append(data['location'])
showPriceList.append(data['showPrice'])
for labelData in data['labels']: #label数据比较特殊,需要再一次for循环
labelDataList.append(labelData['title'])
labelStr=','.join(labelDataList) #拼接获取到标签
labelDataList=[] #请注意需要清空对应数据,否则拼接的数据会出错
labelsList.append(labelStr)
latList.append(data['lat'])
lngList.append(data['lng'])
#将每个爬虫获取到的数据添加到全局变量中,请注意这里要用extend,不用append,因为extend是追加列表数据
cityId.extend(cityIdList)
title.extend(titleList)
detailDesc.extend(detailDescList)
subTitle.extend(subTitleList)
location.extend(locationList)
showPrice.extend(showPriceList)
labels.extend(labelsList)
lat.extend(latList)
lng.extend(lngList)
print "当前线程{}执行任务完成".format(threading.current_thread().name)
#存储数据
def save_data():
#由于接口返回数据没有城市名称,所以这边要比对所有cityId,从最初设置的cityData中拿到城市名称
for id in cityId:
for cityDict in cityData:
if int(id)==cityDict['cityId']: #id类型需要由unicode转化为int
cityName.append(cityDict['name'])
#构建一个字典,每个字段都是一个series,值是列表,方便把数据通过dateframe存储为表格形式
data={
'cityName':cityName,
'cityId':cityId,
'title':title,
'detailDesc':detailDesc,
'subTitle':subTitle,
'location':location,
'showPrice':showPrice,
'labels':labels,
'lat':lat,
'lng':lng
}
print '开始存储数据,共有{}条数据'.format(len(data['cityId']))
data=pd.DataFrame(data) #转化为datefram表格数据
data.to_csv('../数据源/蘑菇租房数据.csv',encoding='utf-8-sig') #存储数据
print "数据存储完成"
if __name__=='__main__':
startTime=time.time() #记录程序开始时间,后面用来计算爬虫耗时
cityData=[
{
'name':'上海',
'cityId':289
},
{
'name': '成都',
'cityId':75
},
{
'name': '东莞',
'cityId':119
},
{
'name': '南京',
'cityId':315
},
{
'name': '合肥',
'cityId':127
},
{
'name': '北京',
'cityId':131
},
{
'name': '杭州',
'cityId':179
},
{
'name': '重庆',
'cityId':132
},
{
'name': '泉州',
'cityId':134
},
{
'name': '贵阳',
'cityId':146
},
{
'name': '长沙',
'cityId':158
},
{
'name': '宁波',
'cityId':180
},
{
'name': '厦门',
'cityId':194
},
{
'name': '武汉',
'cityId': 218
},
{
'name': '苏州',
'cityId': 224
},
{
'name': '西安',
'cityId': 233
},
{
'name': '广州',
'cityId': 257
},
{
'name': '郑州',
'cityId': 268
},
{
'name': '济南',
'cityId': 288
},
{
'name': '福州',
'cityId': 300
},
{
'name': '天津',
'cityId': 332
},
{
'name': '深圳',
'cityId': 340
}
] #通过浏览器审查元素可以得到每个城市的名称和城市ID,城市ID在后面请求接口的时候会用到
idList=[]
for city in cityData:
idList.append(city['cityId']) #从cityData中把城市ID提取出来
#通用的请求头设置,这里没啥好说的
headers={
'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
'refer':'http://www.mgzf.com/list/',
'origin':'http://www.mgzf.com',
'host':'api.mgzf.com',
'Connection':'keep-alive',
'Content-Type':'application/x-www-form-urlencoded'
}
#因为多线程去爬取数据,所以必须设置一个全局变量用来存储爬到的数据,因为各线程爬到的结果和速度都不一样
cityId = []
cityName=[]
title = []
detailDesc = []
subTitle = []
location = []
showPrice = []
labels = []
lat = []
lng = []
page=raw_input("请输入爬取的页数:") #从页面上看,最大页数好像是30,所以输入30差不多
pageNum=int(page) #输入的是str类型,需要转化为int
paramsList=create_params(idList,pageNum) #调用构建请求参数方法
create_task(paramsList) #调用创建多线程的方法
save_data() #调用存储数据的方法
endTime=time.time() #程序结束时时间
print "爬虫结束,共耗时{}".format(endTime-startTime) #打印出整个爬虫的耗时
二、爬取过程和结果
三、利用tableau进行数据分析
1、不同城市平均房租
1)可以看到上海,杭州,北京的平均房租排名前,符合预期
2)第一名的平均房租是最后一名郑州的几乎3倍价格,可以想象到一线城市的压力
3)在下钻城市,拿上海举例,其中虹口区房租最贵高达5527,宝山和奉贤由于较为偏远,平均房租稍微便宜了点,而我住的普通平均房租是3515
2、不同城市小区词云图
1)筛选上海这座城市,可以看到九亭这边房源数比较多,属于松江区域
2)图上颜色越深代表平均房租越贵,从大概位置上看以虹口徐汇地区为主
3、不同城市户型分析
1)筛选了一线城市(上海北京杭州)的户型,可以看到以一室一厅一卫为主,说明市场需求以这种一室一厅的整租为主,从价格上3000元也在可接收范围内
2)再看看二三线城市的分布(宁波郑州等),可以看出,户型出租以多居室为住,平均单间价格为900
3)从这2个图可以看出,一二线城市的出租类型差异化还是很大的
4、不同城市房子描述分析
1)对于一线城市(比如上海),地铁,中心地段,为主要描述关键词