数据API 案例 开发者 关于
掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务
新闻动态 > 媒体报道

我做了个Slack聊天机器人帮我在旧金山租房子

几个月以前,我从波士顿搬到了湾区。我和女朋友Priya在搬家之前,听到了很多有关在旧金山租房有多么困难的故事。如果你在谷歌上搜索如果在旧金山找房子,你会看到有人专门制作攻略,你就知道在这里租房有多难了。

网上的介绍说,房东会公布租房信息,然后你自己要带着各种文书来找房东,而且要愿意在决定租下这间房屋之前就交付定金,这样才能让房东考虑你。在通过研究之后,我们发现找房子最重要的因素就是时机。虽然一些房东希望把房子租出去之前多见几个求租人。但是大多数情况下,如果你能成为房东见到的第一个人,那么你租下这间房子的可能性就会很大。你需要先找到房源信息,快速判断房子是否符合你的要求,然后马上给房东打电话安排看房。

我们看了几个房屋租赁网站,例如PadmapperLiveLovely等,但是这些网站都无法给我们提供实时的房源。而且这些网站上的房源都没有提供额外的信息,例如具体的地理位置、与地铁和公交站的距离等。其实,大多数的湾区房源都是最早出现在Craigslist上的,然后其他的网站会爬取上面的信息,因此其他这些网站所提供的都不是第一手的信息。

我们希望可以:

Craigslist上有新房源信息的时候,立刻获得实时通知

自动过滤那些不符合我们要求的社区的房源

自动过滤那些不符合我们对其他要求的房源,例如离公共交通太远的房源

合并所有房源,进行总体比较

在确定目标房源之后,可以轻松的联系房东

在考虑之后,我意识到我能够用以下4个步骤解决问题:

Craigslist上调取房源信息

过滤不符合要求的房源

在团队聊天工具Slack上显示房源,供我们进行讨论和排名

将整个过程包裹在一个循环loop中,并且将其部署在一台服务器中(让它一直运行)

在这篇文章中,我将会介绍我开发的这个聊天机器人,以及我们是如何运用它来成功找房的。我和Priya使用这个机器人找到了一个价格合理(相对旧金山整体房价来说)的一居室,整个过程只用了大约一周的时间,比我们预想的要短很多。

如果你想要看看代码,你可以点击这里查看,你也可以点击这里查看README.md文档。

第一步:在Craigslist上爬取房源


打造这个机器人的第一步,就是从Craigslist上获取房源信息。不幸的是,Craigslist并没有API,大师我们可以使用python-craiglist包来获取数据。python-craigslist会获取页面的内容,然后使用BeautifulSoup从页面中提取相关信息,并且将其转换为结构数据。这个包的代码非常短,值得你去详细阅读。

Craigslist上,旧金山地区的房源信息列表位于https://sfbay.craigslist.org/search/sfc/apa这个位置。在下面的代码中,我们将会:

导入CraigslistHousing,这是python-craigslist的一个类

用下面的argument对类进行初始化:

     ◦ 站点——我们想要爬取的Craigslist站点。站点就是URL的第一部分,例如http://sfbay.craigslist.org

     ◦ 区域——站点内我们想要爬取的子区域。区域是URL中的最后一部分,例如 https://sfbay.craigslist.org/sfc/,这让我们可以只关注旧金山的房源

     ◦ 类别——我们想要寻找的房源类别。类别是搜索URL中的最后一个部分,例如https://sfbay.craigslist.org/search/sfc/apa,它会列出所有房源

     ◦ 过滤器——我们想在结果中使用的任何过滤条件

最大价格——你预期价格的最大值

最小价格——你预期价格的最小值

