当前位置: 首页 > 科技观察

爬取两万多租房数据,告诉你广州房租现状

时间:2023-03-19 01:38:21 科技观察

爬取两万多租房数据,告诉你广州租房现状三篇:爬虫工具初体验(一)听说你们的爬虫又被封了?(2)不保存爬取的数据,就是耍流氓(3)8月份,因为脑洞大开,决定用python爬虫爬取深圳的租房数据,写了一篇《用Python告诉你深圳房租有多高》,其中获得一致好评,多次转载。由于我的朋友圈大部分都在广州和深圳,所以很多朋友都找我分析一下广州租房价格的现状。这不,这篇文章是在许多声音中发布的。然后,这次对爬虫技术也进行了升级,改进了更多的细节。源码值得仔细探索。本次分析收集了广州市11个区23339条数据,如下图所示:样本数据后半区的数据量比较少,因为该区房源确实不足。所以,这个调查不是很准确,应该算是一个娱乐项目,大家可以看看。我们先看统计结果,再看技术分析。广州房源分布:(分区)天河占据了房源的绝大部分。但这块地的租金可不便宜。房屋分配租金单价:(单价每平方米每月——平均)为1平方米1个月的价格。正方形越大,价格越高。租金单价:sqm/月可以看到,天河、越秀、海珠均突破了50大关,分别为75.042、64.249、59.621,是其他区域的好几倍。如果在天河租一个20平米的房间:75.042x20=1500.84再加上水电物业费200:1500.84+200=1700.84如果按平时生活计算,早餐10元,中午15元元,晚餐15元:1700.84+40×30=2900.84,那么日常生活需要2900.84元。间歇去饭店吃饭,每个月买点衣服,路费,谈女朋友,陪女朋友逛街,给父母加25002900.84+2500=5400.84一千:5200.84+2000=7200.84月薪1万还是有点积蓄,比深圳好,但是广州的工资可能没有深圳高。租金单价:(单价每平方米每天——平均)为1平方米1天的价格。出租单价:平方米/天[捂脸]户型主要有3房2厅和2房2厅。和朋友合租房子是最好的选择,否则和不认识的人合租房子可能会发生一系列不舒服的事情。字体越大,单位数越大。户型根据出租面积统计,出租房屋以30-90平米居多。现在,只能是几个小伙伴合租取暖。租赁面积统计。租赁描述词云这是爬取的租赁描述。字体越大,标志出现的次数越多。其中,【家居、全套、豪华、齐全】占据了很大一部分,说明配套设施相当齐全。租借描述爬虫技术分析请求库:scrapy,requestsHTML分析:BeautifulSoup词云:wordcloud数据可视化:pyecharts数据库:MongoDB数据库连接:pymongo爬虫代码实现与上篇文章不同,使用scrapy爬虫框架爬取中为了抓取数据,各方面也进行了优化,例如:自动生成每个页面的地址。由于房某旗下各区的首页地址与首页以外的地址不同,但有一定的规律,所以需要拼接各部分的地址。首页地址示例:#***页http://gz.zu.fang.com/house-a073/非首页地址:#第二页http://gz.zu.fang.com/house-a073/i32/#第三页http://gz.zu.fang.com/house-a073/i33/#第四页http://gz.zu.fang.com/house-a073/i34/首先分析首页urldefhead_url_callback(self,response):soup=BeautifulSoup(response.body,"html5lib")dl=soup.find_all("dl",attrs={"id":"rentid_D04_01"})#获取url地址的dl标签my_asofeachregion=dl[0].find_all("a")#获取dl标签中的所有a标签,formmy_ainmy_as:ifmy_a.text=="unlimited":#不限区域,特殊处理self.headUrlList.append(self.baseUrl)self.allUrlList.append(self.baseUrl)continueif"surrounding"inmy_a.text:#清除周边区域的数据continue#print(my_a["href"])#print(my_a.text)self.allUrlList.append(self.baseUrl+my_a["href"])self.headUrlList.append(self.baseUrl+my_a["href"])print(self.allUrlList)url=self.headUrlList.pop(0)yieldRequest(url,回调=self.all_url_callback,dont_filter=True)然后解析非主页url。这里先获取每个区域的总页数,再拼接具体的页地址。然后根据headerurl拼接其他页码的urlefall_url_callback(self,response):#解析并拼接所有需要爬取的url地址soup=BeautifulSoup(response.body,"html5lib")div=soup.find_all("div",attrs={"id":"rentid_D10_01"})#获取每个区域的url地址的dl标签span=div[0].find_all("span")#获取dl标签中的所有span标签,span_text=span[0].textforindexinrange(int(span_text[1:len(span_text)-1])):ifindex==0:pass#self.allUrlList.append(self.baseUrl+my_a["href"])else:ifself.baseUrl==response.url:self.allUrlList.append(response.url+"house/i3"+str(index+1)+"/")continueself.allUrlList.append(response.url+"i3"+str(index+1)+"/")iflen(self.headUrlList)==0:url=self.allUrlList.pop(0)yieldRequest(url,callback=self.parse,dont_filter=True)else:url=self.headUrlList.pop(0)yieldRequest(url,callback=self.all_url_callback,dont_filter=True)***解析一个页面的数据defparse(self,response):#解析一个页面的数据self.logger.info(“==========================="soup=BeautifulSoup(response.body,"html5lib")divs=soup.find_all("dd",attrs={"class":"infel"})#获取需要爬取的divfordivindivs:ps=div.find_all("p")try:#捕获异常,因为页面中有些数据没有填写完整,或者插了广告,会没有对应的标签,所以会报错forindex,pinenumerate(ps):#从源码看,每个plabel是我们要的信息,所以遍历这里的p标签,text=p.text.strip()print(text)#输出看看是不是我们要的信息roomMsg=ps[1].text.split("|")area=roomMsg[2].strip()[:len(roomMsg[2])-1]item=RenthousescrapyItem()item["title"]=ps[0].text.strip()item["rooms"]=roomMsg[1].strip()item["area"]=int(float(area))item["price"]=int(ps[len(ps)-1].text.strip()[:len(ps[len(ps)-1].text.strip())-3])item["地址"]=ps[2].text.strip()item["交通"]=ps[3].text.strip()if(self.baseUrl+"house/")inresponse.url:#区分区域无限item["region"]="unlimited"else:item["region"]=ps[2].text.strip()[:2]item["direction"]=roomMsg[3].strip()print(item)yielditemexcept:print("糟糕,出现异常")continueiflen(self.allUrlList)!=0:url=self.allUrlList.pop(0)yieldRequest(url,callback=self.parse,dont_filter=True)主要是数据分析这里的实现是通过pymongo的一些聚合操作进行统计,然后结合相关的图标库来展示数据数据分析:#求某小区租金单价(平方米/元)defgetAvgPrice(self,region):areaPinYin=self.getPinyin(region=region)collection=self.zfdb[areaPinYin]totalPrice=collection.aggregate([{'$group':{'_id':'$region','total_price':{'$sum':'$price'}}}])totalArea=collection.aggregate([{'$group':{'_id':'$region','total_area':{'$sum':'$area'}}}])totalPrice2=list(totalPrice)[0]["total_price"]totalArea2=list(totalArea)[0]["total_area"]returntotalPrice2/totalArea2#获取每个区域一个月需要多少钱)totalAvgPriceList.append(round(avgPrice,3))totalAvgPriceDirList.append({"value":round(avgPrice,3),"name":region+""+str(round(avgPrice,3))})returntotalAvgPriceDirList#获取每个区域每天每平方米多少钱defgetTotalAvgPricePerDay(self):totalAvgPriceList=[]forindex,regioninenumerate(self.getAreaList()):avgPrice=self.getAvgPrice(地区)到talAvgPriceList.append(round(avgPrice/30,3))return(self.getAreaList(),totalAvgPriceList)#获取每个区域的统计样本数defgetAnalycisNum(self):analycisList=[]forindex,regioninenumerate(self.getAreaList()):collection=self.zfdb[self.pinyinDir[region]]print(region)totalNum=collection.aggregate([{'$group':{'_id':'','total_num':{'$sum':1}}}])totalNum2=list(totalNum)[0]["total_num"]analycisList.append(totalNum2)return(self.getAreaList(),analycisList)#获取各区房屋比例defgetAreaWeight(self):结果=自我。zfdb.rent.aggregate([{'$group':{'_id':'$region','weight':{'$sum':1}}}])areaName=[]areaWeight=[]foriteminresult:ifitem["_id"]inself.getAreaList():areaWeight.append(item["weight"])areaName.append(item["_id"])print(item["_id"])print(item["weight"])#print(type(item))return(areaName,areaWeight)#获取标题数据构建词云defgetTitle(self):collection=self.zfdb["rent"]queryArgs={}projectionFields={'_id':False,'title':True}#用字典指定需要的字段searchRes=collection.find(queryArgs,projection=projectionFields).limit(1000)content=''forresultinsearchRes:print(result["title"])content+=result["title"]returncontent#获取用户类型数据(例如:3室2厅)defgetRooms(self):results=self.zfdb.rent.aggregate([{'$group':{'_id':'$rooms','weight':{'$sum':1}}}])roomList=[]weightList=[]forresultinresults:roomList.append(result["_id"])weightList.append(result["weight"])#print(list(result))return(roomList,weightList)#获取租赁面积defgetAcreage(self):results0_30=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':0,'$lte':30}}},{'$group':{'_id':'','count':{'$sum':1}}}])results30_60=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':30,'$lte':60}}},{'$group':{'_id':'','count':{'$sum':1}}}])results60_90=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':60,'$lte':90}}},{'$group':{'_id':'','count':{'$sum':1}}}])results90_120=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':90,'$lte':120}}},{'$group':{'_id':'','count':{'$sum':1}}}])results120_200=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':120,'$lte':200}}},{'$group':{'_id':'','count':{'$sum':1}}}])results200_300=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':200,'$lte':300}}},{'$group':{'_id':'','count':{'$sum':1}}}])results300_400=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':300,'$lte':400}}},{'$group':{'_id':'','count':{'$sum':1}}}])results400_10000=self.zfdb.rent.aggregate([{'$match':{'area':{'$gt':300,'$lte':10000}}},{'$group':{'_id':'','count':{'$sum':1}}}])results0_30_=list(results0_30)[0]["count"]results30_60_=list(results30_60)[0]["count"]results60_90_=list(results60_90)[0]["count"]results90_120_=列表(结果90_120)[0][“计数”]结果120_200_=列表(结果120_200)[0][“计数”]结果200_300_=列表(结果200_300)[0][“计数”]结果300_400_=列表(结果300_400)[0][“计数”]results400_10000_=列表(results400_10000)[0]["count"]attr=["0-30平方米","30-60平方米","60-90平方米","90-120平方米","120-200平方米米米","200-300平方米","300-400平方米","400+平方米"]value=[results0_30_,results30_60_,results60_90_,results90_120_,results120_200_,results200_300_,results300_400_,results400display:00data_atts]00#显示饼图defshowPie(self,title,attr,value):frompyechartsimportPiepie=Pie(title)pie.add("aa",attr,value,is_label_show=True)pie.render()#显示矩形树图defshowTreeMap(self,title,data):frompyechartsimportTreeMapdata=datatreemap=TreeMap(title,width=1200,height=600)treemap.add("深圳",data,is_label_show=True,label_pos='inside',label_text_size=19)treemap.render()#显示柱状图defshowLine(self,title,attr,value):frompyechartsimportBarbar=Bar(title)bar.add("深圳",attr,value,is_convert=False,is_label_show=True,label_text_size=18,is_random=True,#xaxis_interval=0,xaxis_label_textsize=9,legend_text_size=18,label_text_color=["#000"])bar.render()#显示词云defshowWorkCloud(self,content,image_filename,font_filename,out_filename):d=path.dirname(__name__)#content=open(path.join(d,filename),'rb').read()#基于TF-IDF算法的关键词提取,topK返回出现频率最高的几个item,默认值为20,withWeight#是否返回权重keywordtags=jieba.analyse.extract_tags(content,topK=100,withWeight=False)text="".join(tags)#要显示的背景图片img=imread(path.join(d,image_filename))#指定中文font,否则会乱码wc=WordCloud(font_path=font_filename,background_color='black',#wordcloudshape,mask=img,#allow***vocabularymax_words=400,#***font,如果不指定,会是Imageheightmax_font_size=100,#Canvaswidthandheight,如果设置了msak,则不会生效#width=600,#height=400,margin=2,#文字水平放置的频率,默认为0.9,即垂直placement频率为0.1prefer_horizo??ntal=0.9)wc.generate(text)img_color=ImageColorGenerator(img)plt.imshow(wc.recolor(color_func=img_color))plt.axis("off")plt.show()wc.to_file(path.join(d,out_filename))#显示pyecharts的词云defshowPyechartsWordCloud(self,attr,value):frompyechartssimportWordCloudwordcloud=WordCloud(width=1300,height=620)wordcloud.add("",attr,value,word_size_range=[20,100])wordcloud.render()后记距离上次分析租房市场已经3、4个月了,我的技术水平也一直improved一定的改进,所以hardcoding是成长的捷径。***,应对外部条件的变化,我们还是要提升自己的硬实力,这样才能提高自己的生存能力。