第一页:https://bj.lianjia.com/ershoufang/pg1/ 第二页:https://bj.lianjia.com/ershoufang/pg2/ 第三页:https://bj.lianjia.com/ershoufang/pg3/ 第n页:https://bj.lianjia.com/ershoufang/pgn/
<div class="info clear"> <div class="title"><a class="" href="https://bj.lianjia.com/ershoufang/101110713022.html" target="_blank" data-log_index="1" data-el="ershoufang" data-housecode="101110713022" data-is_focus="" data-sl="">玉竹园小区 满五唯一 楼层高 视野好</a><span class="goodhouse_tag tagBlock">必看好房</span></div> <div class="flood"> <div class="positionInfo"><span class="positionIcon"></span><a href="https://bj.lianjia.com/xiaoqu/1111027382276/" target="_blank" data-log_index="1" data-el="region">玉竹园 </a> - <a href="https://bj.lianjia.com/ershoufang/liangxiang/" target="_blank">良乡</a> </div> </div> <div class="address"> <div class="houseInfo"><span class="houseIcon"></span>2室1厅 | 88.62平米 | 北 南 | 简装 | 顶层(共6层) | 2004年建 | 板楼</div> </div> <div class="followInfo"><span class="starIcon"></span>26人关注 / 7天以前发布</div> <div class="tag"><span class="subway">近地铁</span><span class="isVrFutureHome">VR看装修</span><span class="taxfree">房本满五年</span><span class="haskey">随时看房</span></div> <div class="priceInfo"> <div class="totalPrice"><span>225</span>万</div> <div class="unitPrice" data-hid="101110713022" data-rid="1111027382276" data-price="25390"> <span>单价25390元/平米</span></div> </div> </div>
<div class="positionInfo">..</div> <div class="address">...</div> <div class="priceInfo">...</div>而每个页面中都包含 30 个房源,因此我们要匹配以下节点的父节点或者先辈节点,从而确定 Xpath 基准表达式:
<div class="info clear"></div>通过页面结构分析可以得出每页的 30 个房源信息全部包含以下节点中:
<ul class="sellListContent" log-mod="list"> <li class="clear LOGVIEWDATA LOGCLICKDATA"> 房源信息.. </li> </ul>接下来,使用调试工具定位上述元素,然后滚动鼠标滑。这时候神奇的一幕出现了,你会发现
li
标签的class
属性值发生了变化,其结果如下:<ul class="sellListContent" log-mod="list"> <li class="clear LOGCLICKDATA"> 房源信息.. </li> </ul>发生变化的原因是由于 JS 事件触发导致的。因此就需要去页面的源码页进行匹配。
Ctrl+F
分别对 class 变化前后的属性值进行检索,最后发现源码页只存在如下属性:
class="clear LOGVIEWDATA LOGCLICKDATA"因此 Xpath 基准表达式如下所示:
//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]
小区名称:name_list=h.xpath('.//a[@data-el="region"]/text()') 房屋介绍:info_list=h.xpath('.//div[@class="houseInfo"]/text()') 地址信息:address_list=h.xpath('.//div[@class="positionInfo"]/a/text()') 单价信息:price_list=h.xpath('.//div[@class="unitPrice"]/span/text()')其中房屋介绍,主要包含了以下信息:
<div class="address"> <div class="houseInfo"><span class="houseIcon"></span>2室1厅 | 88.62平米 | 北 南 | 简装 | 顶层(共6层) | 2004年建 | 板楼</div> </div>因此,匹配出的 info_list 列表需要经过处理才能得出我们想要的数据,如下所示:
#户型+面积+方位+是否精装+楼层+... ['2室1厅 | 88.62平米 | 北 南 | 简装 | 顶层(共6层) | 2004年建 | 板楼'] info_list=h.xpath('.//div[@class="houseInfo"]/text()') if info_list: #处理列表数据 L=info_list[0].split('|') # ['2室1厅 ', ' 88.62平米 ', ' 北 南 ', ' 简装 ', ' 顶层(共6层) ', ' 2004年建 ', ' 板楼'] if len(L) >= 5: item['model']=L[0].strip() item['area']=L[1].strip() item['direction']=L[2].strip() item['perfect']=L[3].strip() item['floor']=L[4].strip()
#coding:utf8 import requests import random from lxml import etree import time #提供ua信息的的包 from fake_useragent import UserAgent class LinajiaSpider(object): def __init__(self): self.url='https://bj.lianjia.com/ershoufang/pg{}/' #计数,请求一个页面的次数,初始值为1 self.blog=1 # 随机取一个UA def get_header(self): #实例化ua对象 ua=UserAgent() headers={'User-Agent':ua.random} return headers #发送请求 def get_html(self,url): #在超时间内,对于失败页面尝试请求三次 if self.blog<=3: try: res=requests.get(url=url,headers=self.get_header(),timeout=3) html=res.text return html except Exception as e: print(e) self.blog+=1 self.get_html(url) # 解析提取数据 def parse_html(self,url): html=self.get_html(url) if html: p=etree.HTML(html) #基准xpath表达式-30个房源节点对象列表 h_list=p.xpath('//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]') #所有列表节点对象 for h in h_list: item={} #名称 name_list=h.xpath('.//a[@data-el="region"]/text()') #判断列表是否为空 item['name']=name_list[0] if name_list else None #户型+面积+方位+是否精装..['2室1厅 | 88.62平米 | 北 南 | 简装 | 顶层(共6层) | 2004年建 | 板楼'] info_list=h.xpath('.//div[@class="houseInfo"]/text()') #判断列表是否为空 if info_list: L=info_list[0].split('|') # ['2室1厅 ', ' 88.62平米 ', ' 北 南 ', ' 简装 ', ' 顶层(共6层) ', ' 2004年建 ', ' 板楼'] if len(L) >= 5: item['model']=L[0].strip() item['area']=L[1].strip() item['direction']=L[2].strip() item['perfect']=L[3].strip() item['floor']=L[4].strip() #区域+总价+单价 address_list=h.xpath('.//div[@class="positionInfo"]/a/text()') item['address']=address_list[0].strip() if address_list else None total_list=h.xpath('.//div[@class="totalPrice"]/span/text()') item['total_list']=total_list[0].strip() if total_list else None price_list=h.xpath('.//div[@class="unitPrice"]/span/text()') item['price_list']=price_list[0].strip() if price_list else None print(item) # 入口函数 def run(self): try: for i in range(1,101): url=self.url.format(i) self.parse_html(url) time.sleep(random.randint(1,3)) #每次抓取一页要初始化一次self.blog self.blog=1 except Exception as e: print('发生错误',e) if __name__ == '__main__': spider=LinajiaSpider() spider.run()展示部分输出结果:
{'name': '玉竹园 ', 'model': '2室1厅', 'area': '88.62平米', 'direction': '北 南', 'perfect': '简装', 'floor': '顶层(共6层)', 'address': '玉竹园', 'total_list': '225', 'price_list': '单价25390元/平米'} {'name': '摩卡空间 ', 'model': '2室1厅', 'area': '71.95平米', 'direction': '东南', 'perfect': '简装', 'floor': '高楼层(共17层)', 'address': '摩卡空间', 'total_list': '372', 'price_list': '单价51703元/平米'} {'name': '长城国际 ', 'model': '1室1厅', 'area': '52.73平米', 'direction': '西', 'perfect': '精装', 'floor': '22层', 'address': '长城国际', 'total_list': '235', 'price_list': '单价44567元/平米'} {'name': '牛街西里 ', 'model': '3室1厅', 'area': '102.6平米', 'direction': '东北', 'perfect': '其他', 'floor': '中楼层(共20层)', 'address': '牛街西里', 'total_list': '815', 'price_list': '单价79435元/平米'} {'name': '新安里 ', 'model': '1室2厅', 'area': '51.56平米', 'direction': '南 北', 'perfect': '精装', 'floor': '顶层(共6层)', 'address': '新安里', 'total_list': '197', 'price_list': '单价38208元/平米'} {'name': '龙华园 ', 'model': '2室1厅', 'area': '67.73平米', 'direction': '南 北', 'perfect': '简装', 'floor': '顶层(共6层)', 'address': '龙华园', 'total_list': '342', 'price_list': '单价50495元/平米'} ...
本文链接:http://task.lmcjl.com/news/18141.html