爬虫实战-链家北京房租数据
爬虫实战-链家北京房租数据
本篇是对 恋习Python
发布的原创文章《北京房租大涨?6个维度,数万条数据帮你揭穿》中涉及的代码部分的解读。
< 在复现原文代码时,出现了一些报错,在本文中已进行了更改 >
- 爬虫实战-链家北京房租数据
- 1. 数据获取部分
- 1.1 导入包
- 1.2 获取某市区域的所有链接
- 1.3 通过获取某一区域的页数,来拼接某一页的链接
- 1.4 获取某一区域某一页的详细房租信息
- 1.5 定义主函数及设置初始输入参数
- 2. 数据清洗预览
- 3. 数据分析可视化
- 3.1 导入包
- 3.2 子图一:北京路段_房屋均价分布图
- 3.3 子图二:北京主要路段房屋数量
- 3.4 将两个子图结合起来,Overlap叠加不同类型图表输出
- 1. 数据获取部分
1. 数据获取部分
把目前市场占有率最高的房屋中介公司为目标,来获取北京、上海两大城市的租房信息。
(目标链接:https://bj.lianjia.com/zufang/)
整体思路是:
- 先爬取每个区域的url和名称,跟主url拼接成一个完整的url,循环url列表,依次爬取每个区域的租房信息。
- 再爬每个区域的租房信息时,找到最大的页码,遍历页码,依次爬取每一页的二手房信息。
这里用到的几个爬虫Python包:
- requests: 就是用来请求对链家网进行访问的包
- lxml: 解析网页,用xpath表达式与正则表达式一起来获取网页信息,相比bs4速度更快
1.1 导入包
import requests
import time
import re
from lxml import etree
1.2 获取某市区域的所有链接
headers:请求头
content.xpath:
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'}
对第一个正则表达式 "//dd[@data-index = '0']//div[@class='option-list']/a/text()"
的解读:
//dd
表示定位到html文本中字段头由<dd
开头。[@data-index = '0']
表示定位到符合data-index = '0'
这个条件的位置。//div[@class='option-list']/a
同理向后搜寻,定位到以<div
开头并且符合class='option-list
这个条件的位置。/text()
表示将上述位置后的文本内容抓取出来。
对第二个正则表达式 "//dd[@data-index = '0']//div[@class='option-list']/a/@href"
的解读:
- 同样的代码解读同上。
/@href
表示在上述位置后,将href
后面的内容抓取出来。
# 获取北京市区域的所有链接
def get_areas(url):print('start grabing areas')headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'}response = requests.get(url, headers=headers)content = etree.HTML(response.text) # 使用etree来解析html文本areas = content.xpath("//dd[@data-index = '0']//div[@class='option-list']/a/text()") # content.xpath来获取北京市的各个辖区areas_link = content.xpath("//dd[@data-index = '0']//div[@class='option-list']/a/@href") # content.xpath来获取北京市的各辖区的链接(部分)for i in range(1,len(areas)): # 索引0对应的筛选条件是“不限”,即未区分辖区,故跳过area = areas[i]area_link = areas_link[i]link = 'https://bj.lianjia.com' + area_link # 北京市各辖区的第一页print(link)print("开始抓取页面")get_pages(area, link) # 调用get_pages函数
这个 get_areas 函数完成的任务是:
- 获取了北京市各区域第一个页面的链接
- 这个函数的输入是主函数输入的 url
- 最后一个循环中需要调用 get_pages 函数
1.3 通过获取某一区域的页数,来拼接某一页的链接
这个函数嵌套在上一个函数 get_areas
中,传入两个变量,一个是北京市的辖区 area
(str)、另一个是各辖区的首页链接地址 link
。
re.findall(pattern, string):以列表形式返回给定模式的所有匹配项
对正则表达式 page-data=\'{\"totalPage\":(\d+),\"curPage\"
的解读:
- 以东城区为例,打开东城区第一页的源代码,找到显示该辖区有多少页信息的位置,如下图所示,需要匹配到 “19” ,即是需要的信息。
- 直接表示出包含 “19” 的这段代码,将目标信息用
(\d+)
代替,\d 表示0-9的任意一个数字,后面有+号说明这个0-9单个数位出现一到多次。 - 此段代码中的
\'
、\"
中的 \ 为转义字符。
#通过获取某一区域的页数,来拼接某一页的链接
def get_pages(area, link):headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'}resposne = requests.get(link, headers=headers)pages = int(re.findall("page-data=\'{\"totalPage\":(\d+),\"curPage\"", resposne.text)[0]) # 此处的[0]可以省略,因为返回的列表中只有一个参数,表示的就是总页数print("这个区域有" + str(pages) + "页")for page in range(1,pages+1):url = link + 'pg' + str(page) # 此处的link是上个函数的输入,此处的url整个表示各辖区每一页的链接print(url)print("开始抓取" + str(page) +"的信息")get_house_info(area, url) # 调用get_house_info函数
1.4 获取某一区域某一页的详细房租信息
这个函数嵌套在上一个函数 get_pages
中,传入两个变量,一个是北京市的辖区 area
(str)、另一个是各辖区每一页的链接地址 url
。
time.sleep(secs):函数推迟调用线程的运行,可通过参数secs指秒数,表示程序延迟执行的时间。
try … except … :异常处理:try-except 将可能出现异常退出的代码用try……except来处理。
对正则表达式 "//div[@class='where']/span[1]/span/text()"
中 "/span[1]/span"
的解读:
- 首先定位到
class='where'
的位置,/span[1]
表示再搜索到后面索引值为“1”的span(即第二个span)的位置,/span/text()
表示上一个位置后的span后面的文本内容。
对正则表达式 "([\u4E00-\u9FA5]+)租房"
的解读:
[\u4E00-\u9FA5]
表示所有汉字的unicode编码范围- 后面有+号说明汉字的数量可以出现一到多次。
对表达式 with open('链家北京租房.txt','a',encoding='utf-8')
中参数 'a'
的解读:
- ‘a’ 表示打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
raise e:
- raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。
- 如果只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。
#获取某一区域某一页的详细房租信息
def get_house_info(area, url):headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'}time.sleep(2) # 停顿2秒try:resposne = requests.get(url, headers=headers)content = etree.HTML(resposne.text)info=[]num = len(content.xpath("//div[@class='where']/a/span/text()")) # 这个页面上有多少条房源的信息for i in range(num):title = content.xpath("//div[@class='where']/a/span/text()")[i] # 房源信息的标题room_type = content.xpath("//div[@class='where']/span[1]/span/text()")[i] # 房源信息的户型square = re.findall("(\d+)",content.xpath("//div[@class='where']/span[2]/text()")[i])[0] # 房源信息的面积position = content.xpath("//div[@class='where']/span[3]/text()")[i].replace(" ", "") # 房子的朝向try:detail_place = re.findall("([\u4E00-\u9FA5]+)租房", content.xpath("//div[@class='other']/div/a/text()")[i])[0] # 房子的位置信息except Exception as e:detail_place = "" # 若出现报错,则此变量为空值floor =re.findall("([\u4E00-\u9FA5]+)\(", content.xpath("//div[@class='other']/div/text()[1]")[i])[0] # 房子所在的楼层total_floor = re.findall("(\d+)",content.xpath("//div[@class='other']/div/text()[1]")[i])[0] # 房子的总楼层try:house_year = re.findall("(\d+)",content.xpath("//div[@class='other']/div/text()[2]")[i])[0] # 房子的建楼年限except Exception as e:house_year = "" # 若出现报错,则此变量为空值price = content.xpath("//div[@class='col-3']/div/span/text()")[i] # 房子的租金价格with open('链家北京租房.txt','a',encoding='utf-8') as f:f.write(area + ',' + title + ',' + room_type + ',' + square + ',' +position+ ','+ detail_place+','+floor+','+total_floor+','+price+','+house_year+'\n') print('writing work has done!continue the next page')except Exception as e:print(e) # 打印出异常#raise e # 抛出异常。在调试时可选,方便定位异常的原因time.sleep(30) # 防止被限制,延迟程序30秒return get_house_info(area, url) # 再被抛出异常后,重新执行get_house_info函数
1.5 定义主函数及设置初始输入参数
定义主函数及设置初始输入参数,即我们需要爬取的目标网址。
def main():print('start!')url = 'https://bj.lianjia.com/zufang'get_areas(url)if __name__ == '__main__':main()
2. 数据清洗预览
这里没有进行详细的数据清洗了,详细的步骤请参考公众号之前的文章Python数据分析过程(基础版):
作者在2018年8月27日爬取下来的数据如下图,共有14177条,10个维度。(因时间关系,没有进一步清洗此数据了。)
在图中,发现 square
的最小值为0,明显不合适,通过下面的代码找到对应的数据,之后删除就行了。
3. 数据分析可视化
原文章中,没有介绍前期的一些准备工作,对新手来说,实现起来可能会有一些阻碍,在此将其补充一下。
3.1 导入包
这里使用的是 pyecharts
包,所以需要先导入相应的包。
from pyecharts import Line
from pyecharts import Bar
from pyecharts import Overlap
3.2 子图一:北京路段_房屋均价分布图
.agg
:可以对groupby的结果,同时应用多个函数。
前两行代码是对 detail_place
变量进行了分组,然后对各组的 price
变量取平均值、计数,存储为DataFrame数据框的形式,。
Pandas中关于set_index和reset_index的用法:
set_index:DataFrame通过set_index方法,可以设置单索引和复合索引。DataFrame.set_index(keys, drop=True, append=False, inplace=False, verify_integrity=False) ;append添加新索引,drop为False,inplace为True时,索引将会还原为列。
reset_index:reset_index可以还原索引,从新变为默认的整型索引
DataFrame.reset_index(level=None, drop=False, inplace=False, col_level=0, col_fill=”) ;level控制了具体要还原的那个等级的索引,drop为False则索引列会被还原为普通列,否则会丢失。
.sort_values
:按照某一列的大小进行排序。参数ascending为False时,表示降序排列。
#北京路段_房屋均价分布图
detail_place = df.groupby(['detail_place'])
house_com = detail_place['price'].agg(['mean','count'])
house_com.reset_index(inplace=True)
detail_place_main = house_com.sort_values('count',ascending=False)[0:20] # 将counts列按照降序排序,并取出前20位attr = detail_place_main['detail_place']
v1 = detail_place_main['count']
v2 = detail_place_main['mean']
house_com的输出:
detail_place_main的输出:
开始Python 数据可视化:
- 第一步:初始化具体类型图表。语法为: 图表名字 = 图表类型(“图的名字”)。
- 第二步:添加图表的数据和设置各种配置项。具体的语法是: 图表类型
.add()
。 - 第三步:把图,保存到本地,格式是HTML类型。语法为: 图表类型
.render()
。 - 补充:
show_config()
用于打印输出图表的所有配置项。 - 各参数的解释:
is_stack
参数为False时就不堆叠了;
xaxis_rotate
横坐标标签的倾斜角度;
yaxix_min
??这个没有理解,难道指的是yaxis_min,表示y轴的最小值;
mark_point
标注点;
-mark_point_symbol='diamond'
设置标注点形状;
-mark_point_textcolor='#40ff27')
设置标注点颜色;
mark_line
标注线;
xaxis_interval
x轴坐标标签的间隔;
mark_point_textcolor
设置标注点颜色;
mark_point_symbolsize
设置标注点大小;
is_splitline_show
纵轴的分割线是否显示;
is_more_utils
是否展示右侧工具栏。
line = Line("北京主要路段房租均价") # 初始化具体类型图表
line.add("路段",attr,v2,is_stack=True,xaxis_rotate=30,yaxix_min=4.2,mark_point=['min','max'],xaxis_interval=0,line_color='lightblue',line_width=4,mark_point_textcolor='black',mark_point_color='lightblue',is_splitline_show=False)
3.3 子图二:北京主要路段房屋数量
bar = Bar("北京主要路段房屋数量")
bar.add("路段",attr,v1,is_stack=True,xaxis_rotate=30,yaxix_min=4.2,xaxis_interval=0,is_splitline_show=False)
输出结果如下图:
3.4 将两个子图结合起来,Overlap叠加不同类型图表输出
is_add_yaxis
表示是否新增一个 y 坐标轴,默认为 False
overlap = Overlap() # 实例化Overlap类
overlap.add(bar) #向overlap中添加图
overlap.add(line,yaxis_index=1,is_add_yaxis=True)
overlap.render('北京路段_房屋均价分布图.html')
注:没有找到资料解决在Overlap中自定义标题的方案。
Overlap叠加不同类型图表的 其他示例 。
爬虫实战-链家北京房租数据相关推荐
- python数据分析项目实战—————链家北京租房数据统计分析
链家北京租房数据分析 1.读取数据 import pandas as pd import matplotlib.pyplot as plt import numpy as npdata_dframe ...
- 爬取链家北京租房数据并做简单分析
在一个来北京不久的学生眼中,北京是一个神秘又充满魅力的大城市.它无比美好,但又无时无刻不再觊觎这你薄弱的钱包. 租房是很多人都离不开的硬性需求,这里就对从链家爬取的北京地区房屋出租数据进行一个简单分析 ...
- python建筑案例_Python数据分析实战-链家北京二手房价分析
前言 最近在自学Python,通过学习大家的分享案例,看到使用Python进行较多的主要4个方面:爬虫,数据处理,数据可视化以及机器学习建模.对我来说目标就是: 熟练使用numpy pandas 进行 ...
- 学习python抓取数据——链家北京二手房数据
最近在学习用Python进行数据分析.机器学习,基本都是用现成数据集进行模型训练及验证,想用一些实际数据看一下效果,于是想到用Python尝试抓取一些实际数据. 目标:爬取链家网北京二手房房价.位置. ...
- 掌财社:python怎么爬取链家二手房的数据?爬虫实战!
我们知道爬虫的比较常见的应用都是应用在数据分析上,爬虫作为数据分析的前驱,它负责数据的收集.今天我们以python爬取链家二手房数据为例来进行一个python爬虫实战.(内附python爬虫源代码) ...
- python二手房使用教程_python实战:基于链家网二手房数据解析任务
实战:基于链家网二手房数据解析任务 页面:https://bd.ke.com/ershoufang/ 获取如下信息: 标题 位置 房屋信息 关注人数 / 发布时间 房屋总价 房屋单价 备注信息 1.导 ...
- Python爬取链家北京租房信息
刚学习了python,中途遇到很多问题,查了很多资料,最关键的就是要善于调试,div信息一定不要找错,下面就是我爬取租房信息的代码和运行结果: 链家的房租网站 两个导入的包 1.requests 用来 ...
- 利用xpath爬取链家租房房源数据并利用pandas保存到Excel文件中
我们的需求是利用xpath爬取链家租房房源数据,并将数据通过pandas保存到Excel文件当中 下面我们看一下链家官网的房源信息(以北京为例) 如图所示,我们通过筛选得到北京租房信息 那么我们需要将 ...
- 使用Java及jsoup爬取链家北京二手房房价数据
由于是初次使用Java写爬虫,所以代码有些繁琐,请大家见谅,并能给与指正 首先分析链家北京二手房页面,使用360浏览器的审查元素功能,查看源代码,获取查询标签 如图一级查询所示,此图标签所获取的是链家 ...
最新文章
- linux使用shell函数扩充命令,Linux Shell系列教程之(十五) Shell函数简介 | Linux大学...
- 极光推送配置(Android Studio),亲测有效
- 放弃OpenStack?恐怕还不到时候
- Android Bundle类 学习总结
- Navicat for MySQL v8.0.27 的注册码
- Linux打开bashrc权限不够,bash-4.2$ bash: /home/test/.bashrc: 权限不够
- 收藏 | 佐治亚理工出品交互式线代教科书!
- 微信小程序 1 ~ Hello 微信小程序
- VS2010 上手案例---hello word
- 学习记录之显示屏语言模块确定,星瞳学习
- LumaQQ安装过程详解(转)
- 修改c盘user用户文件夹名称
- IBM 开源J9 JVM
- php 汉字、字母验证码 例子
- 2022.5.4.学习笔记数据类型
- Kubernetes之(二十)Helm程序包管理器
- 课程设计-商店管理系统(一)----前端页面的制作(一)
- FLT_MAX 和 FLT_MIN的定义
- safari文件下载后缀加.exe
- 渡一教育js- 10原型,原型链,call/apply
热门文章
- HADOOP KMS使用介绍及性能测试
- 未明学院学员报告:学会数据分析后,我发现美妆大牌到底哪家强 为什么是江浙沪包邮?
- Embedded Linux S3C2440 - QEMU and Graphic
- 各种HIC处理数据之间的相互转化
- linux 把进程调到前台,【如何将后台运行的程序转到前台来?】
- 电子认证许可证书(电子签名 需要 什么资质)
- 服务器未能保存文件夹,Exchange服务器提示 Event ID 50 Ntfs (Ntfs) {延迟写入失败} Windows 无法保存文件...
- 开源许可违反:案例说明(Apache License 2.0)
- 通过netsh一键连接指定WIFI
- iOS 自定义相机,拍照旋转