导语:前几天舍友送了一本介绍正则表达式的书籍,由此联想到,好久没有爬过网站了,但之前学的东西差不多全还给秦老板了(给我们上python的老师)。于是立马上网找了几篇博客,重温了一下几个包的知识点,动了一下小手,这不就有了等下要和你们讲的故事喽。

(一)项目介绍

1. 工具

pycharm 2020.1.5

2. 内容

本项目分为两部分:爬虫 + 数据可视化分析

(1)爬虫:用requests包爬取链家租房网页内容——BeautifulSoup解析——re正则表达式匹配我们需要的内容——sqlite3保存数据

(2)数据可视化分析:pandas清洗分析数据——pyecharts进行可视化分析

(二)爬虫部分

(1)爬虫第一步,了解我们将要爬取网页的信息

如图所示:小框是链家网页租房的网址,大框是我们想要从网页中获取的内容。

由于链家的限制, 把页面往下拉到底部就可以看见,一次最多只能访问100页,一个页面有30套房,就是说,我们一次最多能看见3000套房子的信息。但是实际上的租房房源远不3000。怎么办呢?我们可以通过增加限制条件来获取到尽量全的房源。

        比如按照区域来爬取网页,而不是所有一次性大杂烩爬取,这样可以大大提高我们爬取房源的数量。不过按照区域爬取网页的话,网址也会有相应的改变。下图就是按照区域查看网页的内容,可以看出,这样子网页的url =  https://bj.lianjia.com/zufang/ + 区域拼音 + /pg + 相应网页页数 + /

现在问题又来了,按区域爬虫,区域我们是确定知道的,因为北京统共就那么几个区,但是网址中还需要知道各个区域的页数,各个区域的页数又不相同,那么各个区域分别有多少页呢?这里我用了一个比较笨的方法,但方法虽笨,实现还是挺简单的,就是先爬取每个区域的第一页,页面的最后有显示最大网页数,由此我们就可以得到最大网页数。现在先Fn+F12一下来看看网页的html文件吧~

从上图,我们可以看出页数的信息在div class="content__pg"下面的内容,由此我们可以据其写出正则匹配表达式,并在div class="content__pg"下面的内容找到最大页数。

本次爬虫用的是requests包,首先构造一个响应头部header,用于伪装,然后构造get请求并发起,将爬取下来的数据存于html中,详见代码ask_url函数。

在get_page函数中调用ask_url函数,循环爬取每个区的第一页,一边爬一边用BeautifulSoup解析,并在解析过的数据中,用re.findall()在div class="content__pg"下面的内容匹配找出最大页数。get_page函数中用了两个for循环,一个调用regions中的区,然后爬虫,一个用来查找爬取内容的最大页数。(本次爬虫的限制条件,即区域,只有13个,其他4个较为边缘的区没有计入)

import requests
from bs4 import BeautifulSoup
import re
import sqlite3
import pandas as pddef main():baseurl = 'https://bj.lianjia.com/zufang/'pagelist = get_page(baseurl)                    #得到每个区的页数datalist = get_data(baseurl, pagelist)          #得到所有数据dbpath = "zufang.db"savedata_2_db(datalist, dbpath)                 #存入数据库findpage = re.compile(r'data-totalpage="(\d*)" data-ur')     #构建正则匹配,最大页数def get_page(baseurl):          #找出每个区的页数pagelist = []regions = ['dongcheng', 'xicheng', 'chaoyang', 'haidian', 'fengtai', 'shijingshan', 'tongzhou', 'changping','daxing', 'fangshan', 'shunyi', 'mentougou', 'yizhuangkaifaqu']for reg in regions:url = baseurl + reg + '/pg1/'html = ask_url(url)soup = BeautifulSoup(html, 'html.parser')for item in soup.find_all('div', class_="content__pg"):item = str(item)total_page = re.findall(findpage, item)pagelist.extend(total_page)pagelist = list(map(int, pagelist))return pagelistdef ask_url(url):        #开始爬虫html = ''header = {"User-Agent": "Mozilla/5.0(Windows NT 10.0;WOW64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/84.0.4147.89Safari/537.36 SLBrowser/7 .0.0.6241 SLBChan/23"}        #请求头try:response = requests.get(url, headers=header)    #请求并发起html = response.content.decode('utf-8')#t = uniform(1, 3)#time.sleep(t)except requests.exceptions.ProxyError:print("爬虫发生错误")return html

