链家小区均价数据爬取

最近有个需求,需要了解市场上小区的均价,于是试着写了个爬虫把链家上小区的信息爬取了一下。这次的爬虫任务比较简单,数据量不大,爬取链接中也没加密字段等反爬取策略,感觉还挺适合作为爬虫入门的例子。下载数据主要利用python requests库来完成,解析html页面用到xpath,下面分享些技巧和经验。

  1. 开发环境:
    python3.6
    pip install requests
    pip install lxml
  2. 浏览器:
    Chrome

1. 网站分析

链家网上有小区专门的页面,域名格式为: 城市代码.lianjia.com/xiaoqu/城区代码/页码,期中页码格式为pg1、pg2这样,如果不加页码,则默认显示第1页数据。比如下面显示北京西城区的小区情况,可以看到有1716个小区,小区的名字、均价等信息都有。页面下方有页码。通过更改城市代码、城区名以及页码,就可以获取对应的小区信息。

如果想要下载全国城市小区价格信息,大致分为3个步骤:

  1. 获取城市代码
  2. 获取每个城市的城区代码
  3. 获取某城区所有页码数,拼接url, 下载具体某个城市的某城区信息

下面按照3, 2, 1的顺序讲解,先把一个城区的数据搞下来,再想办法扩展到一个城市和全国范围。

2. 下载城区数据

1. 定位数据位置

浏览器选择Chrome,打开Chrome开发者工具(在页面上单击右键选择检查菜单),依次点击Network栏,Doc栏,会显示本次网络请求到的页面,点击左下Name窗口中的条目,即可在右侧窗口中看到此页面的相关内容,如Headers(请求头)、Preview(预览)、Response(返回内容)、Cookies、Timing。点击Preview可以看到所需要的数据就包含在一个静态页面中,所以只要把这个页面下载下来,就可以提取其中的数据了。

爬虫任务的第一步就是要明确自己需要的数据在哪里。通常在页面上看到的早已渲染好的数据在后端会以两种方式传递给前端:

  1. 一种即是本例中,数据是早已嵌在页面中。通常这种情况数据在Doc中可以看到;
  2. 另一种情况中,页面结构和数据是分离的,数据通常以json格式通过ajax异步请求返回给客户端,再通过js程序加载到页面结构中。这种情况下数据可以在XHR栏看到。

想要定位自己所需的数据在哪,需要借助浏览器的开发者工具,分析一次请求的过程都发生了什么,先看看DocXHR栏里的内容,依次点点每个请求内容(Name栏中的条目),其中Doc栏中的内容在预览情况下比较容易看,就是一个页面,而XHR栏下的通常是json格式的数据,就需要好好和页面上渲染好的数据作比较了。

2. 下载数据

接下来我们就可以开始写代码爬数据了。
首先定义一个Spider类:

class Spider(object):

    def __init__(self, city):
        self.city = city
        self.domain = 'https://%s.lianjia.com' % city

构造方法接收一个城市代码,在本例中即是bj,构造方法中为成员变量domain赋值。
接下来定义一个download_page方法,用于下载一个页面。

def download_page(self, url, file_out):
    res = requests.get(url)
    content = res.text
    with open(file_out, 'w') as fo:
        fo.write(content)

注意参数url格式为self.domain/xiaoqu/城区代码/页码。
编写一个main方法运行试验下,这里先将城区和页码固定:

def main():
    city = 'bj'
    spider = Spider(city)
    page_file = 'test.html'
    residence = 'xicheng'
    page = 'pg2'
    url = '%s/xiaoqu/%s/%s' % (spider.domain, residence, page)
    spider.download_page(url, page_file)

运行后可以看到当前目录下多了一个test.html

将每次网络请求的数据保存下来是个好习惯,有以下好处:

  1. 后续开发解析模块可以利用这些文件,不必重新请求数据,减少时延,节省时间,同时也能减少请求次数,防止被ban;
  2. 方便排查问题,如果程序上线了结果解析出错,可以查看文件内容,看看是网站改版了还是请求的页面错误等。

