<机器学习 房价预测 >对贝壳租房网 信息爬取 及处理。

数据爬取

本篇博客所讲为数据爬取,及处理。

  • 爬取基本数据
  • 对数据进行处理
  • 将数据量化,即数字化
  • 利用机器学习模型做出预测

贝壳租房网西安
我们需要从这里爬取数据,理由是相较于其他租房网站来说,该网站反爬没有那么严格,虽然前一段时间好像加强了反爬但是,还是突破还是比较轻松的。
这里我们需要的关键信息为

header=['房源编号', '所在城市', '区县', '所在街道', '小区名称', '面积', '租赁方式', '朝向', '月租', '计费', '室', '厅', '卫', '入住', '租期', '看房', '所在楼层', '总楼层', '电梯', '车位', '用水', '用电', '燃气', '采暖']

我们以爬取五十页的数据并存入csv文件或者excel文件中,存入什么文件不是特别重要我们最后需要的是一个dataframe结构,也就是机器学习需要的结构,同时后面我们会将该结构转化为numpy.array。
先上代码:

#kaggle 房价预测一:爬取信息
#两个模块用于处理信息
import csv
import re
#该模块用于爬取信息
from bs4 import BeautifulSoup
import requests
import time
#csv文件的标题行
header=['房源编号', '所在城市', '区县', '所在街道', '小区名称', '面积', '租赁方式', '朝向', '月租', '计费', '室', '厅',
        '卫', '入住', '租期', '看房', '所在楼层', '总楼层', '电梯', '车位', '用水', '用电', '燃气', '采暖']
#获得头部信息
headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0'}
with open("E:\\桌面\\kaggle房价预测.csv","w",newline="") as filewrite:
#创建写入对象
    filewriter=csv.writer(filewrite)
#写入标题行
    filewriter.writerow(header)
#https://xa.zu.ke.com/zufang/pg2/#contentList ←第二页的网址  https://xa.zu.ke.com/zufang ←第一页的网址 这里可以看出来规律  
    for page in range(1,51):
#url的规则
        url='https://xa.zu.ke.com/zufang/pg'+str(page)+'/#contentList'
#获得response
        response=requests.get(url=url,headers=headers)
#验证网页是否请求正常        
        if response.status_code==200 :
            pass
        else:
            print("该网页:"+str(url)+"未请求成功!")
            continue
#获得 .text 属性
        page_text = response.text
#利用BeautifulSoup的html.parser对属性进行解析 
        soup=BeautifulSoup(page_text,"html.parser")
        div_list=soup.find_all(class_='content__list--item')
#房源编号 更新codes
        codes=[]
        for div in div_list:
            code=re.search(r'data-house_code="(.*?)" ', str(div)).group()[17:-2]
            codes.append(code)
#储存地区
        areas=[]
        p_list=soup.find_all(class_="content__list--item--des")
        for p in p_list:
            area=[]
            a_list=p.find_all('a')
            for a in a_list:
                a_text=a.text
                if a_text==' ' or a_text=='':
                    a_text="暂无"
                area.append(a_text)
            areas.append(area)
        for i in range(len(codes)):
            time.sleep(1)
#更新message        
            message=[]
            message.extend([codes[i],'西安']+areas[i])
#利用code去进入每个详细页面的url 如:https://xa.zu.ke.com/zufang/XA2565970386130829312.html?nav=0&unique_id=ccca3468-0ef2-43fc-8ea0-0a41b42e0720zufangpg21598277997500
            url='https://xa.zu.ke.com/zufang/'+str(codes[i])+'.html'
#获得page_text
            page_text=requests.get(url=url,headers=headers).text
            soup=BeautifulSoup(page_text,'html.parser')
            try:
                ul_text=soup.find('ul',class_="content__aside__list").text
            except:
                print(message[0]+"该页面进入失败!请检查!")
                continue
#这里对正则的基本功有一定要求,比如,这里我们匹配面积的时候要注意 是在空格和㎡之间,因此空格不能少,以下的\n同理。
#同时我们要使用group截断我们匹配到的字符串防止其他莫名的信息
#获得面积
            s=re.search(r' (.*?)㎡',ul_text).group()[1:]