(2)正式爬取网页信息并解析匹配

从上面,我们得到了每个区的页数,并保存于pagelist里面,返回。接下来,就要正式爬取我们想要的内容了。首先,设置一个空列表来存放我们解析匹配后得到的数据。两个for循环,调用ask_url函数,才能得到regions里面所有区各自的页数的全部内容。

爬取一页内容,解析一页。

再一次Fn+F12,可以看见,我们想要获取的内容,都在div class_="content__list--item",我们先print一个或者多个解析过后的soup,观察其结构和内容,可根据其中内容用re.compile()构造出我们所需内容的正则表达式,在这里就不一一展开了,有兴趣的uu找找相关正则表达式的博客。

之后就开始匹配我们需要的内容,用re包里面的findall方法找出和正则表达式匹配的内容,并先将内容暂时存在一个data列表里,等到所有想要得到的内容都匹配完成之后,再加入datalist列表中。这一步,我们会得到所有想要的信息。

findName = re.compile(r'target="_blank" title="(.*)">')          #构建正则匹配规则
findDistrict = re.compile(r'>(.*?)</a>-<a ')
findLocation = re.compile(r'target="_blank">(.*?)</a>-<a')
findArea = re.compile(r'(\d*\.\d*)㎡')
findDirection = re.compile(r'<i>/</i>(.*?)        <i>/</i>')
findHousetype = re.compile(r'(\d.\d?.?\d?.?)        <span class="hide">')
findSubway = re.compile(r'<i class="content__item__tag--is_subway_house">(.*)</i>')
findPrice = re.compile(r'span class="content__list--item-price"><em>(\d*)</em>')def get_data(baseurl, pagelist):datalist = [] regions = ['dongcheng', 'xicheng', 'chaoyang', 'haidian', 'fengtai', 'shijingshan', 'tongzhou', 'changping','daxing', 'fangshan', 'shunyi', 'mentougou', 'yizhuangkaifaqu']for i in range(0, len(regions)):for page in range(1, pagelist[i] + 1):    #总共page页,爬取页面信息page次page = page + 1url = baseurl + regions[i] + '/pg' + str(page)html = ask_url(url)       #调用函数ask_url,逐次保存爬取下来的信息soup = BeautifulSoup(html, 'html.parser')   #解析html文件#print(soup)for item in soup.find_all('div', class_="content__list--item"):data = []item = str(item)#正则匹配找出所需要的信息Name = re.findall(findName,item)[1]data.append(Name)District = re.findall(findDistrict, item)[0]data.append(District)Location = re.findall(findLocation, item)[1]data.append(Location)Area = re.findall(findArea, item)[0]data.append(Area)Direction = re.findall(findDirection,item)[0]data.append(Direction)Housetype = re.findall(findHousetype,item)[0]data.append(Housetype)Subway = re.findall(findSubway,item)if len(Subway) != 0:Subway = Subway[0]data.append(Subway)else:data.append(" ")Price = re.findall(findPrice, item)[0]data.append(Price)datalist.append(data)return datalist

(3)初始化数据库

首先连接数据库,开启游标功能,使用游标中的execute功能创建zufangdata表,关闭数据库,释放资源。

def init_db(dbpath):  #初始化数据库sql = '''CREATE TABLE zufangdata(id integer primary key autoincrement,name varchar,district varchar,location varchar,area varchar,direction varchar,house_type varchar,subway varchar,price numeric)'''conn = sqlite3.connect(dbpath)cursor = conn.cursor()cursor.execute(sql)conn.commit()conn.close()

(4)将爬取的数据存储到sqlite

第一步,先调用上一步初始化数据库init_db函数;

第二步,连接数据库,开启游标对象,创建游标功能;

