专栏首页青笔原创python 命令行抓取分析北上广深房价数据

python 命令行抓取分析北上广深房价数据

引言

昨天在老家,发布了一篇《python 自动抓取分析房价数据——安居客版》。在文末,第6小节提供了完整代码,可以在 python3 环境,通过命令行传入参数 cookie 自动抓取房价数据。今天回到深圳,才想到,这段脚本只能抓取西双版纳的房价数据,如果读者不自己修改,那么就无法抓取其他城市的房价数据。于是,决定“好事做到底,送佛送到西”,将脚本加以修改,以北上广深为例,提供灵活抓取分析其他城市房价的完整代码。

1. 完整 python 脚本

在上一篇的脚本基础上,稍加修改,将以下代码保存到文件 crawl_anjuke.py 中。

#!/usr/local/bin/python

import requests
from bs4 import BeautifulSoup
import pandas as pd
import matplotlib.pyplot as plt
import time
import argparse

def get_headers(city, page, cookie):
    headers = {
        'authority': '{}.anjuke.com'.format(city),
        'method': 'GET',
        'path': '/community/p{}/'.format(page),
        'scheme': 'https',
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'zh-CN,zh;q=0.9',
        'cache-control': 'no-cache',
        'cookie': cookie,
        'pragma': 'no-cache',
        'referer': 'https://{}.anjuke.com/community/p{}/'.format(city, page),
        'sec-fetch-mode': 'navigate',
        'sec-fetch-site': 'none',
        'sec-fetch-user': '?1',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
    }
    return headers

def get_html_by_page(city, page, cookie):
    headers = get_headers(city, page, cookie)
    url = 'https://{}.anjuke.com/community/p{}/'.format(city, page)
    res = requests.get(url, headers=headers)
    if res.status_code != 200:
        print('页面不存在!')
        return None
    return res.text

def extract_data_from_html(html):
    soup = BeautifulSoup(html, features='lxml')
    list_content = soup.find(id="list-content")
    if not list_content:
        print('list-content elemet not found!')
        return None
    items = list_content.find_all('div', class_='li-itemmod')
    if len(items) == 0:
        print('items is empty!')
        return None
    return [extract_data(item) for item in items]

def extract_data(item):
    name = item.find_all('a')[1].text.strip()
    address = item.address.text.strip()
    if item.strong is not None:
        price = item.strong.text.strip()
    else:
        price = None
    finish_date = item.p.text.strip().split(':')[1]
    latitude, longitude = [d.split('=')[1] for d in item.find_all('a')[3].attrs['href'].split('#')[1].split('&')[:2]]
    return name, address, price, finish_date, latitude, longitude

def is_in_notebook():
    import sys
    return 'ipykernel' in sys.modules

def clear_output():
    import os
    os.system('cls' if os.name == 'nt' else 'clear')
    if is_in_notebook():
        from IPython.display import clear_output as clear
        clear()

def crawl_all_page(city, cookie, limit=0):
    page = 1
    data_raw = []
    while True:
        try:
            if limit != 0 and (page-1 == limit):
                break
            html = get_html_by_page(city, page, cookie)
            data_page = extract_data_from_html(html)
            if not data_page:
                break
            data_raw += data_page
            clear_output()
            print('crawling {}th page ...'.format(page))
            page += 1
        except:
            print('maybe cookie expired!')
            break
    print('crawl {} pages in total.'.format(page-1))
    return data_raw

def create_df(data):
    columns = ['name', 'address', 'price', 'finish_date', 'latitude', 'longitude']
    return pd.DataFrame(data, columns=columns)

def clean_data(df):
    df.dropna(subset=['price'], inplace=True)
    df = df.astype({'price': 'float64', 'latitude': 'float64', 'longitude': 'float64'})
    return df

def visual(df):
    fig, ax = plt.subplots()
    df.plot(y='price', ax=ax, bins=20, kind='hist', label='房价频率直方图', legend=False)
    ax.set_title('房价分布直方图')
    ax.set_xlabel('房价')
    ax.set_ylabel('频率')
    plt.grid()
    plt.show()

