链家网租房信息数据分析——从爬虫到房租预测

前言

只身一人来到异地工作,首当其冲考虑的是住宿问题。如果公司有提供住宿当然最好,但大多时候还是需要我们自己去租房子。可是作为人生地不熟的异乡人,往往对当地租房市场难以有清晰的洞见,也担心自己被房东或中介当了猪宰。现在你可以稍安勿躁了,本文将运用python爬虫sklearn包,以及tableau可视化工具,从数据获取到最终的结果呈现,对当地的租房价格做深入浅出的分析和预测,助你找到称心如意的房子。同时也是我自己数据分析学习的一个小结。

数据爬取

思路

由于链家网的反爬手段比较温和,只要不太过分地爬取,一般不会被ban(本文写作阶段博主还没被其ban过),我们选择链家网进行租房信息的爬取。

由于博主现住上海,就以上海的信息为例。这是上海的网址链接:
https://sh.lianjia.com/zufang/
如果我们切换成其他城市,比如北京,html会变成:
https://bj.lianjia.com/zufang/
可以发现html只有很小的改变,由sh变成了bj,也就是城市的拼音缩写

继续观察,不出意外,我们应该位于链家租房页面的第一页,而拉到底后会发现,一共有100页,那么我们随便点一页,看看网址的变化。
没错,网址变成了:
https://sh.lianjia.com/zufang/pg{你点击的页数}/#contentList
我们将末尾的#contentList去掉后,网址依然可以使用
同理,https://sh.lianjia.com/zufang/pg1/ 也能进入我们最开始的首页
那么我们可以得知,链家租房网页的html结构基本就是这样:
https://{city}.lianjia.com/zufang/pg{number}/

但是还有一个很严重的问题,相信细心的同学已经发现了,链家租房信息一共只有100页,而每页的信息只有30条,一共也就是3000条。而上海的租房信息首页上却显示一共有20000+条。那么怎么把这20000多条全部爬下来呢。这时候我们看看首页的上面还有什么,没错,区域!。对区域加以限制后可以发现,各个区域的租房信息依然最多能有100页信息,也就是3000条。而只有浦东区是超过3000条的,竟然达到了6890条!看来近几年浦东的发展势头很好呀。

结合起来,包含区域的网址结构则为:
https://{city}.lianjia.com/zufang/{region}/pg{number}/
所以我们可以依不同区域,将各区域的100页网址信息全部爬取下来,再对每一页网址信息进行处理,将每一页网址30条信息(if reaching)的网址爬下来。
对于超过3000条的浦东区,可以再进一步,同按区域爬取的思路一样,按租金进行爬取。这样不同租金的网址结构就变成了:
https://{city}.lianjia.com/zufang/{region}/rp{number}/pg{number}/

获取到所有网址信息后,在进入每一个网址,获取该住房的详细信息。好了,废话不多说,show u my code.

爬虫代码

这里只用了requests和BeautifulSoup库,而且是简单的单线程爬取,跟博主类似的新手应该也能看懂。

# -*- coding: UTF-8 -*-

# 导入需要的各种库
import re
import time
import requests
from random import uniform
from bs4 import BeautifulSoup
import pandas as pd
from sqlalchemy import create_engine
import pymysql


# 设置一个请求头,不然无法通过链家的验证
headers = {
   'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'}


