[译]Slack机器人是通过哪些技术帮我在旧金山找到公寓的?(上)

原文:How I built a Slack bot to help me find an apartment in San Francisco

译者:杰微刊兼职翻译巫明瀚    

作者:Vik Paruchuri

几个月前我从波士顿搬到湾区。普利亚 (我女朋友)和我听说过租房市场的各种各样的恐怖故事。实际上,在谷歌上搜索“如何在旧金山找到一间公寓”,会得到 许多页的建议,这也变相的说明了,找房子是一个痛苦的过程。


波士顿很冷,但在 SF(旧金山缩写) 找到一间公寓更是吓人

我们调查了业主所持有的房子,你要把所有的文件带齐,押金带上,对方才会考虑下你。我们一开始调查了很多东西,并得出了找到一间好的公寓得看时机的结论。一个房东随时可能因为某些原因有房源,对于其他人,第一个看到往往表示你能得到它。你必须快速找到它,看是否符合条件,然后跟房东安排一次看房。

我们在网上,像 Padmapper 和 LiveLovely,看到了很多推荐。但是他们都不是很好,他们基本都没有足够的信息,而且没办法放在一起比较。他们很多都缺失一些特定的信息,比如坐在的区域,合适的交通点。大部分湾区的公寓都会出现在Craigslist里,然后被别的站点爬到,但是我们也担心并不是所有的信息都被抓齐了,或者抓取并不够实时。

我们想要一种方法︰

1、我们希望接近实时的知道公寓出现在了Craigslist上。

2、过滤掉我们不喜欢的小区。

3、筛选出与附加的条件不匹配的公寓,比如靠近公交站。

4、聚合所有信息,一起评价。

5、很容易找到对应的房东。

认真考虑这一问题之后,我意识到我们可以解决与四个步骤︰

1、从 Craigslist 抓取所有的信息。

2、过滤掉不符合我们标准的信息。

3、把信息发到Slack团队聊天工具上,然后我们可以讨论和评价他们。

4、把整个过程变成一个持续的循环,并且部署到服务器上去。(所以它会持续运行)。

在这篇文章的其余部分中,我会介绍我们将构建的每一个组件,并且最终搭建一个Slack机器人(Bot)来帮助我们找到公寓。通过这个Bot,我们最终找到了一个价格合理(仅就SF而言),有一张我们喜欢的床的公寓,而且整个过程比我们想象中的要快得多。

** 如果你想要看看代码,你能在这里找到对应的链接和README.md文件**

第一步 — — 从 Craigslist 上抓取信息

建立bot 的第一步是从 Craigslist 上抓取信息。虽然 Craigslist 没有API,但是我们可以用python-craiglist包来获取到所有的帖子.

python-craiglist抓取网页所有的信息然后使用BeautifulSoup来提取相关的内容,最后变成结构化数据。最后的代码很短,值得一读。

金山的 Craigslist 公寓列表在https://sfbay.craigslist.org/search/sfc/apa 。 在以下代码中,我们将︰

1、导入 CraigslistHousing,这是在 python-craigslist 中的一个类。

2、初始化的类具有以下参数︰

1)site – 我们想抓的Craigslist站点。site 参数是URL的第一部分, 比如 https://sfbay.craigslist.org.

2)area – 我们想抓的子区域。 area是URL的最后的部分, 比如 https://sfbay.craigslist.org/sfc/, 只会查找位于旧金山的内容。

3)category – 我么希望查找的类型。category是搜索URL的最后一部分, 比如 https://sfbay.craigslist.org/search/sfc/apa 会列出所有的公寓。

4)filters – 我们希望的过滤器

3、max_price – 最高价格。

4、min_price – 最低价格。

5、通过get_result方法从Craigslist获得最近的结果,返回值是一个generator。

1)通过geotagged参数来给每个结果添加坐标。

2)通过limi参数来限制最大结果数为20。

3)通过newest参数来控制只得到最新的结果。

6、通过返回的generator来获得所有的结果,并且打印出来。

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。每个result都是一个词典,如下所示:

{'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'}

这里是对应字段的描述:

1、datetime – 房源发布的时间。

2、geotag – 房源对应的坐标。

3、has_image – 这个帖子里面是不是有图。