第三步,使用一个for循环,用游标中的execute功能将datalist中的数据逐行插入到数据库的zufangdata表中(其中还有一个for循环,用来加引号的,因为SQL语言非数值型数据要加引号,数值型不用);

第四步,关闭游标,关闭连接

def savedata_2_db(datalist, dbpath):        #保存数据到数据库init_db(dbpath)                         #调用创建数据库函数conn = sqlite3.connect(dbpath)cur = conn.cursor()for data in datalist:for index in range(len(data)):  #列的长度if index == 8:              #SQL数值型不用加引号continuedata[index] = '"' + data[index] +'"'sql = '''INSERT INTO  zufangdata(name, district, location, area, direction, house_type, subway, price)VALUES(%s)'''%",".join(data)            #逗号填补前面s,一行一行假如data数据cur.execute(sql)conn.commit()cur.close()conn.close()if __name__ == "__main__":      #当程序执行时main()#init_db("zufang1.db")print("爬取完成")

打开python DB browser(我用的python是社区版,所以得自己安装数据库插件,如果有不会安装的话,回首页找找教程!很快就可以安好!)看见的数据如下图所示:

        到这里,我们已经成功爬取到了我们所需要的数据,并将数据保存在sqlite中!! 革命已经成功一半啦!!  胜利就在前方,铁子们加油!

(三)数据可视化部分

(1)数据清洗

引入pandas和pyecharts模块

连接数据库,用pandas中的读取数据库数据的方法将表中数据全部取出,接下来就是老套的数据清洗流程,这边就不详细讲啦,上篇讲的比较清楚,有需要的uu可以翻翻上篇https://blog.csdn.net/lynnhc007/article/details/12071134

import sqlite3
import pandas as pd
import pyecharts.options as opts
from pyecharts.charts import Bar, Piedef main():zufang_data = data_cleaning()visual_analyse(zufang_data)def data_cleaning():#建立数据库连接,读取数据connection = sqlite3.connect('zufang.db')zufang_data = pd.read_sql_query("select * from zufangdata;", connection)#查看数据类型#print(zufang_data.info())#数据类型转换zufang_data['area'] = zufang_data['area'].astype(float)#删除错误数据for i in range(21426, 21442):zufang_data = zufang_data.drop([i])zufang_data = zufang_data.drop([6622])#print(zufang_data) #验证#查看你重复值,无#print(zufang_data.duplicated().sum())#查看缺失值,无#print(zufang_data.isnull().sum())return zufang_data

(2)数据可视化

(2.1)地区分析

房源数量分布情况如第二个图,可以看到朝阳、海淀、丰台和大兴这四个区的房源数量要远大于其它区,说明这两个地方的租赁市场比较活跃,人员流动和人口密度可能也比较大。

代码实现:首先,用pandas的groupby方法,按区域统计各个区的房源数量,得到的series数据,index是各个区名,column是房源总数,以条形图的形式展现,生成html文件

def visual_analyse(zufang_data):#区域分析amount_of_district = zufang_data.groupby(by='district')['id'].count()x_district = list(amount_of_district.index)y_amount = list(amount_of_district.values)bar1 = (Bar().add_xaxis(x_district).add_yaxis('房源数量', y_amount).set_global_opts(title_opts=opts.TitleOpts(title='各区房源数量'),xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45})))bar1.render("各区房源数量.html")

(2.2) 小区分析

排序选出平均平方最贵的10小区,最贵的是蓑衣胡同,每平方500RMB。真的好贵,一块砖就要几百块!!!

#小区分析zufang_data['price_1square'] = zufang_data['price'] / zufang_data['area']expensive_flat = pd.Series(zufang_data['price_1square'].nlargest(10))flat_name = []for i in expensive_flat.index:flat_name.append(zufang_data['name'][i])flat_values = []for value in expensive_flat.values:flat_values.append(round(value, 1))bar2 = (Bar().add_xaxis(flat_name).add_yaxis('单位平方价格', flat_values).reversal_axis().set_series_opts(label_opts=opts.LabelOpts(position="right")).set_global_opts(title_opts=opts.TitleOpts(title='最贵小区Top10'),xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45})))bar2.render("最贵小区Top10.html")