def get_parent_url(city):
    """
    获取选定城市的所有父母网址
    :param city:
    :return:
    """
    url = 'http://{city}.lianjia.com/zufang'.format(city=city)
    html = requests.get(url, headers=headers)                                           # 获取网址html
    Soup = BeautifulSoup(html.text, 'lxml')                                             # 解析html
    Selector = Soup.select('ul[data-target="area"] > li.filter__item--level2')          # 找出html中的区域文本
    Selector = Selector[1:]                                                             # 排除第一个区域“不限”
    url_parent_all = []                                                                 # 初始化最终的父母网址列表

    for i in Selector:                                                                  # 对每一个区域进行loop
        url_region = "https://sh.lianjia.com" + i.select('a')[0]['href']                # 找出区域网址
        html_region = requests.get(url_region, headers=headers)                         # 获取区域网址html
        Soup_region = BeautifulSoup(html_region.text, 'lxml')                           # 解析html
        number_data = int(Soup_region.select('span.content__title--hl')[0].text)        # 获取该区域信息条数
        if number_data <= 3000:                                                         # 信息条数少于3000直接开始爬取
            index = Soup_region.select('div.content__pg')[0]                            # 找出页数文本
            index = str(index)                                                          # 将bs4对象转换为str,否则无法进行正则提取
            re_set = re.compile(r'data-totalpage="(.*?)"')
            index = re.findall(re_set, index)[0]                                        # 正则表达式提取出页数
            for j in range(1, int(index)+1):                                            # 对每一页网址进行循环
                url_parent = url_region + "pg{}".format(j)
                url_parent_all.append(url_parent)                                       # 得到该区域每一页的网址,并添加至父母网址列表中
                print(url_parent)
            t = uniform(0, 1)
            time.sleep(t)                                                               # 每爬一个区域,稍作等待,避免被ban
        else:                                                                           # 信息条数大于3000按租金分层
            for i in range(1, 8):
                url_region_rp = url_region + "rp{}/".format(i)
                html_region_rp = requests.get(url_region_rp, headers=headers)
                Soup_region_rp = BeautifulSoup(html_region_rp.text, 'lxml')
                index = Soup_region_rp.select('div.content__pg')[0]                     # 操作同上
                index = str(index)
                re_set = re.compile(r'data-totalpage="(.*?)"')
                index = re.findall(re_set, index)[0]
                for j in range(1, int(index) + 1):
                    url_parent = url_region + "rp{}/".format(i) + "pg{}".format(j)
                    url_parent_all.append(url_parent)
                    print(url_parent)
            t = uniform(0, 1)
            time.sleep(t)
    return url_parent_all


def get_detail_url(url_parent_all):
    """
    对每一个父母网址进行操作,获取最终详尽的子网址列表
    :param city:
    :return:
    """
    url_detail_all = []                                                                 # 创建最终的子网址列表

    for url in url_parent_all:                                                          # 对每一个父母网址进行for loop
        html = requests.get(url, headers=headers)
        Soup = BeautifulSoup(html.text, 'lxml')
        Selector = Soup.select('div a.content__list--item--aside')                      # 解析并找出子网址bs4对象
        for i in Selector:
            i = i['href']
            i = 'http://{city}.lianjia.com'.format(city=city) + i                       # 对每一个bs4子网址对象循环,构建最终的子网址
            url_detail_all.append(i)                                                    # 添加到子网址列表
            print(i)
        t = uniform(0, 0.01)
        time.sleep(t)                                                                   # 每处理一条父母网址暂停t秒
    return url_detail_all


def get_data(url_detail_all):
    """
    从子网址列表中网址获取数据
    :param url_detail_all:
    :return:
    """
    data = []                                                                           # 初始化一个爬虫数据列表
    num_error = 0                                                                       # 记录错误数

    for i in url_detail_all:                                                            # for loop对每一个网址进行爬取操作
        try:                                                                            # 使用try...except...方法防止循环中途出错退出
            info = {
   }
            url = i
            html = requests.get(url, headers=headers)
            Soup = BeautifulSoup(html.text, 'lxml')
            info['房源编号'] = Soup.select('i.house_code')[0].text
            info['链接'] = i
            info['标题'] = Soup.select('p.content__title')[0].text
            info['价格'] = Soup.select('p.content__aside--title')[0].text
            Selector1 = Soup.select('p.content__article__table')[0].text.split('\n')
            info['租赁方式'] = Selector1[1]
            info['户型'] = Selector1[2]
            info['面积'] = Selector1[3]
            info['朝向'] = Selector1[4]
            info['发布时间'] = Soup.select('li[class^="fl oneline"]')[1].text[3:]
            info['入住'] = Soup.select('li[class^="fl oneline"]')[2].text[3:]
            info['租期'] = Soup.select('li[class^="fl oneline"]')[4].text[3:]
            info['看房'
  • 20
    点赞
  • 183
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值