4、has_map – 房源里面是不是又底图。

5、id – Craigslist 房源对应的id。

6、name – Craigslist中出现的对应的名字。

7、price – 月租。

8、url – 查看完整信息的url。

9、where – 创建者填写的地址。

第二步——过滤结果

现在我们有办法能够从Cragislist拿到数据了,接下来我们只需要一个方法能够过滤出一部分,只看那些我们喜欢的信息。

通过地区过滤结果

当普利亚和我搜索公寓的时候,我们希望看下面几个地区的房子,包括:

1、San Francisco

1)Sunset

2)Pacific Heights

3)Lower Pacific Heights

4)Bernal Heights

5)Richmond

2、Berkeley

3、Oakland

1)Adams Point

2)Lake Merritt

3)Rockridge

4、Alameda

为了能定位地区,我们会给每个地区一个方形区间:

img2

上面的这个区间是通过BoundingBox创建的。别忘了在左下角的选项里面选择CSV来获得对应的坐标。

你也可以手动的通过查找位置的左下角和右上角来确定区域,这个可以通过类似Google地图之类的服务来完成。定义了几个区间之后,我们会创建一个区域和坐标点的映射字典:

BOXES = {

"adams_point": [

[37.80789, -122.25000],

[37.81589, -122.26081],

],

"piedmont": [

[37.82240, -122.24768],

[37.83237, -122.25386],

],

...

}

字典的每一个键都是一个区域的名字,每一个键对应的值都是一个二级列表。里层列表的一个是左下角的坐标,第二个是右上角的坐标。我们可以通过对应的信息是否落在坐标之内来过滤信息。

下面的代码会

1.遍历BOXED的值。

2.看结果是不是在对应的区间内。

3.设置对应的变量。

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结果都会有对应的坐标。这取决一创建帖子的人是否提供了一个地点,因为在这之后坐标才会从地点推算出来。发帖的人对Cragslist越熟练,他发的帖子包含坐标的概率就越大。

一般来说代理商发布的链接,都会集中在几处,而且他们一般价格都更高些。房主发布的信息则可能没有坐标,但是却往往更值。因此能发现那些没有坐标的房源是否在我们希望的区域内是很有价值的。我们会创建一系列的区域关键词,然后用字符串匹配的方式来看结果是不是在里面。这个方法并没有用坐标那么精确,因为很多帖子的地区信息总有些问题,但是总比没有的好:

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

根据名字来过滤,我们需要遍历所有的NEIGHBORHOODS:

location = result["where"]

for hood in NEIGHBORHOODS:

if hood in location.lower():

area = hood

一旦结果被我们之前写的两段代码处理过后,我们就能除掉那些我们不感兴趣的地区的房源。虽然有几个漏网之鱼,或者误杀了一些没有地理信息的内容,但是大部分的内容都是我们想要的。

根据交通方便程度过滤结果

普利亚和我都经常到旧金山出差,因此我们希望,如果我们不住在旧金山的话,我们一定要住在交通方便的地方。在湾区,主要的公共交通系统是一个叫做BART的东西。BART是一种部分位于地底的城区之间的交通系统,它连接Oakland, Berkeley, San Francisco以及相邻的区域。

为了把这个功能集成到我们的Bot里,我们首先得定义一系列的交通站点。我们可以通过Google Maps获得这些站点的信息,然后创建一个字典:

TRANSIT_STATIONS = {

"oakland_19th_bart": [37.8118051,-122.2720873],

"macarthur_bart": [37.8265657,-122.2686705],

"rockridge_bart": [37.841286,-122.2566329],

...

}

每一个键都是一个站点的名字,然后和一个列表关联。列表就是对应的经纬度。我们有了这个列表之后就可以找对应的站点了。

下面的代码会:

1、遍历TRANSIT_STATIONS的每一个键

2、用coord_distance函数来找到两个坐标之间的千米距离,你可以在这里找到这个函数的具体解释

3、查看站点是否离房源近

1)如果站点太远(2km以上或者1.2公里以上),过滤掉

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

在这之后,我们就知道最近的站点了。

未完待续。。。。。。

精彩内容推荐:

[译]NGINX高性能缓存实践(上)

[译]NGINX高性能缓存实践(中)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容