#租赁方式
            lease=re.search(r'租赁方式:(.*?)\n',ul_text).group()[5:-1]
#朝向
            aspect = re.search(r'朝向楼层:(.*?) ', ul_text).group()[5:-1]
#这里我是按照需要的信息顺序匹配
#月租 
            div_text=soup.find('div',class_="content__aside--title").text
            monthly_rent=re.search(r'([0-9]*?)元/月',div_text).group()[:-3]
#计费方式
            try:
                charge_way=re.search(r'\((.*?)\)',div_text).group()[1:-1]
            except:
                charge_way="暂无"
#几室几厅几卫
            rooms=re.search(r'([0-9]*?)室',ul_text).group()[:-1]
            halls=re.search(r'([0-9*?])厅', ul_text).group()[:-1]
            toilets=re.search(r'([0-9*?])卫', ul_text).group()[:-1]
#其他信息            
            div_text=soup.find('div',class_='content__article__info').text
#入住
            check_in=re.search(r'入住:(.*?)\n', div_text).group()[3:-1]
#租期
            lease_day=re.search(r'租期:(.*?)\n',div_text).group()[3:-1]
#看房
            see_room=re.search(r'看房:(.*?)\n',div_text).group()[3:-1]
#所在楼层
            in_floor=re.search(r'楼层:(.*?)/',div_text).group()[3:-1]
#总楼层
            all_floors=re.search(r'/(.*?)\n',div_text).group()[1:-1]
#电梯
            elevator=re.search(r'电梯:(.*?)\n',div_text).group()[3:-1]
#车位
            stall = re.search(r'车位:(.*?)\n', div_text).group()[3:-1]
#用水
            water=re.search(r'用水:(.*?)\n',div_text).group()[3:-1]
#用电
            elect=re.search(r'用电:(.*?)\n',div_text).group()[3:-1]
#燃气
            gas=re.search(r'燃气:(.*?)\n',div_text).group()[3:-1]
#采暖
            heating=re.search(r'采暖:(.*?)\n',div_text).group()[3:-1]
#填入message
            message.extend([s,lease,aspect,monthly_rent,charge_way,rooms,halls,toilets,check_in,lease_day,see_room,in_floor,all_floors,elevator,stall,water,elect,gas,heating])
#检查数据           
            try:
                filewriter.writerow(message)
            except:
                print(message[0]+"写入失败,请检查!")
                continue

由于我们需要的信息在架构中的位置各有不同,因此代码会显得冗杂,我尽量让它显得赏心悦目吧。
我们来看一下爬取信息的大概位置。
在这里插入图片描述在这里插入图片描述
该页面完整的捕捉到这些信息之后,我们进入详细页面获得详细的信息。注意这里每个详细页面的网址有固定的格式为:
https://xa.zu.ke.com/zufang/+str(codes[i])+.html
这也是我们为什么首先要爬取房源编号,并存入一个列表的原因,这样做有利于我们访问每个详细页面。
在这里插入图片描述这里是租价,及交付方式。
在这里插入图片描述
这里是面积,朝向等详细信息,对应页面的这里
在这里插入图片描述
代码修改前遇到的问题:

  1. 出现了需要人机验证的问题,这说明你快速的访问触及了人的反爬机制。最好的处理方法是每一个页面的访问底下加一个time.sleep()该方法已经帮我避过了很多次反爬。
  2. 部分数据错误导致无法截取,这个问题,我用try except处理掉了一两条,大部分都不会有什么问题,这类问题应该是前端工程师的锅。
  3. 该代码目前在西安租房这块是没有问题的,但是html他不是静态不变的,可能更新某个数据之后会出现问题,但是稍微去修改就可以了。
数据处理:

爬取数据之后,他还存在一些比较无用的单位,比如房租后面的单位,或者说楼层后面的单位,还有在楼层位置那块我们只需要三个数字表示高低中楼层就可以了,不需要那些多少楼的数据
,这些数据都需要处理,因此我们进行信息处理。
代码:

