温馨提示:
爬虫玩得好,监狱进得早。数据玩得溜,牢饭吃个够。
《刑法》第 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,如果想保存起来可以用数据库存储起来,自己实现一下吧!!