使用Java、Excel資料分析如何有效投資房產

程式猜想發表於2018-01-28

寫在前面的話

2017年IT業中多起猝死、自殺,創業的九死一生,各大公司清算、裁員等。年底也總是充斥著各種活動刷屏,年終總結、賬單等。30歲的程式猿什麼的,不幸我們可能也是其中之一。要改變現狀,離不開錢與投資,提到投資就不得不提房。過去數十年的房價顛覆了努力工作改變生活的思維。作為程式猿比他人多的優勢在於可以利用網際網路資料進行資料分析決策,接下來看看基於鏈家中重慶渝北區人和二手房及國家全國商品房銷售面積統計的簡單資料分析

資料收集

鏈家公共資料

鏈家的url很標準,我們要獲取的資料如下【人和二手房_重慶人和二手房出售】(重慶鏈家網),2018年1月27日資料為472條,共16頁,url格式為
https://cq.lianjia.com/ershoufang/renhe/pg1/
...
https://cq.lianjia.com/ershoufang/renhe/pg16/

Java Jsoup爬蟲

現在全職Java,且為了快速實現採用Jsoup獲取Html資料,程式碼非常簡單

Document document = Jsoup.connect(domain + uri)
   .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
   .timeout(30000).get();
複製程式碼
儲存Html資料至本地檔案

厚道的鏈家並未做防爬蟲,也有可能是為了對搜尋引擎友好,提高曝光率。資料獲取後要儲存至本地檔案就簡單了,合併後程式碼如下

  public String saveHtml(String domain, String uri) {
    FileOutputStream out = null;
    File dest = new File("src/temp_html/" + (uri.endsWith("/") ? uri.substring(0, uri.length() - 1) : uri) + ".html");
    try {
      Document document = Jsoup.connect(domain + uri)
          .userAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
          .timeout(30000).get();

      if (!dest.getParentFile().exists()) {
        dest.getParentFile().mkdirs();
      }

      if (!dest.exists()) {
        dest.createNewFile();
      }
      out = new FileOutputStream(dest, false);
      out.write(document.toString().getBytes("UTF-8"));
      out.close();


    } catch (IOException e) {
      e.printStackTrace();
      return null;
    } finally {
      IOUtils.closeQuietly(out);
    }

    return dest.getPath();
  }
複製程式碼
通過Jsoup解析資料至Excel

再將html中資料解析使用POI寫入Excel就相對簡單了,主要在於規則匹配

解析資料
public List<House> parseHtml(String fileName) {
    List<House> houses = null;
    //讀取本地html的路徑
    File file = new File(fileName);
    //寫個迴圈讀取這些檔案的名字
    try {
      if (file.isFile()) {
        logger.info("檔案{}開始解析", fileName);

        //下面開始解析本地的html
        Document doc = Jsoup.parse(file, "UTF-8");
        Elements sellListContents = doc.select("ul.sellListContent");
        if (!ObjectUtils.isEmpty(sellListContents)) {
          Element sellListContent = sellListContents.first();
          Elements sellContents = sellListContent.select("li.clear");
          if (!ObjectUtils.isEmpty(sellListContents)) {
            houses = new ArrayList<>(sellContents.size());
            for (int i = 0; i < sellContents.size(); i++) {
              Element sellList = sellContents.get(i);
              Elements sellElements = sellList.select("div.clear");
              if (null != sellElements && sellElements.size() == 1) {
                House house = new House();
                Element infoElement = sellElements.first();

                Element titleElement = infoElement.select("div.title").first().select("a[href]").first();
                house.setTitle(titleElement.text());
                house.setUrl(titleElement.attr("href"));

                Element houseInfoElement = infoElement.selectFirst("div.houseInfo");
                house.setVillage(houseInfoElement.select("a").first().text());
                String houseInfo = houseInfoElement.text();
                String[] houseInfos = houseInfo.split("\\|");

                for (int j = 1; j < houseInfos.length; j++
                    ) {
                  switch (j) {
                    case 1:
                      house.setStructure(houseInfos[j].trim());
                      break;
                    case 2:
                      house.setAcreage(houseInfos[j].replace("平米", "").trim());
                      break;
                    case 3:
                      house.setOrientation(houseInfos[j].trim());
                      break;
                    case 4:
                      house.setDecoration(houseInfos[j].trim());
                      break;
                    case 5:
                      house.setElevator(houseInfos[j].trim());
                      break;
                  }
                }
                Element positionInfoElement = infoElement.selectFirst("div.positionInfo");
                house.setRegion(positionInfoElement.select("a").first().text());
                String position = positionInfoElement.text().split(house.getRegion())[0].trim();

                house
                    .setPosition(position.lastIndexOf("-") == position.length() - 1 ? position.substring(0, position.length() - 1).trim() : position);

                house.setTag(infoElement.selectFirst("div.tag").text());

                Element priceInfoElement = infoElement.selectFirst("div.priceInfo");
                house.setTotalPrice(priceInfoElement.selectFirst("div.totalPrice").text().replace("萬", ""));

                house.setUnitPrice(priceInfoElement.selectFirst("div.unitPrice").text().replace("單價", "").replace("元/平米", "").trim());

                logger.info("解析第{}個元素,結果為:{}", i, house.toString());
                houses.add(house);
              }
            }
          }
        }
      }
    } catch (Exception e) {
      logger.error("檔案{}解析錯誤", fileName, e);
    }
    return houses;
  }