(2.3)户型分析

户型分布图在下面图2,可以看出,2间房间的户型远远超出其他户型,2间方+3建房的房源加起来几乎占据的所有户型的2/3,可见,大部分房源都是合租,毕竟租房这么贵,生活成本太高了!!!

#户型分析#在zufang_data后添加一列房间数rooms = []for room in zufang_data['house_type']:        #取house_type第一个数,即几间房rooms.append(room[0:1])zufang_data['rooms'] = rooms            #以房间数作为分组条件进行分组并计数,删除房间数为0的异常值amount_rooms = zufang_data.groupby(by='rooms')['id'].count()amount_rooms = amount_rooms.drop('0')room_index = list(amount_rooms.index)room_values = list(amount_rooms.values)bar3 = (Bar().add_xaxis(room_index).add_yaxis('户型', room_values).set_global_opts(title_opts=opts.TitleOpts(title='房子户型'),xaxis_opts=opts.AxisOpts(name_rotate=60, axislabel_opts={"rotate": 45})))bar3.render("户型.html")

(2.4) 交通分析

如图2所示,近地铁的房源只占所有房源的33.7%,可见地铁房是较为稀缺的房源;近地铁的房子每平方均价是107.2RMB,非近地铁房子是84.22RMB,靠近地铁的房子每平方米高出将近23元,交通越便利,租金越贵,这个符合一般认知。

#交通分析subway_ava = zufang_data.groupby(by='subway')['id'].count()#print(subway_ava)subway_index = ['远地铁', '近地铁']subway_values = list(subway_ava.values)subway_pairs = list(zip(subway_index, subway_values))      #python3中要展示列表得加list,否则只返回一个对象#[('远地铁', 14205), ('近地铁', 7220)]pie = (Pie().add(series_name='交通分析', data_pair=[('远地铁', 14205), ('近地铁', 7220)],label_opts=opts.LabelOpts(is_show=False, position="center")).set_series_opts(tooltip_opts=opts.TooltipOpts(trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"))).render('地铁房占比.html')subway_price = zufang_data.groupby(by='subway')['price'].sum()subway_area = zufang_data.groupby(by='subway')['area'].sum()avg_price = subway_price.values / subway_areay_price = []for i in avg_price.values:y_price.append(round(i, 2))bar4 = (Bar().add_xaxis(['远地铁', '近地铁']).add_yaxis('户型', y_price).set_global_opts(title_opts=opts.TitleOpts(title='地铁房均价'))).render("地铁房均价.html")if __name__ == "__main__":main()

