爬虫第六式:链家房源爬取

温馨提示:

爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。

《刑法》第 285 条,非法获取计算机信息系统数据罪。
       违反国家规定,侵入前款规定以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据,或者对该计算机信息系统实施非法控制,情节严重的,处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

正文:

确定是否为静态

	打开二手房页面 -> 查看网页源码 -> 搜索关键字

我们先看网页中的关键字都是什么:

在这里插入图片描述
然后我们去源代码中查看一下关键字是否存在:
在这里插入图片描述
可以看到关键字也是存在的,只不过,源代码有点乱,看不太好,反正关键字肯定是存在的

接下来我们写Xpath表达式:
xpath表达式
在这里插入图片描述
我们看到xpath写出来了,但是感觉不对吧,右边给出的数据是 120条,这肯定是不对,这个网页的数据是30条,显示出120,肯定是有其他的也包含了,我们只能让xpath匹配到具体到属性了:

在这里插入图片描述
我们这样写到具体的属性了,但是值匹配到了2个????这是为什么,如果有细心的人发现了li和每个li后面的属性是不对的,有的是 class=“clear LOGCLICKDATA” 有的是 class=“clear LOGVIEWDATA LOGCLICKDATA”,但是以往我要这样的话是不是就用或符号了  |  但是我想让大家看的是这样,我鼠标滚轮往下滚一段距离,大家再看截图的效果:
在这里插入图片描述
是不是所有的li的属性都变成了:class="clear LOGCLICKDATA",这样,这也就是我本篇文章想说的一个xpath的知识点:

重要:页面中xpath不能全信,一切以响应内容为主

重要:页面中xpath不能全信,一切以响应内容为主

重要:页面中xpath不能全信,一切以响应内容为主

也就是我们在写xpath表达式的时候,要以响应出来的内容为主,先没事滚动两下,看是否会变,在进行提取操作!!

xpath表达式

1】基准xpath表达式(匹配每个房源信息节点列表)
    '此处滚动鼠标滑轮时,li节点的class属性值会发生变化,通过查看网页源码确定xpath表达式'
    //ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]

我们再看提取的需求,楼房的信息提取表达式:

2】依次遍历后每个房源信息xpath表达式
   2.1)名称: .//div[@class="positionInfo"]/a[1]/text()
   2.2)地址: .//div[@class="positionInfo"]/a[2]/text()

在这里插入图片描述
这样我们把小区名称和地址就提取出来了,因为两个都是a标签,所以我们用到了中括号1、中括号2,来区别他们,因为我们要的每个下面的文本,所有我们保险旗舰,加了 /text()

下面就是又不一样了,所以我们从新在写xpath表达式:

2.3)户型+面积+方位+是否精装+楼层+年代+类型
       info_list: './/div[@class="houseInfo"]/text()' ->  [0].strip().split('|')
       a)户型: info_list[0]
       b)面积: info_list[1]
       c)方位: info_list[2]
       d)精装: info_list[3]
       e)楼层:info_list[4]
       f)年代: info_list[5]
       g)类型: info_list[6]

在这里插入图片描述

info_list: ‘.//div[@class=“houseInfo”]/text()’ -> [0].strip().split(’|’) 这句话,我们看其实他就是把 户型、面积、方位、是否精装、楼层、年代、类型,分开了,因为它网页就是一句话写的,我们使用split(’|’) 按照 | 分割,提取出我们要的一小部分一小部分的数据,方便我们看
户型、面积、方位、是否精装、楼层、年代、类型 分别对应列表里的0,1,2,3,4,5,6 所以我们使用列表数据的索引进行提取

再看总价和单价:

2.4)总价+单价
       a)总价: .//div[@class="totalPrice"]/span/text()
       b)单价: .//div[@class="unitPrice"]/span/text()

在这里插入图片描述
总价的数字和万字是分开的,我们也就不要了,心里知道反正全国各地的房价是万计数的嘛,对吧
单价正常提取就行

接下来我们写代码:

导入的包:

import requests
from lxml import etree
import time
import random
from fake_useragent import UserAgent

定义功能函数,减少重复代码

找到URL地址的规律:
https://bj.lianjia.com/ershoufang/pg1/ —— 第一页
https://bj.lianjia.com/ershoufang/pg2/ —— 第二页
https://bj.lianjia.com/ershoufang/pg3/ —— 第三页
… …

class LianjiaSpider(object):
    def __init__(self):
        self.url = 'https://bj.lianjia.com/ershoufang/pg{}/'

定义url请求函数

def parse_html(self,url):
    headers = {'User-Agent':UserAgent().random}
    html = requests.get(url=url,headers=headers).content.decode('utf-8','ignore')
    self.get_data(html)

定义爬虫功能请求函数

