课前说明:本章节请求的 url 部分用 ** 代替
本章节需要掌握的知识点:
1、如何用 xpath 解析数据;
2、如何用 csv 存储数据(注:由于字典是无序的,所以写入csv时,表头可能跟内容搭配不对,需要手动调整表头信息);
3、对csv 数据进行分析;
爬虫分三个模块:
1、请求模块:用于构造请求体,并将请求到的网页(数据)返回给解析模块;
2、解析模块:用于提取数据(本章节用xpath提取网页中的数据),并返回数据给存储模块;
3、存储模块:用于csv存储数据。
案例简介:
用于抓取网页 https://%s.lianj**.com/ershoufang/pg%d/ 中二手房的信息,例如:图片url、房价、位置、房型等信息。
简单解析一下爬虫的思路:
1、访问链接:https://%s.lianj**.com/ershoufang/pg%d/ 检查该网站是否动态加载网站(本网站为静态网站)
2、如何判断网站是否为静态网站:右键检查网页源代码,从页面上随机选取我们要爬取的内容,例如房价信息,然后ctrl+f查找,若找到了,则为静态网页,否则为动态加载网页(多试几次,多用一些我们需要爬取的内容尝试)
3、观察网页url的变化,经过观察,页面的变化规律为:
https://%s.lianj**.com/ershoufang/pg1/
https://%s.lianj**.com/ershoufang/pg2/
https://%s.lianj**.com/ershoufang/pg3/
4、用xpath 解析网页内容:
右击检查 -> 点击(下图带箭头的标志) -> 然后点击 “简装三房 望花园景观。。。”(我们要爬取的信息) -> 点击之后控制台右边会出现蓝色块,这个蓝色块就是我们想要的信息 -> 在这蓝色块上 右击 -> Copy -> Copy xpath 这样就能得到我们想要的内容的 xpath 路径
爬虫代码如下:
import csv
from urllib import request
from time import sleep
from lxml import etree
# 请求模块
def handle_url(url, page, city):
'''
:param url: 请求url
:param page: 请求页面
:param city: 请求城市
:return: 返回请求对象
'''
# 拼接url
page_url = url % (city, page)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
}
return request.Request(url=page_url, headers=headers)
def request_data(url, city, start, end):
'''
:param url: 请求url
:param city: 请求城市
:param start: 开始页面
:param end: 结束页面
:return: 返回请求到的HTML(数据)
'''
for page in range(start, end + 1):
req = handle_url(url, page, city)
res = request.urlopen(req)
sleep(2)
yield res.read().decode('utf-8')
# 解析模块
def analysis_html(html_list, city):
'''
:param html_list: 请求到的HTML列表
:param city: 请求城市
:return: 返回解析数据列表
'''
for html in html_list:
# 用etree把HTML字符串初始化成一个节点树
html_tree = etree.HTML(html)
if city == 'bj':
house_list = html_tree.xpath('//ul[@class="sellListContent"]//li')
for house in house_list:
title = house.xpath('.//div[@class="title"]/a/text()')
houseInfo = house.xpath('.//div[@class="houseInfo"]//text()')
positionInfo = house.xpath('.//div[@class="positionInfo"]//text()')
unitPrice = house.xpath('.//div[@class="unitPrice"]//text()')
totalPrice = house.xpath('.//div[@class="totalPrice"]//text()')
pic = house.xpath('.//img[@class="lj-lazy"]/@data-original')
if len(title) != 0:
item = {}
item['title'] = title[0]
item['houseInfo'] = ''.join(houseInfo[0:11])
item['positionInfo'] = ''.join(positionInfo[0:5])
item['unitPrice'] = unitPrice[0]
item['totalPrice'] = ''.join(totalPrice[0:2])
item['pic'] = pic[0]
yield item
else:
pass
else:
# 查找到所有的房屋信息
house_list = html_tree.xpath('//ul[@class="sellListContent"]//li')
# 遍历所有的房屋信息,从中提取我们想要的内容,并且格式化
for house in house_list:
# 每一个house代表一个房屋信息的li节点,我们可以以house为根,通过
# xpath的相对路径去解析本节点内部的数据
# 创建一个字典,用于整合一条房屋信息
item = {}
# 用相对路径提取元素
item['title'] = house.xpath('.//div[@class="title"]/a/text()')[0]
item['houseInfo'] = ''.join(house.xpath('.//div[@class="houseInfo"]//text()'))
item['positionInfo'] = ''.join(house.xpath('.//div[@class="positionInfo"]//text()'))
item['unitPrice'] = house.xpath('.//div[@class="unitPrice"]//text()')[0]
item['totalPrice'] = house.xpath('.//div[@class="totalPrice"]//text()')[0]
item['pic'] = house.xpath('.//img[@class="lj-lazy"]/@data-original')[0]
yield item
# 存储模块
def write_to_csv(data):
'''
写入csv表
:param data:
:return:
'''
# 把数据整合成二维列表的形式
fp = open('ershoufang.csv', 'a+')
# 用csv模块,创建一个写的对象
writer = csv.writer(fp)
# 首先写表头
writer.writerow(['positionInfo', 'pic', 'title', 'unitPrice', 'totalPrice', 'houseInfo'])
# 遍历data,把所有的房屋信息写入
for item in data:
# 把字典整合成一个列表
house = []
for k, v in item.items():
house.append(v)
# 把整合出来的信息写入
writer.writerow(house)
fp.close()
def main():
url = 'https://%s.lianj**.com/ershoufang/pg%d/'
city = input('请输入城市:')
start = int(input('请输入起始页:'))
end = int(input('请输入终止页:'))
# 请求
html_list = request_data(url, city, start, end)
# 解析
data = analysis_html(html_list, city)
# 储存
write_to_csv(data)
if __name__ == '__main__':
main()
作者爬虫的是 gz (广州) 的所有二手房信息,现对二手房进行分析:
代码如下(推荐用Jupyter Notebook工具,方便调试):
import re
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
%matplotlib inline
import matplotlib.pyplot as plt
# 加载二手房信息
ershoufang = pd.read_csv('./ershoufang.csv')
# 对二手房中的 houseInfo进行切割
def houseInfo(house):
info_list = house.split('|')
return info_list
# 将切割后的信息整合成二维列表
def h_info(house):
house_list = []
for h in house:
house_list.append(h)
return house_list
# 提取单价
def unit_price(price):
pat = re.compile(r'单价(\d+)元/平米')
res = float(pat.findall(price)[0])
return res
# 切割 houseInfo 注:此步骤本该在爬虫阶段就把houseInfo分开,方便后期进行数据分析!!!
house_info = ershoufang['houseInfo'].map(houseInfo)
# 整合 houseInfo
house_info = h_info(house_info)
# 将 houseInfo 转为DataFrame 数据
house_df = DataFrame(house_info,columns=['name','type','size','position','other1','elevator','other2'])
# 通过级联的方式合并两个 DataFrame 数据
ershoufang = pd.concat([ershoufang,house_df],axis=1)
# 删除多余的信息
del ershoufang['houseInfo']
# 去掉单价中的汉字
ershoufang['unitPrice'] = ershoufang['unitPrice'].map(unit_price)
# 重命名其中的两个列索引名
ershoufang.rename(columns={'unitPrice':'unitPrice(元/平米)','totalPrice':'totalPrice(万元)'})
# 分类:type 聚合:unitPrice
totalPrice_max = ershoufang.groupby('type')[['totalPrice']].max()
totalPrice_min = ershoufang.groupby('type')[['totalPrice']].min()
totalPrice_mean = ershoufang.groupby('type')[['totalPrice']].mean()
unitPrice_max = ershoufang.groupby('type')[['unitPrice']].max()
unitPrice_min = ershoufang.groupby('type')[['unitPrice']].min()
unitPrice_mean = ershoufang.groupby('type')[['unitPrice']].mean()
# 画出单价的线形图
plt.figure(figsize=(16,9))
plt.plot(unitPrice_max,marker='*',markersize=10,label='unitPrice_max')
plt.plot(unitPrice_min,marker='*',markersize=10,label='unitPrice_min')
plt.plot(unitPrice_mean,marker='*',markersize=10,label='unitPrice_mean')
plt.grid(alpha=0.8)
plt.xlabel('tyeps')
plt.ylabel('yuan/pingfang',rotation=0,horizontalalignment='right')
plt.legend(loc=(0,1),ncol=3)
plt.yticks(range(10000,110000,10000))
# 画出总价的线形图
plt.figure(figsize=(16,9))
plt.plot(totalPrice_max,marker='*',markersize=10,label='totalPrice_max')
plt.plot(totalPrice_min,marker='*',markersize=10,label='totalPrice_min')
plt.plot(totalPrice_mean,marker='*',markersize=10,label='totalPrice_mean')
plt.grid(alpha=0.8)
plt.xlabel('tyeps')
plt.ylabel('totalPrice(w)',rotation=0,horizontalalignment='right')
plt.legend(loc=(0,1),ncol=3)
plt.yticks(range(100,3000,100))
单价线形图如下:
(注:由于matplotlib不支持中文,故横坐标中文显示不出来,内容为 几室几厅,最后两个为:双拼别墅,独栋别墅)
根据上图可知:
广州二手房的房价大约为 3.5万/平方米 ;
最高二手房房价大于10万/平方米,最低二手房房价小于 1万/平方米;
总价线形图为:
根据上图可知:
在广州也可以用 100万买到房子哦!!!!!!
广州的二手房最高总价居然达到了 2700万!!!
↖(^ω^)↗!好好奋斗,祝同学们早日在广州买房哈。
温馨提示:好了,本案例就到此结束,记得动手敲一敲哦n(*≧▽≦*)n!