def run(city, cookie, limit):
    data = crawl_all_page(city, cookie, limit)
    if len(data) == 0:
        print('empty: crawled noting!')
        return
    df = create_df(data)
    df = clean_data(df)
    visual(df)

    _price = df['price']
    _max, _min, _average, _median, _std = _price.max(), _price.min(), _price.mean(), _price.median(), _price.std()
    print('\n{} house price statistics\n-------'.format(city))
    print('count:\t{}'.format(df.shape[0]))
    print('max:\t¥{}\nmin:\t¥{}\naverage:\t¥{}\nmedian:\t¥{}\nstd:\t¥{}\n'.format(_max, _min, round(_average, 1), _median, round(_std, 1)))
    
    df.sort_values('price', inplace=True)
    df.reset_index(drop=True, inplace=True)
    #  保存 csv 文件
    dt = time.strftime("%Y-%m-%d", time.localtime())
    csv_file = 'anjuke_{}_community_price_{}.csv'.format(city, dt)
    df.to_csv(csv_file, index=False)

def get_cli_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--city', type=str, help='city.')
    parser.add_argument('-k', '--cookie', type=str, help='cookie.')
    parser.add_argument('-l', '--limit', type=int, default=0, help='page limit (30 records per page).')
    args = parser.parse_args()
    return args

if __name__ == '__main__':
     args = get_cli_args()
     run(args.city, args.cookie, args.limit)

2. 新增参数说明

2.1 city

顾名思义,city 就是指定脚本将要抓取的城市。这个参数来自哪里,是不是随便传呢?当然不是,因为数据来自网站,因此,就必须是网站支持的城市。在安居客网站,体现为二级域名,如北京站是 beijing.anjuke.com ,那么获取北京站的 city 即为 beijing 。

2.2 limit

抓取最大分页数。之所以需要这个参数,因为抓取城市所有小区的数据,需要分页一次次抓取,通过观察,安居客分页是通过 url 传入的。正常思路,容易想到,从第1页开始,每成功获取1页数据,将页面变量加1, 直到获取不到数据。但是,在抓取深圳数据时,我发现,网站上看到最多只能查看到50页, 如下图所示。但实际,在抓取50页面后面的数据时,会返回 第1页的数据。这样,导致自动累加的策略失效,不能跳出循环。因此,需要增加 limit 参数,来手动指定加载最大的页面数。这个数,需要自己打开对应城市,如下图,找到最大页面数。以深圳为例(https://shenzhen.anjuke.com/community/p50/) ,limit 设置为 50 。

注:cookie 参数和上一篇 《python 自动抓取分析房价数据——安居客版》 一样

3. 命令行抓取北上广深数据

3.1 抓取北京房价数据

python crawl_anjuke.py --city beijing --limit 50 --cookie "sessid=5AACB464..."

3.2 抓取上海房价数据

python crawl_anjuke.py --city shanghai --limit 50 --cookie "sessid=5AACB464..."

3.3 抓取广州房价数据

python crawl_anjuke.py --city guangzhou --limit 50 --cookie "sessid=5AACB464..."

3.4 抓取深圳房价数据

python crawl_anjuke.py --city shenzhen --limit 50 --cookie "sessid=5AACB464..."

4. 数据分析

4.1 加载数据

运行 3 小节命令后,会在当前目录生成如下四个 csv 文件。后面日期为运行命令当天的日期。

  • anjuke_beijing_community_price_2019-09-17.csv
  • anjuke_shanghai_community_price_2019-09-17.csv
  • anjuke_guangzhou_community_price_2019-09-17.csv
  • anjuke_shenzhen_community_price_2019-09-17.csv
import pandas as pd
import time

dt = time.strftime("%Y-%m-%d", time.localtime())
cities = ['beijing', 'shanghai', 'guangzhou', 'shenzhen']
csv_files = ['anjuke_{}_community_price_{}.csv'.format(city, dt) for city in cities]

dfs = []

city_cn = {
    'beijing': '北京',
    'shanghai': '上海',
    'guangzhou': '广州',
    'shenzhen': '深圳'
}

for city in cities:
    f = 'anjuke_{}_community_price_{}.csv'.format(city, dt)
    df = pd.read_csv(f)
    df.insert(0, 'city', city_cn[city])
    dfs.append(df)

df = pd.concat(dfs, ignore_index=True)

df.sample(10)

.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }

city

name

address

price

finish_date

latitude

longitude

1313

北京

丽水莲花小区

[西城区-广安门外]莲花河胡同1号

101969.0

2006

39.890680

116.330223

2140

上海

海棠苑

[普陀区-真如]真北路1902弄1-65号

51336.0

1997

31.247430

121.391095

2643

上海

西凌新邨

[黄浦区-蓬莱公园]西凌家宅路27弄,111弄,137弄,西藏南路1374弄,制造局路365...

75157.0

1987

31.204728

121.487230

3501

广州

锦鸿花园