def get_data(self,html):
    p = etree.HTML(html)
    # 基准xpath: [<element li at xxx>,<element li>]
    # 得到每个房源信息的li节点对象列表,如果此处匹配出来空,则一定要查看响应内容
    li_list = p.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]')
    # for遍历,依次提取每个房源信息,放到字典item中
    item = {}
    for li in li_list:
        # 名称+区域
        name_list = li.xpath('.//div[@class="positionInfo"]/a[1]/text()')
        item['name'] = name_list[0].strip() if name_list else None
        address_list = li.xpath('.//div[@class="positionInfo"]/a[2]/text()')
        item['address'] = address_list[0].strip() if address_list else None
 # 户型+面积+方位+是否精装+楼层+年代+类型
 # h_list: ['']
 h_list = li.xpath('.//div[@class="houseInfo"]/text()')
 if h_list:
     info_list = h_list[0].split('|')
     if len(info_list) == 7:
         item['model'] = info_list[0].strip()
         item['area'] = info_list[1].strip()
         item['direct'] = info_list[2].strip()
         item['perfect'] = info_list[3].strip()
         item['floor'] = info_list[4].strip()
         item['year'] = info_list[5].strip()[:-2]
         item['type'] = info_list[6].strip()
     else:
         item['model'] = item['area'] = item['direct'] = item['perfect'] = item['floor'] = item['year'] = item['type'] = None
 else:
     item['model'] = item['area'] = item['direct'] = item['perfect'] = item['floor'] = item['year'] = item['type'] = None

这样写我们之前有提到过,是因为我们不确定是否所有我们提取的数据都是存在的,我们就先写出了简单的提取,默认他全有,然后我们 if、else判断一下,如果数据都有代码就执行if里面的,没有就执行else里面的,这样也就节省了我们代码量,最里层if语句是判断是不是执行到了户型+面积+方位+是否精装+楼层+年代+类型 这个板块,外面的是判断代码的数据是不是全有

下面是总价和单价的表达式: 我们同样做了为空判断

 # 总价+单价
 total_list = li.xpath('.//div[@class="totalPrice"]/span/text()')
 item['total'] = total_list[0].strip() if total_list else None
 unit_list = li.xpath('.//div[@class="unitPrice"]/span/text()')
 item['unit'] = unit_list[0].strip() if unit_list else None

最后程序入口函数,用来控制整体逻辑:

 def run(self):
 	 for pg in range(1,101):
         url = self.url.format(pg)
         self.parse_html(url)
         time.sleep(random.randint(1,2))

奉上全部代码:

import requests
from lxml import etree
import time
import random
from fake_useragent import UserAgent

class LianjiaSpider(object):
    def __init__(self):
        self.url = 'https://bj.lianjia.com/ershoufang/pg{}/'

    def parse_html(self,url):
        headers = {'User-Agent':UserAgent().random}
        html = requests.get(url=url,headers=headers).content.decode('utf-8','ignore')
        self.get_data(html)


    def get_data(self,html):
        p = etree.HTML(html)
        # 基准xpath: [<element li at xxx>,<element li>]
        li_list = p.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]')
        # for遍历,依次提取每个房源信息,放到字典item中
        item = {}
        for li in li_list:
            # 名称+区域
            name_list = li.xpath('.//div[@class="positionInfo"]/a[1]/text()')
            item['name'] = name_list[0].strip() if name_list else None
            address_list = li.xpath('.//div[@class="positionInfo"]/a[2]/text()')
            item['address'] = address_list[0].strip() if address_list else None
            # 户型+面积+方位+是否精装+楼层+年代+类型
            # h_list: ['']
            h_list = li.xpath('.//div[@class="houseInfo"]/text()')
            if h_list:
                info_list = h_list[0].split('|')
                if len(info_list) == 7:
                    item['model'] = info_list[0].strip()
                    item['area'] = info_list[1].strip()
                    item['direct'] = info_list[2].strip()
                    item['perfect'] = info_list[3].strip()
                    item['floor'] = info_list[4].strip()
                    item['year'] = info_list[5].strip()[:-2]
                    item['type'] = info_list[6].strip()
                else:
                    item['model'] = item['area'] = item['direct'] = item['perfect'] = item['floor'] = item['year'] = item['type'] = None
            else:
                item['model'] = item['area'] = item['direct'] = item['perfect'] = item['floor'] = item['year'] = item['type'] = None

            # 总价+单价
            total_list = li.xpath('.//div[@class="totalPrice"]/span/text()')
            item['total'] = total_list[0].strip() if total_list else None
            unit_list = li.xpath('.//div[@class="unitPrice"]/span/text()')
            item['unit'] = unit_list[0].strip() if unit_list else None

            print(item)

    def run(self):
        for pg in range(1,101):
            url = self.url.format(pg)
            self.parse_html(url)
            time.sleep(random.randint(1,2))

if __name__ == '__main__':
    spider = LianjiaSpider()
    spider.run()

运行:
在这里插入图片描述
在这里插入图片描述
ok,如果想保存起来可以用数据库存储起来,自己实现一下吧!!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨旭华 

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值