数据分析实战项目--链家租房数据可视化分析相关推荐

  1. 数据挖掘与数据分析项目链家租房数据(一)数据爬虫

    数据挖掘与数据分析项目链家租房数据(一)数据爬虫 今日无聊将一个过去做的链家数据分析项目弄上来,当时是某面试,三天时间完成,主要是供大家抄代码和分享一下思考点,这一章是爬虫部分. 网站原图 结果截图 ...

  2. Python数据分析实战,,美国总统大选数据可视化分析[基于pandas]

    目录 前言 一.任务详情 二.数据集来源 三.实现过程 四.运行代码 前言 在学习Python数据分析的过程中,是离不开实战的. 今天跟大家带来数据分析可视化经典项目,美国总统大选数据可视化分析,希望 ...

  3. 数据挖掘与数据分析项目链家租房数据(三)进一步探索与归纳

    当时认为起初的分析逻辑混乱,模型单一,从这两个角度进行改进继续分析. 未进行再次加工,代码见资源中的exploration2 问题背景及重述 想法最初产生于如下背景:目前的租房市场中租房一方往往处于弱 ...

  4. 爬虫实战:链家租房数据爬取,实习僧网站数据爬取

    前面已经进行了爬虫基础部分的学习,于是自己也尝试爬了一些网站数据,用的策略都是比较简单,可能有些因素没有考虑到,但是也爬取到了一定的数据,下面介绍两个爬过的案例. 爬虫实战 链家网站爬取 实习僧网站爬 ...

  5. 深圳租房数据可视化分析【Plotly库绘图】

    深圳租房数据可视化分析[plotly库绘图] 一.技术介绍 1.可视化技术支持来源: 2.选择plotly理由: 二.代码实现及分析: 1.导入库及解读数据集: 2.数据清洗与转换 3.统计数据 4. ...

  6. 知乎爬虫与数据分析(二)pandas+pyecharts数据可视化分析篇(上)

    注:代码完整版可移步Github--https://github.com/florakl/zhihu_spider. 知乎爬虫与数据分析(一)数据爬取篇 知乎爬虫与数据分析(三)pandas+pyec ...

  7. python实战项目_11 个实战项目,掌握 Python 数据可视化

    ​俗语有曰:字不如表,表不如图. 人类天生就是视觉动物.想象一本教科书,如果没有图表.插图或流程图,将变得更加枯燥和难以理解.视觉效果对于数据分析.传达结果都有至关重要的作用. 数据可视化就是通过易读 ...

  8. python数据可视化 知乎_11 个实战项目,掌握 Python 数据可视化

    ​俗语有曰:字不如表,表不如图. 人类天生就是视觉动物.想象一本教科书,如果没有图表.插图或流程图,将变得更加枯燥和难以理解.视觉效果对于数据分析.传达结果都有至关重要的作用. 数据可视化就是通过易读 ...

  9. Python爬虫入门教程石家庄链家租房数据抓取

    1. 写在前面 这篇博客爬取了链家网的租房信息,爬取到的数据在后面的博客中可以作为一些数据分析的素材. 我们需要爬取的网址为:https://sjz.lianjia.com/zufang/ 2. 分析 ...

  10. Python爬虫入门【16】:链家租房数据抓取

    1. 写在前面 作为一个活跃在京津冀地区的开发者,要闲着没事就看看石家庄这个国际化大都市的一些数据,这篇博客爬取了链家网的租房信息,爬取到的数据在后面的博客中可以作为一些数据分析的素材. 我们需要爬取 ...

最新文章

  1. 为什么重启路由器 经常重启让WiFi更快
  2. MySQL和java连连看_用 JAVA 开发游戏连连看(之一)动手前的准备
  3. 单手撸了个springboot+mybatis+druid
  4. 【转】人工智能-1.2.2 神经网络是如何进行预测的
  5. Spring mvc 组件
  6. python 版本控制及django,git的使用
  7. javaweb入门笔记(5)-cookie和session
  8. JSON服务器(json-server)
  9. 视频编解码(十四):机顶盒调试编解码器显示总结
  10. Ado.net的连接池
  11. 02 | 该如何选择消息队列?
  12. 使用微博自动记录俯卧撑个数
  13. 长期戴耳机听歌的危害这么大,这些坏习惯你还在做吗
  14. DiskGenius 复制磁盘 提示 设备未就绪
  15. 计算机教师知识老化,【计算机教学论文】技校计算机教学分析(共2800字)
  16. 原生js实现动态数据表格
  17. Android日期时间与时区使用总结汇总
  18. 大家在人生低谷时有多惨,怎么熬过来的(二)
  19. android 面试算法题:青蛙跳楼梯 阶乘求和 三位数中的水仙花数
  20. PyQt5技术分享:制作一个美观的Dock栏

热门文章

  1. 安装SQL 2000挂起的解决办法
  2. 2018年湖南省高中数学联赛(A)卷试题
  3. 题目234 吃土豆
  4. 前端工作日记day2
  5. 动态页面抓取超级指南_减少页面重量的完整指南
  6. 方舟生存进化怎么自建服务器?方舟生存进化自建服务器教程
  7. gta5正版私人服务器,《GTA5》国内神级服务器,万人同服?堪称虚拟版地球OL?...
  8. python音乐播放器以及美观化_Python3.4的标准库
  9. Sentinel-哨兵机制
  10. 幻灯片制作去除模板背景