#kaggle房价预测二:处理信息
import csv
import re
flag_list=["高楼层","中楼层",'低楼层']
with open("E:\\桌面\\kaggle房价预测.csv",'r',newline='') as fileread:
    with open('E:\\桌面\\数据数据化一.csv','w',newline='') as filewrite:
#创建读取对象 及 写入对象
        filereader=csv.reader(fileread)
        filewriter=csv.writer(filewrite)
#略过第一行
        header=next(filereader)
#写入头部
        filewriter.writerow(header)
        for row_list in filereader:
#去掉㎡
            row_list[5]=row_list[5][:-1]
#朝向去掉后面的保留第一个 原先的朝向若有两者以上 以/隔开
            row_list[7]=row_list[7].split('/')[0]
#入住时间处理  利用空行替换/
            row_list[13]=row_list[13].replace('-','')
#先处理总层数
            row_list[17]=row_list[17].replace('层','')
#对所在楼层处理为 低中高楼层
            if row_list[16]=='地下室':
                    row_list[16]='低楼层'
            elif row_list[16] not in flag_list :
                if int(row_list[16]) <= int(row_list[17])/3:
                    row_list[16]=flag_list[2]
                elif int(row_list[17]) / 3 < int(row_list[16]) < int(row_list[17]) / 3 * 2:
                    row_list[16]=flag_list[1]
                else:
                    row_list[16]=flag_list[0]
#利用pop删除索引0 也就是房源编号
            row_list.pop(0)
            filewriter.writerow(row_list)
#第一步处理完成

这里处理完成后,大概不会有什么问题,我们将数据数字化。

#kaggle房价预测二:信息数据化
#这里对对应地区进行编号,因为我们需要的机器学习模型是不能识别字符串的。
import csv
import re
#为了地区的编号可以设置一个编号函数实现自动编号 三个地区需要编号,我们需要设置三个列表和三个整数完成函数的使用
def Automatic_numbering(element,label_list,label):
    '''
    举例 假设我们填入的三个地区为 a b a
    那么a先进去 ,设置label初始值为1
    第一返回1 2 这是label为2
    再进去b 这时返回2 3,label为3 在填入a 这是返回1 3 label依然为3,因为a与a重名不需要重新赋值
    '''
    if element not in label_list:
        label_list.append(element)
        ret=label
        label+=1
    else:
        ret = label_list.index(element) + 1
    return ret,label
#设置初始的三个列表和整数
label_list_a,label_list_b,label_list_c=[],[],[]
label_a=label_b=label_c=1
#这里的all_days得用在后面处理特殊情况
all_days=0
all_rows=[]
with open("E:\\桌面\\数据数据化一.csv",'r',newline="") as fileread:
    with open("E:\\桌面\\数据编码二.csv",'w',newline="") as filewrite:
#创建读写对象
        filereader=csv.reader(fileread)
        filewriter=csv.writer(filewrite)
        header=next(filereader)
#将头部写入
        filewriter.writerow(header)
        for row_list in filereader:
#这里我利用西安的数据 西安的编码记为3
            row_list[0]=3
#对区县位的编码
            row_list[1],label_a=Automatic_numbering(row_list[1], label_list_a, label_a)
            row_list[2],label_b=Automatic_numbering(row_list[2], label_list_b, label_b)
            row_list[3],label_c=Automatic_numbering(row_list[3], label_list_c, label_c)
#对租赁方式 整租为 1 合租为 2
            if row_list[5]=='整租':
                row_list[5]=1
            else:
                row_list[5]=2