[海珠区-赤岗]石榴岗路17号

21058.0

1996

23.086848

113.339585

884

北京

华阳家园

[朝阳区-团结湖]姚家园路

68591.0

2003

39.931654

116.474879

335

北京

远洋傲北

[昌平区-北七家]立汤路36号

40920.0

2011

40.144035

116.416105

2391

上海

三杨新村一街坊

[浦东新区-三林]海阳路215弄

61022.0

2013

31.155239

121.489228

1843

上海

莘城苑

[闵行区-莘庄]疏影路1111弄

38664.0

2004

31.116479

121.360167

5640

深圳

赛格景苑

[福田区-景田北]景田北路

73620.0

1998

22.554100

114.037624

3874

广州

晓园新村

[海珠区- 昌岗]江南大道南3号

33617.0

2005

23.086375

113.281508

4.2 按城市分组的房价统计数据

  • count: 数据记录数
  • mean: 平均值
  • std: 标准差
  • min: 最小值
  • 25%: 1/4 位数
  • 50%: 中位数
  • 75%: 3/4 位数
  • max: 最大值
df.groupby('city')['price'].describe()

.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }

count

mean

std

min

25%

50%

75%

max

city

上海

1500.0

59444.306000

29571.455036

5119.0

39905.75

54900.5

73924.50

343089.0

北京

1500.0

65946.664000

32268.489449

6787.0

42127.00

60733.0

84843.00

204745.0

广州

1500.0

33767.053333

22430.584341

5530.0

17548.25

28889.0

43684.75

277682.0

深圳

1500.0

58886.890000

36035.745033

5510.0

37739.50

53107.0

72797.00

330579.0

4.3 数据可视化

按城市分组,显示价格分布小提琴图箱线图

import seaborn as sb
import matplotlib.pyplot as plt
# %matplotlib inline

plt.figure(figsize=(12, 6))
plt.subplot(1,2, 1)
ax1 = sb.violinplot(data = df, x = 'city', y = 'price', inner = 'quartile')
plt.subplot(1,2, 2)
sb.boxplot(data = df, x = 'city', y = 'price')
plt.ylim(ax1.get_ylim())
plt.show()

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python 自动抓取分析房价数据——安居客版

    中秋回家,顺便想将家里闲置的房子卖出去。第一次卖房,没经验,于是决定委托给中介。中介要我定个价。最近几年,房价是涨了不少,但是长期在外,也不了解行情。真要定个价...

    我是一条小青蛇
  • Make 快速入门

    make 是 linux 系统的实用程序。它用于管理对于大型程序的自动编译任务,自动决定程序某一部分需要重新编译,并发出编译指令。虽然,我们最常见于 C 语言程...

    我是一条小青蛇
  • kaggle 图像分类竞赛实战(一):数据集下载和清洗

    本文集以 Kaggle 网站真实竞赛《dogs-vs-cats-redux-kernels-edition》为主线,讲解如何使用深度学习技术解决图像分类问题。本...

    我是一条小青蛇
  • websocket 协议解析

    本文作者:IMWeb went 原文出处:IMWeb社区 未经同意,禁止转载 1.使用websocket 场景 websocket作为用于双向通信的实...

    IMWeb前端团队
  • websocket 协议解析

    websocket作为用于双向通信的实用协议,在笔者最近做的全平台私信系统进行了应用。本次开发的私信系统与普遍理解的“发送-接受-发送”三个流程分开不一样,实现...

    IMWeb前端团队
  • Java WebSocket-1.基础实现

    悠扬前奏
  • WordPress Plugin AutoSuggest插件SQL注入复现与分析

    由于笔者有个习惯,每天都会去exploit-db网站上去逛逛。最近就看到了一个WordPress插件问题导致的SQL注入漏洞,抱着好奇的心,我就开始这个漏洞的复...

    FB客服
  • 针对Quant的Python快速入门指南

    最近有越来越多的朋友在知乎或者QQ上问我如何学习入门Python,就目前需求来看,我需要写这么一篇指南。

    用Python的交易员
  • 知道这几点你就学会了Python!

    由于Python目前在各个领域都比较火,尤其是人工智能和量化金融方面的应用,更让人趋之若鹜,还不会Python的你是不是落伍了呢。下面就是我的不装逼教你学Pyt...

    企鹅号小编
  • 我与Python--从Hacker到探索Deep Learning

    进入大学之后,我们逐渐“被教授”了C、C++、Java等编程语言,但为什么我会选择python作为最喜欢的编程语言呢?

    流川枫

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动