3. 解析数据

1. 找到节点xpath

解析用到xpath,开发者工具对xpath支持很好。先看下面的例子:


按照步骤操作后可以获得一个节点的xpath。在上例中,可以获得价格的xpath:

'/html/body/div[4]/div[1]/ul/li[1]/div[2]/div[1]/div[1]/span'

通过开发者工具的Console栏可以方便地验证xpath:

在Console栏中的命令行输入$x()方法,将上面复制的xpath粘贴进去,$x()方法接收xpath路径作为参数,返回对应的节点列表,通过[0]选择第1个节点,节点的innterText变量保存了对应的内容。通过更改xpath,就可以获取各个小区的名字和价格等信息了。

2. 优化xpath

上面通过开发者工具获取的xpath是一条绝对路径,可以看到路径上有很多节点,如果以后网站改版了,路径上的某个节点改变了,那这条xpath就不能指向正确的位置了。所以通常可以利用节点的相对位置和属性等来写出更友好的xpath,比如我将上面4条xpath改写一下:


同样获取到了需要的数据,而且此时看xpath也容易理解,比如第1条取小区名,是取class="listContent"的ul下,第1个li下,class="title"的div下,a标签的内容;如果想要取价格,就把div的class改为totalPrice,a标签换成span。这比绝对路径上一堆节点好理解多了。

$x('//ul[@class="listContent"]/li[1]//div[@class="title"]/a')[0].innerText
$x('//ul[@class="listContent"]/li[1]//div[@class="totalPrice"]/span')[0].innerText
$x('//ul[@class="listContent"]/li[2]//div[@class="title"]/a')[0].innerText
$x('//ul[@class="listContent"]/li[2]//div[@class="totalPrice"]/span')[0].innerText

Chrome的命令行提供了很多方便的工具,$x()即是其一,利用好了事半功倍,更多知识请参见Chrome命令行

3. python代码

在Spider类中添加parse方法:

def parse(self, page_file):
    with open(page_file) as f:
        content = f.read()
        selector = etree.HTML(content)
        # 获取li数量
        path = '//ul[@class="listContent"]/li'
        size = len(selector.xpath(path))
        # 遍历li节点
        for i in range(1, size + 1):
            li_path = '//ul[@class="listContent"]/li[%s]' % i
            # 获取小区名
            path = li_path + '//div[@class="title"]/a'
            name = selector.xpath(path)[0].text
            # 获取价格
            path = li_path + '//div[@class="totalPrice"]/span'
            price = selector.xpath(path)[0].text
            print(name, price)

运行试验下:

def main():
    city = 'bj'
    spider = Spider(city)
    page_file = 'test.html'
    residence = 'xicheng'
    page = 'pg2'
    url = '%s/xiaoqu/%s/%s' % (spider.domain, residence, page)
    # spider.download_page(url, page_file)
    spider.parse(page_file)

输出:

锦官苑 139610
马甸南村 140382
玺源台 99726
黄寺大街24号院 134787
新街口西里三区 111055
...

需要的数据就都有了!

3. 其他工作

至于如何按页码、按城区、按城市下载全国数据,代码都已经都实现了,也不难,有时间下回分解,中秋快乐!

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

推荐阅读更多精彩内容

  • 在财富与自由课的最后一天,我们做了一个练习,想象自己,所有的金钱目标都已经实现了,那么我想过的完美一天是什么样子的...
    陈敬Chris阅读 1,604评论 0 51
  • 意志力是什么?你是否曾经想过减肥,结果跑步一周就坚持不下去了。你是否曾经想过坚持写作,结果写了一个月就再也没动力写...
    安心1986阅读 345评论 0 4
  • 今年高考,河北省有一篇作文《感谢贫穷》获高分。乍看到题目,内心有一种隐隐的不适感。如同读《孔融让梨》一般的感觉。总...
    一枝寒影斜阅读 640评论 5 4