赞
踩
使用 Python 爬虫库完成链家二手房(https://bj.lianjia.com/ershoufang/rs/)房源信息抓取,包括楼层、区域、总价、单价等信息。
第一页: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/
使用 Chrome 开发者工具对页面元素进行审查,从而确定 Xpath 表达式。首先根据要抓取的数据确定“基准表达式”。通过审查一处房源的元素结构,可以得知房源信息都包含在以下代码中:
<div class="info clear"> <div class="title"><a class="" href="https://bj.lianjia.com/ershoufang/101122052862.html" target="_blank" data-log_index="1" data-el="ershoufang" data-housecode="101122052862" 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/1111027377165/" target="_blank" data-log_index="1" data-el="region">金汉绿港三区 </a> - <a href="https://bj.lianjia.com/ershoufang/shunyicheng/" target="_blank">顺义城</a> </div> </div> <div class="address"> <div class="houseInfo"><span class="houseIcon"></span>3室2厅 | 139.96平米 | 南 北 | 简装 | 低楼层(共20层) | 2009年 | 板塔结合</div> </div> <div class="followInfo"><span class="starIcon"></span>12人关注 / 29天以前发布</div> <div class="tag"><span class="subway">近地铁</span><span class="vr">VR房源</span><span class="taxfree">房本满五年</span><span class="haskey">随时看房</span></div> <div class="priceInfo"> <div class="totalPrice totalPrice2"><i> </i><span class="">485</span><i>万</i></div> <div class="unitPrice" data-hid="101122052862" data-rid="1111027377165" data-price="34653"><span>34,653元/平</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>
特别注意:这里可以class="clear LOGVIEWDATA LOGCLICKDATA"需要使用源码查看
因此 Xpath 基准表达式如下所示:
//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]
根据页面元素结构确定待抓取信息的 Xpath 表达式,分别如下:
小区名称:position = h.xpath('.//a[@data-el="region"]/text()')[0]
房屋介绍:hourseInfo_list = h.xpath('.//div[@class="houseInfo"]/text()')
单价信息:addresunitPrice = h.xpath('.//div[@class="unitPrice"]//text()')[0].strip()
总价信息:totalPrice = h.xpath('.//div[@class="totalPrice totalPrice2"]//text()')[0].strip()
其中房屋介绍,主要包含了以下信息:
<div class="houseInfo">
<span class="houseIcon"></span>4室2厅 | 133.68平米 | 南 北 | 精装 | 顶层(共6层) | 板楼
</div>
因此,匹配出的 info_list 列表需要经过处理才能得出我们想要的数据,如下所示:
# 房屋信息:3室2厅 | 147.95平米 | 南 东南 | 简装 | 中楼层(共18层) | 塔楼 hourseInfo_list = h.xpath('.//div[@class="houseInfo"]/text()') if hourseInfo_list: hourseInfo = hourseInfo_list[0].split("|") if len(hourseInfo) >= 5: if hourseInfo: # 户型 item.append(hourseInfo[0].strip()) # 面积 item.append(hourseInfo[1].strip()) # 方向 item.append(hourseInfo[2].strip()) # 是否精装 item.append(hourseInfo[3].strip()) # 楼层 item.append(hourseInfo[4].strip()) # 楼型 item.append(hourseInfo[5].strip())
为了提高网页信息的抓取质量,减小网络波动带来的响应,我们可以设置一个规则:在超时时间内(3秒),在该时间内对于请求失败的页面尝试请求三次,如果均未成功,则抓取下一个页面。
通过上述分析得出了所有的 Xpath 表达式,下面开始编写爬虫程序,代码如下:
在# coding:utf8 import requests import random from lxml import etree import time # 提供ua信息的的包 from fake_useragent import UserAgent # mysql数据库模块 import pymysql class LianJiaSpider(object): # 构造方法,设置请求路径,每个页面请求次数,数据库连接对象、游标对象 def __init__(self): # 爬取路径{}表示第几页的数据 self.url = 'https://bj.lianjia.com/ershoufang/pg{}/' # 计数,请求一个页面的次数,初始值为1 self.count = 1 # 数据库连接对象 self.db = pymysql.connect(host="127.0.0.1", user='root', password="", db='lianjia') # 数据库游标对象 self.cursor = self.db.cursor() # 随机获取UA def get_headers(self): ua = UserAgent() # headers = { # 设置header # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', # # 'Accept-Encoding': 'gzip, deflate, br', # 'Accept-Language': 'zh-CN,zh;q=0.9', # 'Cache-Control': 'no-cache', # 'Connection': 'keep-alive', # 'User-Agent': 'Mozilla/4.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', # 'referer': 'https://passport.meituan.com/', # 'Cookie': '__mta=42753434.1633656738499.1634781127005.1634781128998.34; uuid_n_v=v1; _lxsdk_cuid=17c5d879290c8-03443510ba6172-6373267-144000-17c5d879291c8; uuid=60ACEF00317A11ECAAC07D88ABE178B722CFA72214D742A2849B46660B8F79A8; _lxsdk=60ACEF00317A11ECAAC07D88ABE178B722CFA72214D742A2849B46660B8F79A8; _csrf=94b23e138a83e44c117736c59d0901983cb89b75a2c0de2587b8c273d115e639; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1634716251,1634716252,1634719353,1634779997; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1634781129; _lxsdk_s=17ca07b2470-536-b73-84%7C%7C12' headers = {'User-Agent': ua.random} return headers # 获取html def get_html(self, url): # 在超时内,对失败请求页面尝试三次 if self.count <= 3: try: # 获取HTTPResponse对象 resp = requests.get(url=url, headers=self.get_headers(), timeout=5) html = resp.text # print(html) return html except Exception as e: print(e) self.get_html(url) self.count += 1 # 数据的解析 def parse_html(self, url): # print(url) html = self.get_html(url) # print(html) if html: parse_html = etree.HTML(html) # 获取每页显示的三十个房源列表 sellListContent = parse_html.xpath( '//ul[@class="sellListContent"]/li[@class="clear LOGVIEWDATA LOGCLICKDATA"]') # print(sellListContent) # 存储所有的数据列表 h_list = [] for h in sellListContent: item = [] # 小区 position = h.xpath('.//a[@data-el="region"]/text()')[0] item.append(position) # 房屋信息:3室2厅 | 147.95平米 | 南 东南 | 简装 | 中楼层(共18层) | 塔楼 hourseInfo_list = h.xpath('.//div[@class="houseInfo"]/text()') if hourseInfo_list: hourseInfo = hourseInfo_list[0].split("|") if len(hourseInfo) >= 5: if hourseInfo: # 户型 item.append(hourseInfo[0].strip()) # 面积 item.append(hourseInfo[1].strip()) # 方向 item.append(hourseInfo[2].strip()) # 是否精装 item.append(hourseInfo[3].strip()) # 楼层 item.append(hourseInfo[4].strip()) # 楼型 item.append(hourseInfo[5].strip()) # 单价 unitPrice = h.xpath('.//div[@class="unitPrice"]//text()')[0].strip() # 总价 totalPrice = h.xpath('.//div[@class="totalPrice totalPrice2"]//text()')[0].strip() item.append(unitPrice) item.append(totalPrice) h_list.append(item) return h_list def save_html(self, url): try: h_list = self.parse_html(url) # 将列表中的每个元素转换为元祖 h_list = list(map(tuple, h_list)) # print(h_list) sql = "insert into house (position,housetype,area,direction,hardcover,floor,buildtype,unitprice,totalprice)values(%s,%s,%s,%s,%s,%s,%s,%s,%s)" self.cursor.executemany(sql, h_list) self.db.commit() except Exception as e: self.db.rollback() print("数据库添加失败", str(e.args)) def run(self): try: start = int(input("请输入开始页:")) stop = int(input("请输入终结页:")) for i in range(start, stop + 1): url = self.url.format(i) self.save_html(url) time.sleep(random.randint(2, 3)) except Exception as e: print("抓取失败,", e) finally: self.cursor.close() self.db.close() if __name__ == '__main__': begin = time.time() spider = LianJiaSpider() spider.run() end = time.time() print('数据爬取完毕,总共耗时:%.2f' % (end - begin))
mysql> create database lianjia; Query OK, 1 row affected (0.00 sec) mysql> use lianjia Database changed mysql> create table house( -> position varchar(40), -> housetype varchar(40), -> area varchar(20), -> direction varchar(20), -> hardcover varchar(20), -> floor varchar(20), -> buildtype varchar(20), -> unitPrice varchar(10), -> totalPrice varchar(10) -> ); Query OK, 0 rows affected (0.05 sec)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。