複製程式碼
寫入Excel
public String writeExcel(String fileName, List<House> houses) {
    logger.info("檔案{}開始寫入", fileName);
    try (POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(fileName))) {
      Workbook workbook = new HSSFWorkbook(fs);
      HSSFSheet sheet = (HSSFSheet) workbook.getSheetAt(0);
      final int preLastRowNum = sheet.getLastRowNum();
      logger.info("檔案{},上次行數為{}", fileName, preLastRowNum);
      for (int i = 0; i < houses.size(); i++) {
        Row row = sheet.createRow(preLastRowNum + i + 1);
        House house = houses.get(i);
        row.createCell(0).setCellValue(preLastRowNum + i + 1);
        row.createCell(1).setCellValue(house.getTitle());
        row.createCell(2).setCellValue(house.getTotalPrice());
        row.createCell(3).setCellValue(house.getUnitPrice());
        row.createCell(4).setCellValue(house.getVillage());
        row.createCell(5).setCellValue(house.getStructure());
        row.createCell(6).setCellValue(house.getAcreage());
        row.createCell(7).setCellValue(house.getOrientation());
        row.createCell(8).setCellValue(house.getDecoration());
        row.createCell(9).setCellValue(house.getElevator());
        row.createCell(10).setCellValue(house.getPosition());
        row.createCell(11).setCellValue(house.getRegion());
        row.createCell(12).setCellValue(house.getTag());
        row.createCell(13).setCellValue(house.getUrl());
      }
      FileOutputStream fileOut = new FileOutputStream(fileName);
      workbook.write(fileOut);
      IOUtils.closeQuietly(fileOut);
      logger.info("檔案{}寫入完成,{}-{}行", fileName, preLastRowNum + 1, preLastRowNum + houses.size());
    } catch (IOException e) {
      logger.error("excel-{}處理出錯", fileName, e);
    }

    return fileName;
  }
複製程式碼

資料處理

寫入後核對資料沒什麼問題,然後需要對資料進行簡單處理。

刪除車位、別墅

觀察發現資料中有車位、別墅存在,不在我們投資範圍,需要從類別中篩選出並刪除

重複資料

小區名稱中發現有如棕櫚泉一期、棕櫚泉二期、棕櫚泉三期字樣,我們此次的初步分析暫時不需要這樣統計,在後期具體決策時再才需要參考某期是否離輕軌、學校更近,車位比更高等。所以也需要通過函式全部替換成“棕櫚泉”。

資料分析展現

資料分析在多年拉卡拉、中匯支付風控、合規經驗的老婆手把手教導下完成,大量運用透檢視

小區平均單價分析

初步資料處理後,不會再有總價500w這種不忍直視的內容,此時我們需要來個直觀的小區平均單價柱狀圖衝擊一下

小區平均單價柱狀圖

小區平均單價

型別 平均單價(元) 小區
最高 20,000 比畢得豪園
最低 6,480 人和家園
平均 14,151.46

當然這個價格並不準確,其中有房齡、交通、裝修、是否帶學校指標等其它因素,但是至少可以讓我們跨出第一步讓自己對市場價格有一定的認知,計算自己可承受的價位

房源數量分析

接下來看看房源多的一些小區,經過我們之前處理過後不分幾期幾期後只取前20,分佈如下

房源前20小區餅狀圖

棕櫚泉的樓盤本身大房子數量多,且含有別墅洋房及高層,出售最多,佔比27.32%力奪第二的9.02%。想必其中很多都是投資來的,如果分析清水與裝修過的比例、持有房子年限等應該是可以分析出來

戶型佔比分析

作為投資戶型極其重要,市場上哪些戶型受歡迎,新樓盤也一樣會對人群分析,對樓盤人群定位。

戶型餅狀圖

3室2廳大比分奪得冠軍,4室2廳為第二齣乎意料,應該具體分析這些房子的出處是否為棕櫚泉的洋房。除去4室2廳外,3室2廳和2室2廳都在我們接受的範圍,像我們沒有多少財力的年輕人很多第一套房會選擇2室2廳,夠自己和孩子住,但是如果加入父母就不夠住了,多半會賣掉換一個3室2廳。
由此可以得出結論,如果目標為這些改善型的人群,3室2廳是最佳選擇
如果財力有限可以可以買2室2廳
如果錢更少是否需要買個1室來投資出租,就該考慮地段及租金的關聯關係了。

全國商品房銷售面積分析

2月3日對全國商品房銷售面積抓取,製作簡單折線圖

全國銷售面積

重慶銷售面積

其實還有很多可分析,想必房子的熱門程度和價格是有一定關係,多久可以收回成本等。程式猿總是想太多,一個簡單需求整成了一個價格監控、預測系統。所以本文只做簡單分析,程式碼也很簡單,2小時左右。

原始碼

Github:house

轉載請標明出處:
http://2tu.github.io/2018/01/27/data-analysis-house-price/
本文出自Tu's blog

相關文章