#需要一个总的字典,包含各种编码
            all_dic={'东': 1, '南': 2, '西': 3, '北': 4, '东南': 5, '东北': 6, '西南': 7, '西北': 8,'未知':0,
                     '月付价': 1, '季付价': 2, '半年付价': 3, '年付价': 4,'双月付价':5,'None': 0,'暂无':0,
                    '随时入住': 1,
                    '随时可看': 1, '需提前预约': 2, '一般下班后可看': 3,'只能周末看房':4,
                    '低楼层': 1, '中楼层': 2, '高楼层': 3,
                    '有': 1, '无': 2, '暂无数据': 3,
                    '暂无数据': 1, '免费使用': 2, '租用车位': 3,
                    '民水': 1, '商水': 2, '暂无数据': 3,
                    '民电': 1, '商电': 2, '暂无数据': 3,
                    '有': 1, '无': 2, '暂无数据': 3,
                    '集中供暖': 1, '自采暖': 2, '暂无数据': 3}
#对朝向的编码 
            row_list[6]=all_dic[row_list[6]]
#计费方式 
            row_list[8]=all_dic[row_list[8]]
#入住
            if row_list[12]=="随时入住":
                row_list[12]=1
            elif row_list[12]=='暂无数据':
                row_list[12]=0
#租期 利用sub函数将无用项全部替换为空格。用split分开,这个思路应该是我前一段时间看到我一个朋友的代码是这么写的,觉得挺好,借鉴的
            if row_list[13] != '暂无数据':
                if re.search(r'年', row_list[13]):
                    day= int(re.sub(r'(\D)', ' ', row_list[13]).split()[0])*12
                elif re.search(r'月', row_list[13]):
                    day= int(re.sub(r'(\D)', ' ', row_list[13]).split()[0])
                row_list[13]=day
                all_days+=day
#看房
            row_list[14]=all_dic[row_list[14]]
#所在楼层            
            row_list[15]=all_dic[row_list[15]]
#电梯
            row_list[17]=all_dic[row_list[17]]
#车位
            row_list[18]=all_dic[row_list[18]]
#用水
            row_list[19]=all_dic[row_list[19]]
#用电
            row_list[20]=all_dic[row_list[20]]
#燃气
            row_list[21]=all_dic[row_list[21]]
#采暖
            row_list[22]=all_dic[row_list[22]]
            all_rows.append(row_list)
        for row in all_rows:
            if row[13]=='暂无数据':
                row[13]=all_days//1503
            filewriter.writerow(row)

对信息数据化。

  1. 地区,小区名等等,我们需要对它进行编号,而我们是不可能手动编号的,因此我们用一个自动编号的函数,函数的原理在代码中已经详解,很容易理解。
  2. 对于部分缺失的数据,我们利用众数或者平均数替代,使误差最小化。
  3. 提前设置一个大的字典,对应各个字符串,这是我能想到最方便的方法,每一次赋值访问字符串便可。
  4. 租期我们按最小租期去算。

1.
代码仅用于对西安租房数据的爬取及处理,若是换做其他城市可能多多少少会有一些报错,不过没关心,应该都只是小问题,我们最后处理出来的数据一定要是纯数字,否则我们需要的机器学习模型无法运行。
2.
预测房价需要大量的数据,因此不能仅靠一个城市的租房数据去判断,可以改代码去爬取成都,天津等等地方的数据,整合之后利用机器学习模型去预测会更加准确,甚至可以准确到契合度为百分之九十九点九,数据就是这么可怕。

这边我放一个模型吧,不太想详解,可以的话,放到下一篇博客吧。
#放入机器学习模型 
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
#k邻近模型
from sklearn.neighbors import KNeighborsRegressor
#加载数据
data=pd.read_csv('E:\\桌面\\数据编码二.csv', encoding='gb18030')
#利用train_test_split分离测试集与数据集 这里注意drop函数返回一个已经取消了列的dataframe
X_train,X_test,y_train,y_test = train_test_split(np.array(data.drop(columns=['月租'])), np.array(data['月租']),
                                                    test_size=0.2, random_state=22)
#构建模型
knn_mode=KNeighborsRegressor(n_neighbors=5)
knn_mode.fit(X_train,y_train)
y_predict= knn_mode.predict(X_test)
print(y_predict)
print('the score is {0:.2f}'.format(knn_mode.score(X_train, y_train)))
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值