使用get_results方法从Craigslist上获取结果

     ◦ 通过地理标记(geotaggedargument,尝试给每一个结果添加坐标

     ◦ 通过限制(limitargument只显示20个结果

     ◦ 通过最近(newestargument,只显示最新的房源

在结果生成器中获取每一条结果,并且进行显示。

 from craigslist import CraigslistHousing

cl = CraigslistHousing(site='sfbay', area='sfc', category='apa',

                         filters={'max_price': 2000, 'min_price': 1000})

results = cl.get_results(sort_by='newest', geotagged=True, limit=20)

for result in results:

    print result

机器人打造的第一步马上就完成了!我们现在可以在Craigslist上获取房源数据了。每一条结果都包含以下几个field

 {'datetime': '2016-07-20 16:39',

 'geotag': (37.783166, -122.418671),

 'has_image': True,

 'has_map': True,

 'id': '5692904929',

 'name': 'Be the first in line at Brendas restaurant!SQuiet studio available',

 'price': '$1995',

 'url': 'http://sfbay.craigslist.org/sfc/apa/5692904929.html',

 'where': ‘tenderloin'}

以下是这些field的描述:

• datetime – 房源的上传时间

• geotag –  房源的位置坐标

• has_image – 该房源在Craigslist上是否有图片

• has_map – 房源是否有地图信息

• id – 该房源在Craigslist上的唯一ID

• name – 该房源在Craigslist上的名称

• price – 房租单月价格

• url – 查看房源完整信息的URL

• where – 房源创建者对房屋位置的描述

第二步:过滤结果


现在我们已经可以从Craigslist上获取信息了,现在我们需要对这些房源进行过滤,只显示那些符合要求的房源。

根据位置过滤结果


Priya和我在寻找房子的时候,我们限定了几个区域,其中包括:

旧金山

     ◦ Sunset

     ◦ Pacific Heights

     ◦ Lower Pacific Heights

     ◦ Bernal Heights

     ◦ Richmond

伯克利

奥克兰

     ◦ Adams Point

     ◦ Lake Merritt

     ◦ Rockridge

阿拉米达

为了可以根据社区过滤,我们首先需要定义这些区域在地图上的位置:


上图中的区域框是使用BoundingBox创建的。使用的时候注意要使用左下角的CSV选项,这样才能获得框内的坐标。

你也可以使用Google Maps等工具定义坐标。在定义了范围框之后,我们还要创建社区和坐标的dictionary

 BOXES = {

    "adams_point": [

        [37.80789, -122.25000],

        [37.81589, -122.26081],

    ],

    "piedmont": [

        [37.82240, -122.24768],

        [37.83237, -122.25386],

    ],

    ...

} 

dictionary中的每一个key都是社区的名称,每一个key都含有房源的列表。第一个内部列表是范围框左下角的坐标,第二个是右上角的坐标。之后,我们就可以通过查看一个坐标点是否位于范围框之内,来对结果进行过滤。

下面的代码将会:

在范围框内对每一个key进行循环

查看结果是否位于范围框之内

如果位于范围框内,则制定合适的变量

 def in_box(coords, box):

    if box[0][0] < coords[0] < box[1][0] and box[1][1] < coords[1] < box[0][1]:

        return True

    return False

geotag = result["geotag"]

area_found = False

area = ""

for a, coords in BOXES.items():

    if in_box(geotag, coords):

        area = a

        area_found = True

不过,并非所有Craigslist上的结果都有坐标。在发布信息的时候,并不是每一个发布人都会选择使用坐标。

一般来说,只有代理人发布的高价房源才有坐标信息,房东直租的房源则没有坐标,但是房东直接发布的房源价格更合理。因此,我需要找到一种方式,来确定那些没有坐标信息的房源是否位于我的目标社区内。我们将会创建一个社区的列表,然后通过串匹配来查看房源是否符合要求。这样做的精确度不如使用坐标,因为很多房源显示的社区名称都不太准确,但是总比什么都没有要强:

 NEIGHBORHOODS = ["berkeley north", "berkeley", "rockridge", "adams point", ... ]

为了完成这种基于名称的匹配,我们可以在每一个NEIGHBORHOODS中进行循环:

 location = result["where"]

for hood in NEIGHBORHOODS:

    if hood in location.lower():

        area = hood 

完成上面两部之后,我们就可以移除那些不符合要求的房源了。当然,也有一些符合要求的房源,由于缺少社区信息和位置信息而无法显示,但是这个系统可以找到大多数符合要求的房源。

根据离公共交通距离过滤


在搬来之前,我们就意识到我们要在住处和旧金山城里通勤。因此,如果我们不住在城里,就一定要住在离公共交通近的地方。在湾区,最重要的通勤方式就BART,也就是这里的地铁,它连接了奥克兰、伯克利、旧金山和附近其他区域。

为了给聊天机器人添加这个功能,我们首先需要定义地铁站的列表。我们可以使用Google Maps找到地铁站的坐标,然后再创建一个dictionary

 TRANSIT_STATIONS = {

    "oakland_19th_bart": [37.8118051,-122.2720873],

    "macarthur_bart": [37.8265657,-122.2686705],

    "rockridge_bart": [37.841286,-122.2566329],

    ...

}

每一个key都是地铁站的名称,它还有一个相关的列表。这个列表包含了地铁站的经纬度。在创建了dictionary之后,我们就可以计算每一个房源结果离地铁站的距离了。

下面的代码将会:

TRANSIT_STATIONS中循环每一个keyitem

使用coord_distance函数计算两对坐标间的距离

查看哪个地铁站离房源最近

     ◦ 如果地铁站太远(超过2公里),则忽略该地铁站

     ◦   如果另一地铁站距离小于前一个地铁站,则使用这个地铁站

 min_dist = None

near_bart = False

bart_dist = "N/A"

bart = ""

MAX_TRANSIT_DIST = 2 # kilometers

for station, coords in TRANSIT_STATIONS.items():

    dist = coord_distance(coords[0], coords[1], geotag[0], geotag[1])

    if (min_dist is None or dist < min_dist) and dist < MAX_TRANSIT_DIST:

        bart = station

        near_bart = True

    if (min_dist is None or dist < min_dist):

        bart_dist = dist

做完这一步之后,我们就能够知道离房源最近的地铁站在哪里了。

第三步:创建Slack机器人


设置


在过滤了结果之后,我们就可以将这些东西上传到Slack上了。如果你对Slack还不熟悉,简单说,它就是一个团队聊天程序。你在Slack中创建一个团队,然后邀请其他成员。每一个Slack成员都有多个channel,在这些channel中成员可以进行信息交流。每一条信息都可以被channel中的其他成员进行注释,例如添加赞或其他表情。

在将结果上传到Slack上之后,我就可以和其他人一起在房源中进行挑选了。要想做到这一点,我们需要:

创建Slack团队

创建channel,将房源上传到此。推荐你将channel的名称命名为#housing

获得Slack API token

完成这几步之后,我们就可以来写代码了。

写代码


在获得了正确的channel名称和token之后,我们就可以将房源列表上传到Slack上了。我们将会使用python-slackclient,这是一个Python包,让我们可以轻松使用Slack APIpython-slackclient默认使用Slack token,之后让我们可以使用很多用来管理团队和消息的API端点。

下面的代码将会:

使用SLACK_TOKEN.初始化SlackClient

从包含所有信息(例如价格、社区、和URL等)的结果中创建信息串

使用用户名pybot将信息上传至Slack

 from slackclient import SlackClient

SLACK_TOKEN = "ENTER_TOKEN_HERE"

SLACK_CHANNEL = "#housing"

sc = SlackClient(SLACK_TOKEN)

desc = "{0} | {1} | {2} | {3} | <{4}>".format(result["area"], result["price"], result["bart_dist"], result["name"], result["url"])

sc.api_call(

    "chat.postMessage", channel=SLACK_CHANNEL, text=desc,

    username='pybot', icon_emoji=':robot_face:'

)

在所有东西都连接好之后,Slack机器人会将房源上传到Slack上:

 

第四步:操作


现在我们已经把所有基本工作都做好了,现在要做的就是让代码持续的运行下去。我们要让Slack实时显示房源。我们需要完成以下几个步骤:

在数据库中储存房源,这样就不用把信息副本发送到Slack

从余下的代码中分理出设置,例如SLACK_TOKEN,让他们更容易调整

创建一个可以持续运行的loop,让它24/7的工作

储存房源


首先,我们需要使用一个叫做SQLAlchemyPython库来储存房源信息。SQLAlchemy是一个Object Relational MapperORM),它让我们可以更轻松的在Python内使用数据库。

使用SQLAlchemy,我们可创建一个数据库图表,这个图表会储存房源信息,我们还能用它创建一个数据库连接,让我们轻松的在图表中添加数据。

我们要将SQLAlchemySQLite数据库引擎配合使用,这个数据库将会把我们的数据储存到一个单一的文件夹中:listings.db

下面的代码将会:

导入SQLAlchemy.

创建一个指向SQLite数据库listings.db的连接

定义一个名为Listing的图表,里面包含Craigslist房源中所有相关的field

     ◦ 独特的field cl_id和连接能避免我们将副本房源上传到Slack

在连接中创建一个数据库session,它让我们能储存房源

 from sqlalchemy import create_engine

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean

from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///listings.db', echo=False)

Base = declarative_base()

class Listing(Base):

    """

    A table to store data on craigslist listings.

    """

    __tablename__ = 'listings'

    id = Column(Integer, primary_key=True)

    link = Column(String, unique=True)

    created = Column(DateTime)

    geotag = Column(String)

    lat = Column(Float)

    lon = Column(Float)

    name = Column(String)

    price = Column(Float)

    location = Column(String)

    cl_id = Column(Integer, unique=True)

    area = Column(String)

    bart_stop = Column(String)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)

session = Session() 

这样数据库模式就有了,我们要将每一条房源都储存在数据库中。

从代码中分离配置


下一步,就是要从代码中分离配置。我们要创建一个名为settings.py的文件,用来储存配置。

我们要将以下设置移入settings.py中:

• MIN_PRICE – 最低的预期价格

• MAX_PRICE – 最高的预期价格

• CRAIGSLIST_SITE – Craigslist网站上的目标区域站点

• AREAS – 网站上的目标区域站点列表

• BOXES – 社区的坐标范围框

• NEIGHBORHOODS – 如果列表没有坐标,社区的列表会被调用

• MAX_TRANSIT_DIST – 房源离地铁的最大距离

• TRANSIT_STATIONS – 地铁站的坐标

• CRAIGSLIST_HOUSING_SECTION – Craigslist上的目标子页面

• SLACK_CHANNEL – 我们希望机器人发布房源结果的Slack channel

我们还需要创建一个名为private.py的文件,这个文件会被git忽略,它需要包含以下key

• SLACK_TOKEN – 发布至Slack团队的token

你可以在这里看到完成后的settings.py文件。

创建循环


最后,我们需要创建一个循环,让代码持续运行。下面的代码将会:

在命令行中被调用的时候:

     ◦ 显示包含当前时间的状态信息

     ◦ 通过调用do_scrape函数运行Craigslist爬取代码

     ◦ 当用户输入Ctrl + C的时候退出

     ◦ 通过显示tracebackcontinuing处理其他例外

     ◦ 如果没有其他例外,则显示成功消息

     ◦ 在爬取间隙让系统休眠。默认情况下,爬取间隙为20分钟

 from scraper import do_scrape

import settings

import time

import sys

import traceback

if __name__ == "__main__":

    while True:

        print("{}: Starting scrape cycle".format(time.ctime()))

        try:

            do_scrape()

        except KeyboardInterrupt:

            print("Exiting....")

            sys.exit(1)

        except Exception as exc:

            print("Error with the scraping:", sys.exc_info()[0])

            traceback.print_exc()

        else:

            print("{}: Successfully finished scraping".format(time.ctime()))

        time.sleep(settings.SLEEP_INTERVAL)

我们还需要在settings.py中添加SLEEP_INTERVAL,这样才能控制爬取间隙。

运行


现在所有代码都打包好了,下面就是要让Slack机器人运行起来了。

在本地计算机中运行


你可以在Github上找到这个项目。在README.md中,你会找到详细的安装指导。我还推荐你遵循Docker的指导。Docker这个工具能让你更轻松的创建和部署程序,并且让你更快速的在本地计算器中开始使用Slack机器人。

以下是使用Docker安装并运行Slack机器人的基本的步骤:

创建一个名为config的文件夹,然后将一个名为private.py的文件放入其中

     ◦ 你在private.py中定义的设置将会优先于settings.py中的设置

     ◦ 通过添加private.py中的设置,你可以自定义聊天机器人的行为

private.py中的设置定义新的值

     ◦ 例如,你可以在private.py中添加AREAS = [‘sfc'],实现只查看旧金山的房源

     ◦ 如果你想要将结果上传到名字不是housingSlack channel,你可以为SLACK_CHANNEL添加入口

     ◦ 如果你不想查看湾区的房源,你还需要将下列设置更新为最小值:

▪ CRAIGSLIST_SITE

▪ AREAS

▪ BOXES

▪ NEIGHBORHOODS

▪ TRANSIT_STATIONS

▪ CRAIGSLIST_HOUSING_SECTION

▪ MIN_PRICE

▪ MAX_PRICE

按照这些步骤安装Docker

若要用默认配置运行聊天机器人:

     ◦ docker run -d -e SLACK_TOKEN={YOUR_SLACK_TOKEN} dataquestio/apartment-finder

若要用自己的配置运行聊天机器人:

      ◦ docker run -d -e SLACK_TOKEN={YOUR_SLACK_TOKEN} -v {ABSOLUTE_PATH_TO_YOUR_CONFIG_FOLDER}:/opt/wwc/apartment-finder/config dataquestio/apartment-finder

部署聊天机器人


除非你打算24/7不关机,你需要将聊天机器人部署在服务器上,这样它才能不间断的工作。我们可以在DigitalOcean这个主机提供商那里创建一个服务器。在安装了Docker的情况下,DigitalOcean可以自动创建服务器。

DigitalOcean上创建了服务器之后,你可以ssh进服务器中,然后使用上面提到的Docker安装和使用说明。

下一步


完成上面的步骤之后,你就有了可以自己找房子的Slack聊天机器人了。使用这个机器人,Priya和我在旧金山找到了好房子,价格虽然说不上便宜,但是比旧金山一般的一居室要便宜一些。找房所花费的时间远远低于我们的预期。但是,虽然这个聊天机器人满足了我们的需求,但是如果你需要的话,也可以使用一些扩展来改善这个聊天机器人:

使用Slack上的赞和踩功能,并且使用机器学习对聊天机器人进行训练

使用一个API自动调取地铁站的位置信息

添加房源附近的其他信息,例如公园、医院等

添加位置评分,或是其他社区质量评分,例如犯罪率

自动调取房东的电话或邮件

自动给房东打电话,并且安排看房日期

如果需要的话,你可以在Github上发起pull request,让这个聊天机器人更加完善。希望它也能帮你找到合适的房子!


掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务
新闻动态 > 媒体报道
我做了个Slack聊天机器人帮我在旧金山租房子
发布:2016-08-30

几个月以前,我从波士顿搬到了湾区。我和女朋友Priya在搬家之前,听到了很多有关在旧金山租房有多么困难的故事。如果你在谷歌上搜索如果在旧金山找房子,你会看到有人专门制作攻略,你就知道在这里租房有多难了。

网上的介绍说,房东会公布租房信息,然后你自己要带着各种文书来找房东,而且要愿意在决定租下这间房屋之前就交付定金,这样才能让房东考虑你。在通过研究之后,我们发现找房子最重要的因素就是时机。虽然一些房东希望把房子租出去之前多见几个求租人。但是大多数情况下,如果你能成为房东见到的第一个人,那么你租下这间房子的可能性就会很大。你需要先找到房源信息,快速判断房子是否符合你的要求,然后马上给房东打电话安排看房。

我们看了几个房屋租赁网站,例如PadmapperLiveLovely等,但是这些网站都无法给我们提供实时的房源。而且这些网站上的房源都没有提供额外的信息,例如具体的地理位置、与地铁和公交站的距离等。其实,大多数的湾区房源都是最早出现在Craigslist上的,然后其他的网站会爬取上面的信息,因此其他这些网站所提供的都不是第一手的信息。

我们希望可以:

Craigslist上有新房源信息的时候,立刻获得实时通知

自动过滤那些不符合我们要求的社区的房源

自动过滤那些不符合我们对其他要求的房源,例如离公共交通太远的房源

合并所有房源,进行总体比较

在确定目标房源之后,可以轻松的联系房东

在考虑之后,我意识到我能够用以下4个步骤解决问题:

Craigslist上调取房源信息

过滤不符合要求的房源

在团队聊天工具Slack上显示房源,供我们进行讨论和排名

将整个过程包裹在一个循环loop中,并且将其部署在一台服务器中(让它一直运行)

在这篇文章中,我将会介绍我开发的这个聊天机器人,以及我们是如何运用它来成功找房的。我和Priya使用这个机器人找到了一个价格合理(相对旧金山整体房价来说)的一居室,整个过程只用了大约一周的时间,比我们预想的要短很多。

如果你想要看看代码,你可以点击这里查看,你也可以点击这里查看README.md文档。

第一步:在Craigslist上爬取房源


打造这个机器人的第一步,就是从Craigslist上获取房源信息。不幸的是,Craigslist并没有API,大师我们可以使用python-craiglist包来获取数据。python-craigslist会获取页面的内容,然后使用BeautifulSoup从页面中提取相关信息,并且将其转换为结构数据。这个包的代码非常短,值得你去详细阅读。

Craigslist上,旧金山地区的房源信息列表位于https://sfbay.craigslist.org/search/sfc/apa这个位置。在下面的代码中,我们将会:

导入CraigslistHousing,这是python-craigslist的一个类

用下面的argument对类进行初始化:

     ◦ 站点——我们想要爬取的Craigslist站点。站点就是URL的第一部分,例如http://sfbay.craigslist.org

     ◦ 区域——站点内我们想要爬取的子区域。区域是URL中的最后一部分,例如 https://sfbay.craigslist.org/sfc/,这让我们可以只关注旧金山的房源

     ◦ 类别——我们想要寻找的房源类别。类别是搜索URL中的最后一个部分,例如https://sfbay.craigslist.org/search/sfc/apa,它会列出所有房源

     ◦ 过滤器——我们想在结果中使用的任何过滤条件

最大价格——你预期价格的最大值

最小价格——你预期价格的最小值

使用get_results方法从Craigslist上获取结果

     ◦ 通过地理标记(geotaggedargument,尝试给每一个结果添加坐标

     ◦ 通过限制(limitargument只显示20个结果

     ◦ 通过最近(newestargument,只显示最新的房源

在结果生成器中获取每一条结果,并且进行显示。

 from craigslist import CraigslistHousing

cl = CraigslistHousing(site='sfbay', area='sfc', category='apa',

                         filters={'max_price': 2000, 'min_price': 1000})

results = cl.get_results(sort_by='newest', geotagged=True, limit=20)

for result in results:

    print result

机器人打造的第一步马上就完成了!我们现在可以在Craigslist上获取房源数据了。每一条结果都包含以下几个field

 {'datetime': '2016-07-20 16:39',

 'geotag': (37.783166, -122.418671),

 'has_image': True,

 'has_map': True,

 'id': '5692904929',

 'name': 'Be the first in line at Brendas restaurant!SQuiet studio available',

 'price': '$1995',

 'url': 'http://sfbay.craigslist.org/sfc/apa/5692904929.html',

 'where': ‘tenderloin'}

以下是这些field的描述:

• datetime – 房源的上传时间

• geotag –  房源的位置坐标

• has_image – 该房源在Craigslist上是否有图片

• has_map – 房源是否有地图信息

• id – 该房源在Craigslist上的唯一ID

• name – 该房源在Craigslist上的名称

• price – 房租单月价格

• url – 查看房源完整信息的URL

• where – 房源创建者对房屋位置的描述

第二步:过滤结果


现在我们已经可以从Craigslist上获取信息了,现在我们需要对这些房源进行过滤,只显示那些符合要求的房源。

根据位置过滤结果


Priya和我在寻找房子的时候,我们限定了几个区域,其中包括:

旧金山

     ◦ Sunset

     ◦ Pacific Heights

     ◦ Lower Pacific Heights

     ◦ Bernal Heights

     ◦ Richmond

伯克利

奥克兰

     ◦ Adams Point

     ◦ Lake Merritt

     ◦ Rockridge

阿拉米达

为了可以根据社区过滤,我们首先需要定义这些区域在地图上的位置:


上图中的区域框是使用BoundingBox创建的。使用的时候注意要使用左下角的CSV选项,这样才能获得框内的坐标。

你也可以使用Google Maps等工具定义坐标。在定义了范围框之后,我们还要创建社区和坐标的dictionary

 BOXES = {

    "adams_point": [

        [37.80789, -122.25000],

        [37.81589, -122.26081],

    ],

    "piedmont": [

        [37.82240, -122.24768],

        [37.83237, -122.25386],

    ],

    ...

} 

dictionary中的每一个key都是社区的名称,每一个key都含有房源的列表。第一个内部列表是范围框左下角的坐标,第二个是右上角的坐标。之后,我们就可以通过查看一个坐标点是否位于范围框之内,来对结果进行过滤。

下面的代码将会:

在范围框内对每一个key进行循环

查看结果是否位于范围框之内

如果位于范围框内,则制定合适的变量

 def in_box(coords, box):

    if box[0][0] < coords[0] < box[1][0] and box[1][1] < coords[1] < box[0][1]:

        return True

    return False

geotag = result["geotag"]

area_found = False

area = ""

for a, coords in BOXES.items():

    if in_box(geotag, coords):

        area = a

        area_found = True

不过,并非所有Craigslist上的结果都有坐标。在发布信息的时候,并不是每一个发布人都会选择使用坐标。

一般来说,只有代理人发布的高价房源才有坐标信息,房东直租的房源则没有坐标,但是房东直接发布的房源价格更合理。因此,我需要找到一种方式,来确定那些没有坐标信息的房源是否位于我的目标社区内。我们将会创建一个社区的列表,然后通过串匹配来查看房源是否符合要求。这样做的精确度不如使用坐标,因为很多房源显示的社区名称都不太准确,但是总比什么都没有要强:

 NEIGHBORHOODS = ["berkeley north", "berkeley", "rockridge", "adams point", ... ]

为了完成这种基于名称的匹配,我们可以在每一个NEIGHBORHOODS中进行循环:

 location = result["where"]

for hood in NEIGHBORHOODS:

    if hood in location.lower():

        area = hood 

完成上面两部之后,我们就可以移除那些不符合要求的房源了。当然,也有一些符合要求的房源,由于缺少社区信息和位置信息而无法显示,但是这个系统可以找到大多数符合要求的房源。

根据离公共交通距离过滤


在搬来之前,我们就意识到我们要在住处和旧金山城里通勤。因此,如果我们不住在城里,就一定要住在离公共交通近的地方。在湾区,最重要的通勤方式就BART,也就是这里的地铁,它连接了奥克兰、伯克利、旧金山和附近其他区域。

为了给聊天机器人添加这个功能,我们首先需要定义地铁站的列表。我们可以使用Google Maps找到地铁站的坐标,然后再创建一个dictionary

 TRANSIT_STATIONS = {

    "oakland_19th_bart": [37.8118051,-122.2720873],

    "macarthur_bart": [37.8265657,-122.2686705],

    "rockridge_bart": [37.841286,-122.2566329],

    ...

}

每一个key都是地铁站的名称,它还有一个相关的列表。这个列表包含了地铁站的经纬度。在创建了dictionary之后,我们就可以计算每一个房源结果离地铁站的距离了。

下面的代码将会:

TRANSIT_STATIONS中循环每一个keyitem

使用coord_distance函数计算两对坐标间的距离

查看哪个地铁站离房源最近

     ◦ 如果地铁站太远(超过2公里),则忽略该地铁站

     ◦   如果另一地铁站距离小于前一个地铁站,则使用这个地铁站

 min_dist = None

near_bart = False

bart_dist = "N/A"

bart = ""

MAX_TRANSIT_DIST = 2 # kilometers

for station, coords in TRANSIT_STATIONS.items():

    dist = coord_distance(coords[0], coords[1], geotag[0], geotag[1])

    if (min_dist is None or dist < min_dist) and dist < MAX_TRANSIT_DIST:

        bart = station

        near_bart = True

    if (min_dist is None or dist < min_dist):

        bart_dist = dist

做完这一步之后,我们就能够知道离房源最近的地铁站在哪里了。

第三步:创建Slack机器人


设置


在过滤了结果之后,我们就可以将这些东西上传到Slack上了。如果你对Slack还不熟悉,简单说,它就是一个团队聊天程序。你在Slack中创建一个团队,然后邀请其他成员。每一个Slack成员都有多个channel,在这些channel中成员可以进行信息交流。每一条信息都可以被channel中的其他成员进行注释,例如添加赞或其他表情。

在将结果上传到Slack上之后,我就可以和其他人一起在房源中进行挑选了。要想做到这一点,我们需要:

创建Slack团队

创建channel,将房源上传到此。推荐你将channel的名称命名为#housing

获得Slack API token

完成这几步之后,我们就可以来写代码了。

写代码


在获得了正确的channel名称和token之后,我们就可以将房源列表上传到Slack上了。我们将会使用python-slackclient,这是一个Python包,让我们可以轻松使用Slack APIpython-slackclient默认使用Slack token,之后让我们可以使用很多用来管理团队和消息的API端点。

下面的代码将会:

使用SLACK_TOKEN.初始化SlackClient

从包含所有信息(例如价格、社区、和URL等)的结果中创建信息串

使用用户名pybot将信息上传至Slack

 from slackclient import SlackClient

SLACK_TOKEN = "ENTER_TOKEN_HERE"

SLACK_CHANNEL = "#housing"

sc = SlackClient(SLACK_TOKEN)

desc = "{0} | {1} | {2} | {3} | <{4}>".format(result["area"], result["price"], result["bart_dist"], result["name"], result["url"])

sc.api_call(

    "chat.postMessage", channel=SLACK_CHANNEL, text=desc,

    username='pybot', icon_emoji=':robot_face:'

)

在所有东西都连接好之后,Slack机器人会将房源上传到Slack上:

 

第四步:操作


现在我们已经把所有基本工作都做好了,现在要做的就是让代码持续的运行下去。我们要让Slack实时显示房源。我们需要完成以下几个步骤:

在数据库中储存房源,这样就不用把信息副本发送到Slack

从余下的代码中分理出设置,例如SLACK_TOKEN,让他们更容易调整

创建一个可以持续运行的loop,让它24/7的工作

储存房源


首先,我们需要使用一个叫做SQLAlchemyPython库来储存房源信息。SQLAlchemy是一个Object Relational MapperORM),它让我们可以更轻松的在Python内使用数据库。

使用SQLAlchemy,我们可创建一个数据库图表,这个图表会储存房源信息,我们还能用它创建一个数据库连接,让我们轻松的在图表中添加数据。

我们要将SQLAlchemySQLite数据库引擎配合使用,这个数据库将会把我们的数据储存到一个单一的文件夹中:listings.db

下面的代码将会:

导入SQLAlchemy.

创建一个指向SQLite数据库listings.db的连接

定义一个名为Listing的图表,里面包含Craigslist房源中所有相关的field

     ◦ 独特的field cl_id和连接能避免我们将副本房源上传到Slack

在连接中创建一个数据库session,它让我们能储存房源

 from sqlalchemy import create_engine

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean

from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///listings.db', echo=False)

Base = declarative_base()

class Listing(Base):

    """

    A table to store data on craigslist listings.

    """

    __tablename__ = 'listings'

    id = Column(Integer, primary_key=True)

    link = Column(String, unique=True)

    created = Column(DateTime)

    geotag = Column(String)

    lat = Column(Float)

    lon = Column(Float)

    name = Column(String)

    price = Column(Float)

    location = Column(String)

    cl_id = Column(Integer, unique=True)

    area = Column(String)

    bart_stop = Column(String)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)

session = Session() 

这样数据库模式就有了,我们要将每一条房源都储存在数据库中。

从代码中分离配置


下一步,就是要从代码中分离配置。我们要创建一个名为settings.py的文件,用来储存配置。

我们要将以下设置移入settings.py中:

• MIN_PRICE – 最低的预期价格

• MAX_PRICE – 最高的预期价格

• CRAIGSLIST_SITE – Craigslist网站上的目标区域站点

• AREAS – 网站上的目标区域站点列表

• BOXES – 社区的坐标范围框

• NEIGHBORHOODS – 如果列表没有坐标,社区的列表会被调用

• MAX_TRANSIT_DIST – 房源离地铁的最大距离

• TRANSIT_STATIONS – 地铁站的坐标

• CRAIGSLIST_HOUSING_SECTION – Craigslist上的目标子页面

• SLACK_CHANNEL – 我们希望机器人发布房源结果的Slack channel

我们还需要创建一个名为private.py的文件,这个文件会被git忽略,它需要包含以下key

• SLACK_TOKEN – 发布至Slack团队的token

你可以在这里看到完成后的settings.py文件。

创建循环


最后,我们需要创建一个循环,让代码持续运行。下面的代码将会:

在命令行中被调用的时候:

     ◦ 显示包含当前时间的状态信息

     ◦ 通过调用do_scrape函数运行Craigslist爬取代码

     ◦ 当用户输入Ctrl + C的时候退出

     ◦ 通过显示tracebackcontinuing处理其他例外

     ◦ 如果没有其他例外,则显示成功消息

     ◦ 在爬取间隙让系统休眠。默认情况下,爬取间隙为20分钟

 from scraper import do_scrape

import settings

import time

import sys

import traceback

if __name__ == "__main__":

    while True:

        print("{}: Starting scrape cycle".format(time.ctime()))

        try:

            do_scrape()

        except KeyboardInterrupt:

            print("Exiting....")

            sys.exit(1)

        except Exception as exc:

            print("Error with the scraping:", sys.exc_info()[0])

            traceback.print_exc()

        else:

            print("{}: Successfully finished scraping".format(time.ctime()))

        time.sleep(settings.SLEEP_INTERVAL)

我们还需要在settings.py中添加SLEEP_INTERVAL,这样才能控制爬取间隙。

运行


现在所有代码都打包好了,下面就是要让Slack机器人运行起来了。

在本地计算机中运行


你可以在Github上找到这个项目。在README.md中,你会找到详细的安装指导。我还推荐你遵循Docker的指导。Docker这个工具能让你更轻松的创建和部署程序,并且让你更快速的在本地计算器中开始使用Slack机器人。

以下是使用Docker安装并运行Slack机器人的基本的步骤:

创建一个名为config的文件夹,然后将一个名为private.py的文件放入其中

     ◦ 你在private.py中定义的设置将会优先于settings.py中的设置

     ◦ 通过添加private.py中的设置,你可以自定义聊天机器人的行为

private.py中的设置定义新的值

     ◦ 例如,你可以在private.py中添加AREAS = [‘sfc'],实现只查看旧金山的房源

     ◦ 如果你想要将结果上传到名字不是housingSlack channel,你可以为SLACK_CHANNEL添加入口

     ◦ 如果你不想查看湾区的房源,你还需要将下列设置更新为最小值:

▪ CRAIGSLIST_SITE

▪ AREAS

▪ BOXES

▪ NEIGHBORHOODS

▪ TRANSIT_STATIONS

▪ CRAIGSLIST_HOUSING_SECTION

▪ MIN_PRICE

▪ MAX_PRICE

按照这些步骤安装Docker

若要用默认配置运行聊天机器人:

     ◦ docker run -d -e SLACK_TOKEN={YOUR_SLACK_TOKEN} dataquestio/apartment-finder

若要用自己的配置运行聊天机器人:

      ◦ docker run -d -e SLACK_TOKEN={YOUR_SLACK_TOKEN} -v {ABSOLUTE_PATH_TO_YOUR_CONFIG_FOLDER}:/opt/wwc/apartment-finder/config dataquestio/apartment-finder

部署聊天机器人


除非你打算24/7不关机,你需要将聊天机器人部署在服务器上,这样它才能不间断的工作。我们可以在DigitalOcean这个主机提供商那里创建一个服务器。在安装了Docker的情况下,DigitalOcean可以自动创建服务器。

DigitalOcean上创建了服务器之后,你可以ssh进服务器中,然后使用上面提到的Docker安装和使用说明。

下一步


完成上面的步骤之后,你就有了可以自己找房子的Slack聊天机器人了。使用这个机器人,Priya和我在旧金山找到了好房子,价格虽然说不上便宜,但是比旧金山一般的一居室要便宜一些。找房所花费的时间远远低于我们的预期。但是,虽然这个聊天机器人满足了我们的需求,但是如果你需要的话,也可以使用一些扩展来改善这个聊天机器人:

使用Slack上的赞和踩功能,并且使用机器学习对聊天机器人进行训练

使用一个API自动调取地铁站的位置信息

添加房源附近的其他信息,例如公园、医院等

添加位置评分,或是其他社区质量评分,例如犯罪率

自动调取房东的电话或邮件

自动给房东打电话,并且安排看房日期

如果需要的话,你可以在Github上发起pull request,让这个聊天机器人更加完善。希望它也能帮你找到合适的房子!


×
企业用户认证,
可获得1000次免费调用
注册登录 > 企业账户认证 > 领取接口包
企业用户认证领取接口包 立即领取
× 企业用户认证,
可获得1000次免费调用,立即领取>
数 据 驱 动 未 来
Data Drives The Future