Python 实现高德地图找房
知识点
requests、BeautifulSoup、csv 等库的简单使用
高德地图 JavaScript API 的使用
1.安装需要的库:
sudo pip3 install --upgrade pip
sudo pip3 install bs4
sudo pip3 install lxml
用到三个文件:crawl.py、rent.csv与index.html,其中:
crawl.py 是一个非常简单的爬取网页的脚本。
rent.csv 由 crawl.py 生成,是房源文件。
index.html 是最重要的显示地图的部分。
实现的流程大致如下:
我为什么不把 js 代码和 css 代码从 index.html 中分出来呢,写脚本怎么顺手怎么来就好。
代码没有难度,主要就是看看几个 API 如何使用,下面给出文档链接:
高德 JavaScript API 帮助文档
高德 JavaScript API 示例中心
Requests: HTTP for Humans
Beautiful Soup 4.2.0 文档
本节将通过实践操作,带领大家利用高德 API 和 Python 解决租房问题。
注意:由于前端页面可能会经常发生变动,大家在这里主要掌握这种分析方法,如果页面发生了变化依然可以按照对应的方法进行爬取。
2.分析一下我们需要爬取的页面:https://bj.58.com/pinpaigongyu/。
选择好目标价位:
按 F12 键或右键点击检查分析元素:
我们发现所有的租房信息都是一个 li 标签,并且都位于 ul 标签下,当我们往下移的时候,相应的标签也会越来越多,所以我们便可以利用有无 list 类来判定是否为租房请求。当然,这是在没有任何条件获取到的全部信息,但很多信息我们是不会看的,既然有租房的意愿,那么我们就有一个大致的心理承受价格,因此我们可以来分析一下该网站的 url 构造形式,大致了解了它的路径规则:/pingpaigongyu/pn/{page}/minprice={min_rent}_{max_rent}
。
看一下页面能够提供给我们什么信息:
框框圈出的信息对我们来说已经足够了,点进去后还可以查看其他各类信息:
这些信息有些是经过了加密,并不一定准确,然后我们就可以根据以上信息来写一个小的爬虫程序。
创建 crawl.py 文件,这里先直接给出全部代码。
#!/usr/bin/env python3
from bs4 import BeautifulSoup # 网页解析模块
import requests # 网络请求模块
import csv # csv 文件模块
import time
import lxml
# 网址
url = "https://bj.58.com/pinpaigongyu/pn/{page}/?minprice=2000_4000"# 初始化页码
page = 0
# 打开rent.csv文件,如果没有就创建一个,并设置写入模式
csv_file = open("rent.csv","w",encoding="utf-8")
# 创建writer对象
csv_writer = csv.writer(csv_file, delimiter=',')
# 循环所有页面
while True:page += 1print("fetch: ", url.format(page=page))time.sleep(1)# 抓取目标页面response = requests.get(url.format(page=page))# 设置编码模式response.encoding = 'utf-8'# 创建一个BeautifulSoup对象,获取页面正文html = BeautifulSoup(response.text,features="lxml")# 获取当前页面的房子信息house_list = html.select(".list > li")# 循环在读不到新的房源时结束if not house_list:break# 房子信息for house in house_list:# 获取房子标题house_title = house.select("h2")[0].string# 获取房子链接地址house_url = house.select("a")[0]["href"]# 对标题进行分隔house_info_list = house_title.split()# 如果第二列是公寓名则取第一列作为地址if "公寓" in house_info_list[1] or "青年社区" in house_info_list[1]:house_location = house_info_list[0]else:house_location = house_info_list[1]house_money = house.select(".money")[0].select("b")[0].string# 写入一行数据csv_writer.writerow([house_title, house_location, house_money, house_url])# 关闭文件
csv_file.close()
鉴于爬的量不大所以就简单处理了。代码中已经给出了大部分注释,这里只对requests、BeautifulSoup、csv 库的使用进行说明:
csv 一般用作表格文件,直接用文本编辑器打开也可读,行与行之间就是换行来隔开,列与列之间就是用逗号(也可指定其它字符)隔开,Python 标准库中的 csv 库就是用来读写 csv 文件的。
requests 是一个对使用者非常友好的 http 库,看一遍 Quickstart 就能基本掌握它的使用。
用到它的地方也就仅仅两句:
# 抓取目标页面
response = requests.get(url.format(page=page))
# 获取页面正文
response.text
Beautiful Soup
是一个用来解析 html 或者 xml 文件的库,支持元素选择器,使用起来也非常方便:
# 创建一个 BeautifulSoup 对象
html = BeautifulSoup(response.text,features="lxml")# 获取 class=list 的元素下的所有 li 元素
house_list = html.select(".list > li")# 得到标签包裹着的文本
house.select("h2")[0].string
# 得到标签内属性的值
house.select("a")[0]["href"]
由于该网站设置了反爬虫机制非常容易被屏蔽。因此在每次爬取页面时使用 time.sleep(1),1 代表 1 秒。讲解完毕,运行代码 python3 crawl.py。注意如果是非会员在实验环境中不能联网,执行爬取的操作大家可以在本地进行)。等执行完毕查看目录会发现已经生成了 rent.csv 文件:
因为反爬虫以及数据加密的原因所以数据可能是乱码的,大家可以使用这个文件继续接下来的实验。
wget https://labfile.oss.aliyuncs.com/courses/599/rent.csv
后面我们需要从本地导入这个文件,大家需要下载这个文件到本地。
页面大框架可直接从示例中心复制:高德 JavaScript API 示例中心。
在 /home/project 目录下新建 index.html 复制粘贴下面的设计页面代码:
<html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><metaname="viewport"content="initial-scale=1.0, user-scalable=no, width=device-width"/><title>毕业生租房</title><linkrel="stylesheet"href="http://cache.amap.com/lbs/static/main1119.css"/><linkrel="stylesheet"href="http://cache.amap.com/lbs/static/jquery.range.css"/><script src="http://cache.amap.com/lbs/static/jquery-1.9.1.js"></script><script src="http://cache.amap.com/lbs/static/es5.min.js"></script><script src="http://webapi.amap.com/maps?v=1.3&key=36aa77404a1c2f2fc922a569eaa49422&plugin=AMap.ArrivalRange,AMap.Scale,AMap.Geocoder,AMap.Transfer,AMap.Autocomplete"></script><script src="http://cache.amap.com/lbs/static/jquery.range.js"></script><style>/*面板控制样式*/.control-panel {position: absolute;top: 30px;right: 20px;}/*面板内容样式*/.control-entry {width: 280px;background-color: rgba(119, 136, 153, 0.8);font-family: fantasy, sans-serif;text-align: left;color: white;overflow: auto;padding: 10px;margin-bottom: 10px;}/*文字与右侧的距离*/.control-input {margin-left: 120px;}/*输入框宽度*/.control-input input[type='text'] {width: 160px;}/*文字样式*/.control-panel label {float: left;width: 120px;}/*路线规划信息窗体样式*/#transfer-panel {position: absolute;background-color: white;max-height: 80%;overflow-y: auto;top: 30px;left: 20px;width: 250px;}</style></head><body><div id="container"></div><!--显示输入地址面板--><div class="control-panel"><div class="control-entry"><label>选择工作地点:</label><div class="control-input"><input id="work-location" type="text" /></div></div><!--显示选择交通的面板--><div class="control-entry"><label>选择通勤方式:</label><div class="control-input"><inputtype="radio"name="vehicle"value="SUBWAY,BUS"onClick="takeBus(this)"checked/>公交+地铁<inputtype="radio"name="vehicle"value="SUBWAY"onClick="takeSubway(this)"/>地铁</div></div><!--显示导入房源的面板--><div class="control-entry"><label>导入房源文件:</label><div class="control-input"><input type="file" name="file" onChange="importRentInfo(this)" /></div></div></div><div id="transfer-panel"></div><script>//地图部分var map = new AMap.Map('container', {resizeEnable: true, //页面可调整大小zoomEnable: true, //可缩放center: [116.397428, 39.90923], //地图中心,这里使用的是北京的经纬度zoom: 11, //缩放等级,数字越大离地球越近});</script></body>
</html>
copy
讲解一下部分代码:
这一句中你会看到 key 这个参数,它需要你注册高德的开发者用户,创建应用才能得到,由于 JS API 不像它家的 Web API 一样有流量限制,所以这个 key 大可随意使用。
<script src="http://webapi.amap.com/maps?v=1.3&key=36aa77404a1c2f2fc922a569eaa49422&plugin=AMap.ArrivalRange,AMap.Scale,AMap.Geocoder,AMap.Transfer,AMap.Autocomplete"></script>
接下来我们简单的介绍一下如何注册高德地图的 key。
第一步:登录高德地图,点击右上角进行登录。
第二步:完善资料,包括开发者认证,邮箱认证,电话认证等,如实填写即可,由于认证比较简单,就不贴图了。
第三步:点击右上角控制台,创建应用,应用信息根据自己的实际填写即可。
第四步:创建完应用后点击右上角的“+”号就可以创建应用的 Key,使用地图是根据开发文档把相应位置的 key 值换成自己的就好了。
第五步:只需要根据开发文档把相应的 JS 引入项目,把你的 key 填写就可以了。
<script src="http://webapi.amap.com/maps?v=1.3&key=你的key值&plugin=AMap.ArrivalRange,AMap.Scale,AMap.Geocoder,AMap.Transfer,AMap.Autocomplete"></script>
载入编写代码时可能用到的 API 插件:
<script src="http://webapi.amap.com/maps?v=1.3&key=36aa77404a1c2f2fc922a569eaa49422&plugin=AMap.ArrivalRange,AMap.Scale,AMap.Geocoder,AMap.Transfer,AMap.Autocomplete"></script>
从名字就可以看出插件的作用:
ArrivalRange:公交到达圈。
Scale:标尺。
Geocoder:正向地理编码(地址-坐标)。
Transfer:路径规划。
Autocomplete:地址自动补全。
这些都可以在示例中心找到对应的例子:高德地图示例中心。
在输入标签内可以设定 onClick 与 onChange 属性,它们的作用是当该输入元素上发生点击或者内容变化的事件时,设定的内容就会被运行。
<inputtype="radio"name="vehicle"value="SUBWAY,BUS"onClick="takeBus(this)"checked
/>
<input type="file" name="file" onChange="importRentInfo(this)" />
control-panel 就是右上角的输入面板区域。transfer-panel 是路径规划面板,它只有在调用了路径规划的函数时才会出现。
<div class="control-panel"><div id="transfer-panel"></div>
</div>
输入 python3 -m http.server 8080 打开服务器,浏览器访问 http://localhost:8080 查看效果。
这个时候你发现页面里面没有地图,这是为什么呢?打开控制台看报错信息:
这是因为 https 页面里动态的引入 http 资源,比如引入一个 JS 文件,会被直接 block 掉的,在 https 页面里通过 AJAX 的方式请求 http 资源,也会被直接 block 掉的。解决办法是,在页面的 head 标签中加入:
<metahttp-equiv="Content-Security-Policy"content="upgrade-insecure-requests"
/>
意思是自动将 http 的不安全请求升级为 https。当然你也可以直接把 http 修改成 https,这里只是分享一下遇到的 bug 以及解决方法。
最终效果:
高德这套 API 只要逛逛示例中心,示例中心没有的就去看看参考手册,基本就够用了。接着上一节 index.html 里 script 的内容:
var map = new AMap.Map('container', {resizeEnable: true,zoomEnable: true,center: [116.397428, 39.90923],zoom: 11,
});
//添加标尺
var scale = new AMap.Scale();
map.addControl(scale);
//经度、纬度、时间、通勤方式(默认是地铁+公交)
var arrivalRange = new AMap.ArrivalRange();
var x,y,t,vehicle = 'SUBWAY,BUS';
//工作地点,工作标记
var workAddress, workMarker;
//房源标记数组
var rentMarkerArray = [];
//多边形数组,存储到达范围的计算结果
var polygonArray = [];
//路线规划
var amapTransfer;
//信息窗体对象
var infoWindow = new AMap.InfoWindow({offset: new AMap.Pixel(0, -30),
});
//地址自动补全对象
var auto = new AMap.Autocomplete({input: 'work-location',
});
//添加事件监听,在选择完地址后调用workLocationSelected
AMap.event.addListener(auto, 'select', workLocationSelected);function takeBus(radio) {vehicle = radio.value;loadWorkLocation();
}function takeSubway(radio) {vehicle = radio.value;loadWorkLocation();
}
//导入房源信息触发的方法
function importRentInfo(fileInfo) {//获取房源文件名称var file = fileInfo.files[0].name;loadRentLocationByFile(file);
}
//选择工作地点后触发的方法
function workLocationSelected(e) {//更新工作地点,加载到达范围workAddress = e.poi.name;//调用加载1小时到达区域的方法loadWorkLocation();
}
//加载工作地点标记
function loadWorkMarker(x, y, locationName) {workMarker = new AMap.Marker({map: map,title: locationName,icon: 'http://webapi.amap.com/theme/v1.3/markers/n/mark_r.png',position: [x, y],});
}
//加载到达范围
function loadWorkRange(x, y, t, color, v) {arrivalRange.search([x, y],t,function (status, result) {if (result.bounds) {for (var i = 0; i < result.bounds.length; i++) {//多边形对象var polygon = new AMap.Polygon({map: map,fillColor: color, //填充色fillOpacity: '0.4', //透明度strokeColor: color,strokeOpacity: '0.8',strokeWeight: 1, //线宽});//到达范围的多边形路径polygon.setPath(result.bounds[i]);//增加多边形polygonArray.push(polygon);}}},{policy: v,});
}
//添加房源标记
function addMarkerByAddress(address) {//地理编码对象var geocoder = new AMap.Geocoder({city: '北京',radius: 1000,});//获取位置geocoder.getLocation(address, function (status, result) {if (status === 'complete' && result.info === 'OK') {//获取地理编码var geocode = result.geocodes[0];//标记对象rentMarker = new AMap.Marker({map: map, //显示标记的地图title: address, //鼠标移动至标记时所显示的文字//标记图标地址icon: 'http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',//位置position: [geocode.location.getLng(), geocode.location.getLat()],});rentMarkerArray.push(rentMarker);//相关房源网络地址rentMarker.content ="<div>房源:<a target = '_blank' href='https://bj.58.com/pinpaigongyu/?key=" +address +"'>" +address +'</a><div>';//标记的事件处理rentMarker.on('click', function (e) {//设置信息窗体显示的内容infoWindow.setContent(e.target.content);infoWindow.open(map, e.target.getPosition());//路线规划是否清除if (amapTransfer) amapTransfer.clear();//换乘对象amapTransfer = new AMap.Transfer({map: map,policy: AMap.TransferPolicy.LEAST_TIME,city: '北京市',panel: 'transfer-panel',});//根据起、终点坐标查询换乘路线amapTransfer.search([{keyword: workAddress,},{keyword: address,},],function (status, result) {});});}});
}
//清除已有的到达区域
function delWorkLocation() {if (polygonArray) map.remove(polygonArray);if (workMarker) map.remove(workMarker);polygonArray = [];
}
//清除现有的房源标记
function delRentLocation() {if (rentMarkerArray) map.remove(rentMarkerArray);rentMarkerArray = [];
}
//加载1小时到达区域
function loadWorkLocation() {//清除已有的到达区域delWorkLocation();//创建地址坐标对象var geocoder = new AMap.Geocoder({city: '北京',radius: 1000,});//获取位置geocoder.getLocation(workAddress, function (status, result) {if (status === 'complete' && result.info === 'OK') {var geocode = result.geocodes[0]; //获取地址编码x = geocode.location.getLng(); //经度y = geocode.location.getLat(); //纬度//加载工作地点标记loadWorkMarker(x, y);//加载工作地点1小时内到达的范围loadWorkRange(x, y, 60, '#3f67a5', vehicle);//地图移动到工作地点的位置map.setZoomAndCenter(12, [x, y]);}});
}
//加载房源位置
function loadRentLocationByFile(fileName) {//清除现有的房源标记delRentLocation();//所有的地点都记录在集合中var rent_locations = new Set();//获取文件中的房源信息$.get(fileName, function (data) {//分割信息data = data.split('\n');//遍历房源位置data.forEach(function (item, index) {rent_locations.add(item.split(',')[1]);});rent_locations.forEach(function (element, index) {//加上房源标记addMarkerByAddress(element);});});
}
代码中已经给出了大部分注释,这里对小部分进行特别说明:
添加标尺,参考带功能控件的地图。
var scale = new AMap.Scale();
map.addControl(scale);
一些需要放到全局的变量:
//公交到达圈对象
var arrivalRange = new AMap.ArrivalRange();
//经度,纬度,时间(用不到),通勤方式(默认是地铁+公交)
var x,y,t,vehicle = 'SUBWAY,BUS';
//工作地点,工作标记
var workAddress, workMarker;
//房源标记队列
var rentMarkerArray = [];
//多边形队列,存储公交到达的计算结果
var polygonArray = [];
//路径规划
var amapTransfer;
信息窗体的使用,在房源标记被点击时弹出,参考给多个点添加信息窗体。
//信息窗体对象
var infoWindow = new AMap.InfoWindow({offset: new AMap.Pixel(0, -30),
});
//在房源标记被点击时打开
rentMarker.on('click', function (e) {//鼠标移到标记上会显示标记content属性的内容infoWindow.setContent(e.target.content);//在标记的位置打开窗体infoWindow.open(map, e.target.getPosition());
});
地址补完的使用,参考输入提示后查询。
var auto = new AMap.Autocomplete({//通过id指定输入元素input: 'work-location',
});
//添加事件监听,在选择补完的地址后调用workLocationSelected
AMap.event.addListener(auto, 'select', workLocationSelected);
copy
function workLocationSelected(e) {//更新工作地点,加载公交到达圈workAddress = e.poi.name;loadWorkLocation();
}
loadWorkLocation 的实现,这部分包含了地理编码的内容,参考正向地理编码(地址-坐标)。
function loadWorkLocation() {//首先清空地图上已有的到达圈delWorkLocation();var geocoder = new AMap.Geocoder({city: '北京',radius: 1000,});geocoder.getLocation(workAddress, function (status, result) {if (status === 'complete' && result.info === 'OK') {var geocode = result.geocodes[0];x = geocode.location.getLng();y = geocode.location.getLat();//加载工作地点标记loadWorkMarker(x, y);//加载60分钟内工作地点到达圈loadWorkRange(x, y, 60, '#3f67a5', vehicle);//地图移动到工作地点的位置map.setZoomAndCenter(12, [x, y]);}});
}
loadWorkRange 的实现,在地图上绘制到达圈,参考:公交到达圈。
function loadWorkRange(x, y, t, color, v) {arrivalRange.search([x, y],t,function (status, result) {if (result.bounds) {for (var i = 0; i < result.bounds.length; i++) {//新建多边形对象var polygon = new AMap.Polygon({map: map,fillColor: color,fillOpacity: '0.4',strokeColor: color,strokeOpacity: '0.8',strokeWeight: 1,});//得到到达圈的多边形路径polygon.setPath(result.bounds[i]);polygonArray.push(polygon);}}},{policy: v,});
}
载入房源信息功能的实现。由于安全问题,浏览器想要得到文件在系统内的位置就得用上一些技巧,这里还是算了,偷一下懒,因为房源文件跟 index.html 在同一个文件夹下,所以我们只要得到文件名就足够了。
function importRentInfo(fileInfo) {var file = fileInfo.files[0].name;loadRentLocationByFile(file);
}
我们使用一个集合来记录所有的房源地址。
function loadRentLocationByFile(fileName) {//先删除现有的房源标记delRentLocation();//所有的地点都记录在集合中var rent_locations = new Set();//jquery操作$.get(fileName, function (data) {data = data.split('\n');data.forEach(function (item, index) {rent_locations.add(item.split(',')[1]);});rent_locations.forEach(function (element, index) {//加上房源标记addMarkerByAddress(element);});});
}
addMarkerByAddress 的实现参考:按起终点名称规划路线与点标记。
注意其中这一句会被显示在信息窗体上。链接指向 58 品牌公寓馆的搜索页面,搜索的地址就是点标记(房源)的地址:
rentMarker.content ="<div>房源:<a target = '_blank' href='https://bj.58.com/pinpaigongyu/?key=" +address +"'>" +address +'</a><div>';
可直接下载最终代码使用:
wget https://labfile.oss.aliyuncs.com/courses/599/index.html
输入 python3 -m http.server 8080 打开服务器,浏览器访问 http://localhost:8080 查看效果。
首先选择工作地点:
划出了一小时内的通勤范围:
导入房源文件:
导入后:
选择一处房源,会自动帮你规划路径:
选中房源地址跳转到目标页面:
Python 实现高德地图找房相关推荐
- 高德地图找房 # 编程大实践 # Python # 嵩天 # cilay
高德地图找房 一.项目背景 1.高德开放平台 高德开放平台是国内技术领先的LBS服务提供商,拥有先进的数据融合技术和海量的数据处理能力. 服务超过三十万款移动应用,日均处理定位请求及路径规划数百亿次. ...
- python + 高德地图API实现地图找房
python + 高德地图API实现地图找房 项目简介:根据工作地点信息和58同城爬取的租房信息,通过高德地图进行显示,同时利用高德API自动规划房源到工作地点的通勤路线(公交+地铁) 项目仓库:ht ...
- 使用vue和高德地图,仿58地图找房pc端,且解决marker过多卡顿
使用vue和高德地图,仿58地图找房pc端,且解决marker过多卡顿 1.在index.html引入高德地图链接 https://webapi.amap.com/maps?v=1.4.15& ...
- 你还在为高德地图找不到门牌号等详细地址而烦恼吗?你还在等什么——Python调用高德地图API实现经纬度换算、地图可视化
Python调用高德地图API实现经纬度换算.地图可视化 前地图可视化的工具和函数比较多,但是在不知道相关地点经纬度的情况下,通过python调用高德地图API实现经纬度换算,并且直接在高德地图新推出 ...
- python爬取贝壳找房之北京二手房源信息
所用库 requests xpath解析库 multiprocessing多进程 pandas库用于保存csv文件 实战背景 本文首发于:python爬取贝壳找房之北京二手房源信息 主要是为了做北京二 ...
- Java开发导入腾讯地图描点_腾讯地图点聚合开发-实现地图找房功能
链家实现的效果 分析 链家的地图找房主要分为三层.第一层为市区层,比如南山.罗湖等:第二层为片区,比如南头.科技园等:第三层则为小区. 因为第一层,第二层的数据没有那么多,这两个接口都是把所有的数据一 ...
- 百度地图——地图找房功能
代码地址:https://github.com/huiyan-fe/BMapGLLib HouseSearchService层 package cn.itcast.baidumap. ...
- Python调用高德地图API实现经纬度换算、地图可视化
作者 | 糖甜甜甜 出品 | 经管人学数据分析 Python调用高德地图API实现经纬度换算.地图可视化 前地图可视化的工具和函数比较多,但是在不知道相关地点经纬度的情况下,通过python调用高德地 ...
- 腾讯地图实现地图找房功能
链家实现的效果 最近接到一个需求,需要使用鹅厂地图实现类似链家网的地图找房功能,然后我去网上看了一下,基本上使用的都是百度地图.于是我打算自己稍微封装一下,可以在使用的时候更加的方便. 01. 分析 ...
最新文章
- 【MyBatis】MyBatis分页插件PageHelper的使用
- REST架构下,浏览器怎么发送put与delete请求?
- C++静态联编与动态联编
- 商城系统学习总结(1)——订单与库存在高并发场景下案例解析
- 资源丨用PyTorch实现Mask R-CNN
- 《马化腾:领跑互联网》— 综合素质提升书籍
- Python中用冒号表达式对列表切片
- bootdo jar 改war 坑
- php会员代码大全,PHP在线获取VIP会员账号API接口代码
- 团队组成五个基本要素_团队构成的五大要素
- 如何在linux系统下的抓包文件保存在u盘里,在xp系统中打开,【Linux】Wireshark在Linux系统下的安装及使用...
- session保存和清除cookies
- android实现延时的方法,Android实现延时总结
- JavaScript学习第十九天
- 同时使用scanf()函数和getchar()函数无法输入字符串的问题
- 数据库sql简单的优化方案
- 尚硅谷大数据Hadoop(26)P100-P104编写Reduce类型,,WordCounter,运行,程序调试
- 如何完全卸载oracle
- java实现“两数之和”
- oracle如何删除重复数据保留第一条记录