python爬虫实战:使用scrapy和matplotlib进行房地产数据分析
作者:互联网
虽然python爬虫已经火了很多年,但今天我们依然会通过scrapy爬虫框架结合matplotlib数据分析库实战一个房地产网站的数据采集以及数据分析,来帮助大家更系统的掌握大数据分析的进本流程。快跟随icode9小编一起来学习吧。
在今天的文章中,我们将从最大的房地产网站之一提取房地产列表,然后分析数据。与我们之前的网络数据分析博文类似,我将向您展示一种使用python提取网络数据然后对数据集进行描述性分析的简单方法。
技术堆栈和方法
我们将使用 python 作为编程语言。
工具和库:
尽管这可能是一个非常复杂的项目,因为它还涉及网络抓取和数据分析,但我们将通过使用以下过程使其变得简单:
定义数据要求
实施数据提取
进行数据分析(查询数据库+可视化)
开始吧!
资料要求
对于每个网络抓取项目,我们需要回答的第一个问题是——我们到底需要什么数据?当涉及到房地产清单时,我们可以抓取的数据点太多了,我们必须根据我们的需要真正缩小范围。现在,我将选择这些字段:
上市类型
价格
房屋面积
城市
建成年份
这些数据字段将使我们能够自由地从不同的角度查看列表。
数据提取
既然我们知道要从网站中提取哪些数据,我们就可以开始处理我们的蜘蛛了。
安装 Scrapy
我们正在使用 Scrapy,这个项目的网络抓取框架。建议在虚拟环境下安装Scrapy,这样不会和其他系统包冲突。
创建一个新文件夹并安装 virtualenv:
mkdir real_estate
cd real_estate
pip install virtualenv
virtualenv env
源 env/bin/activate
安装废料:
pip 安装 scrapy
如果您在安装 Scrapy 时遇到问题,请查看安装指南。
创建一个新的 Scrapy 项目:
scrapy startproject 房地产
检查
现在我们已经安装了 Scrapy,让我们检查一下我们试图从中获取数据的网站。为此,您可以使用浏览器的检查器。我们的目标是在页面上的 HTML 中找到所有数据字段,并为它们编写一个选择器/XPath。
上面的 HTML 片段包含许多列表元素。在每个<li>标记内,我们可以找到许多我们正在寻找的字段,例如 listing_type 字段。由于不同的列表定义了不同的细节,我们不能仅根据 HTML 标签或 CSS 选择器来选择数据。(一些房源定义了房屋大小,而另一些则没有)因此,例如,如果我们想从上面的代码中提取房源类型,我们可以使用 XPath 来选择一个在其文本中具有“房源类型”的 HTML 元素,然后提取它的第一个兄弟。
listing_type的 Xpath :
“//span[@class='listing-detail-stats-main-key'[contains(text(),'ListingType')]/following-sibling::span”
然后,我们可以对房屋大小等做同样的事情……
“//span[@class='listing-detail-stats-main-key'[contains(text(),HouseSize)]/following-sibling::span”
找到所有选择器后,这是我们的爬虫代码:
def 解析(自我,响应):
item_loader = ItemLoader(item=RealEstateItem(),response=response)
item_loader.default_input_processor = MapCompose(remove_tags)
item_loader.default_output_processor = TakeFirst()
item_loader.add_value("url", response.url)
item_loader.add_xpath("listing_type", "")
item_loader.add_css("price", "")
item_loader.add_css("price", "")
item_loader.add_xpath(" house_size", "")
item_loader.add_css("city", "")
item_loader.add_xpath("year_built","")
返回 item_loader.load_item()
如您所见,我正在使用ItemLoader。这样做的原因是对于某些字段,提取的数据是混乱的。例如,这是我们得到的原始房屋面积值:
2,100 平方英尺
该值在当前形式下不可用,因为我们需要一个数字作为房屋大小。不是字符串。所以我们需要去掉单位和逗号。在 Scrapy 中,我们可以为此使用输入处理器。这是带有清洁输入处理器的house_size字段:
house_size = Field(
input_processor=MapCompose(remove_tags, strip, lambda value: float(value.replace(" sqft", "").replace(",", "")))
)
这个处理器做什么,按顺序:
移除 HTML 标签
删除不必要的空格
删除“平方英尺”
删除逗号
输出变成一个数值,可以很容易地插入到任何数据库中:
2100
为每个字段编写清理函数后,这就是我们的项目类的样子:
类 RealEstateItem(项目):
listing_type = Field(
input_processor=MapCompose(remove_tags, strip)
)
price = Field(
input_processor=MapCompose(remove_tags, lambda value: int(value.replace(",", "")))
)
house_size = Field(
input_processor=MapCompose(remove_tags, strip, lambda 值: float(value.replace(" sqft", "").replace(",", "")))
)
year_built = Field(
input_processor=MapCompose(remove_tags, strip) , lambda 值: int(value))
)
city = Field()
url = Field()
数据库管道
至此,我们处理了数据提取部分。为了准备用于分析的数据,我们需要将其存储在数据库中。为此,我们创建了一个自定义的 Scrapy 管道。如果您不确定 Scrapy 中的数据库管道是如何工作的,请查看这篇文章。
类数据库管道(对象):
def __init__(self, db, user, passwd, host):
self.db = db
self.user = user
self.passwd = passwd
self.host = host
@classmethod
def from_crawler(cls, crawler):
db_settings = crawler.settings.getdict("DB_SETTINGS")
如果不是db_settings:
raise NotConfigured
db = db_settings['db']
user = db_settings['user']
passwd = db_settings['passwd ']
host = db_settings['host']
return cls(db, user, passwd, host)
def open_spider(self, spider):
self.conn = MySQLdb.connect(db=self.db,
user=self.user, passwd=self.passwd,
host=self.host,
charset='utf8', use_unicode=True)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
sql = "INSERT INTO Listing (url, price, listing_type, house_size, year_built, city) VALUES (%s, %s, %s, %s, %s, %s)"
self.cursor.execute(sql,
(
item.get("url"),
item.get("价格"),
item.get("listing_type"),
item.get("house_size"),
item.get(" year_built"),
item.get("city")
)
)
self.conn.commit()
返回项目
def close_spider(self, spider):
self.conn.close()
现在我们可以运行我们的蜘蛛:
scrapy 爬行 real_estate
如果我们做的一切都正确,我们应该在数据库中看到提取的记录,格式很好。
数据分析
现在我们要可视化数据。希望能对它有更深入的了解。我们将要遵循的绘制图表的过程:
查询数据库获取需要的数据
将其放入熊猫数据框中
必要时清理、操作数据
使用panda的plot()函数绘制图表
在分析数据集时得出影响深远的结论之前,需要指出几件重要的事情:
我们只有大约 3000 条记录(我没有抓取整个网站,只有一部分)
我删除了土地清单(没有建筑物)
我只抓取了美国 9 个城市的列表
描述性报告
首先,让我们“了解”一下我们的数据库。我们将把整个数据库查询到一个数据框中,并创建一个新字典来获取所有数字数据字段的最小值、最大值、平均值和中值。然后从字典创建一个新的数据框,以便能够将其显示为表格。
query = ("SELECT price, house_size, year_built FROM Listing")
df = pd.read_sql(query, self.conn)
d = {'平均值': df.mean(),
'Min': df.min(),
' Max': df.max(),
'Median': df.median()}
return pd.DataFrame.from_dict(d, dtype='int32')[["Min", "Max", "Mean", "Median "]].转置()
这张表向我们展示了一些信息:
最古老的房子建于1837年
最便宜的房子是 19 900 美元,最贵的是 15 950 000 美元
最小的房子是 256 平方英尺,最大的是 17 875 平方英尺
平均房屋建于 1950 年代
平均房屋面积为 1883 平方英尺(174 平方米)
户型比例
总的来说,我们有三种房源类型:公寓/联排别墅、多户住宅和单户住宅。让我们看看哪些在列表中更受欢迎。
查询 = ("SELECT listing_type, COUNT(*) AS 'count' FROM Listing GROUP BY listing_type")
df = pd.read_sql(query, self.conn, index_col="listing_type") df.plot.pie(y="count ", autopct="%1.1f%%", figsize=(7, 7))
plt.show()
超过一半的房源是单户住宅。其中大约三分之一被认为是公寓/联排别墅。只有 12.3% 是多户住宅。
价格与年份的相关性
接下来,让我们看看价格。最好看看建筑物的价格和年龄之间是否存在任何相关性。
query = "SELECT price, year_built FROM Listing"
df = pd.read_sql(query, self.conn)
x = df["year_built"].tolist()
y = df["price"].tolist()
c = 计数器( zip(x, y))
s = [10 * c[(xx, yy)] for xx, yy in zip(x, y)] df.plot(kind="scatter", x="year_built", y= "price", s=s, color="blue")
yy, locs = plt.yticks()
ll = ['%.0f' % a for a in yy]
plt.yticks(yy, ll)
plt.show( )
我们在这里看到的是,我们几乎每年都有一份清单。但我们并不能从中得出明确的结论。好像20世纪上半叶的房子不少,而且比新造的还贵。
每个城市的平均价格
在这一篇中,我们将查看每个城市的价格标签。
query = “SELECT FLOOR(AVG(price)) AS 'price', city, COUNT(city) AS 'count' FROM Listing GROUP BY city HAVING count > 30”
df = pd.read_sql(query, self.conn)
df = df.drop(df.index[1]) ax = plt.gca() ax.get_yaxis().get_major_formatter().set_useOffset(False) ax.get_yaxis().get_major_formatter().set_scientific(False) df.plot。 barh(x="city", y="price", color="royalblue",
ax=ax, grid=True)
plt.xlabel("AvgPrice ($)")
plt.ylabel("城市")
plt.show ()
在我们分析的城市中,旧金山和西雅图是最贵的。旧金山的平均房价约为 165 万美元。西雅图约为 185 万美元。买房最便宜的城市是奇克托瓦加和布法罗,这两个城市的平均房价约为 25 万美元。
每种户型的平均价格
在接下来的分析中,我们将检查不同房屋类型的平均价格。三种户型中,哪一种最贵,哪一种最便宜?
query = "SELECT FLOOR(AVG(price)) AS 'price', listing_type FROM Listing GROUP BY listing_type"
df = pd.read_sql(query, self.conn)
ax = plt.gca()
df.plot(kind="酒吧", x="listing_type", y="price", color="royalblue", ax=ax, grid=True, rot=0)
plt.xlabel("Type")
plt.ylabel("AvgPrice ($)" )
plt.show()
正如我们所见,Condo/Townhome 和 Multi-Family Type 房屋之间没有太大区别。两者都在 1,000,000 美元至 1,200,000 美元之间。单户住宅要便宜得多,平均价格为 800,000 美元。
本文是与icode9.com icode9技术文章分享的小编一起来为大家呈现这篇技术文章,希望本文能对大家有帮助。感谢阅读!
在今天的文章中,我们将从最大的房地产网站之一提取房地产列表,然后分析数据。与我们之前的网络数据分析博文类似,我将向您展示一种使用python提取网络数据然后对数据集进行描述性分析的简单方法。
技术堆栈和方法
我们将使用 python 作为编程语言。
工具和库:
尽管这可能是一个非常复杂的项目,因为它还涉及网络抓取和数据分析,但我们将通过使用以下过程使其变得简单:
定义数据要求
实施数据提取
进行数据分析(查询数据库+可视化)
开始吧!
资料要求
对于每个网络抓取项目,我们需要回答的第一个问题是——我们到底需要什么数据?当涉及到房地产清单时,我们可以抓取的数据点太多了,我们必须根据我们的需要真正缩小范围。现在,我将选择这些字段:
上市类型
价格
房屋面积
城市
建成年份
这些数据字段将使我们能够自由地从不同的角度查看列表。
数据提取
既然我们知道要从网站中提取哪些数据,我们就可以开始处理我们的蜘蛛了。
安装 Scrapy
我们正在使用 Scrapy,这个项目的网络抓取框架。建议在虚拟环境下安装Scrapy,这样不会和其他系统包冲突。
创建一个新文件夹并安装 virtualenv:
mkdir real_estate
cd real_estate
pip install virtualenv
virtualenv env
源 env/bin/activate
安装废料:
pip 安装 scrapy
如果您在安装 Scrapy 时遇到问题,请查看安装指南。
创建一个新的 Scrapy 项目:
scrapy startproject 房地产
检查
现在我们已经安装了 Scrapy,让我们检查一下我们试图从中获取数据的网站。为此,您可以使用浏览器的检查器。我们的目标是在页面上的 HTML 中找到所有数据字段,并为它们编写一个选择器/XPath。
上面的 HTML 片段包含许多列表元素。在每个<li>标记内,我们可以找到许多我们正在寻找的字段,例如 listing_type 字段。由于不同的列表定义了不同的细节,我们不能仅根据 HTML 标签或 CSS 选择器来选择数据。(一些房源定义了房屋大小,而另一些则没有)因此,例如,如果我们想从上面的代码中提取房源类型,我们可以使用 XPath 来选择一个在其文本中具有“房源类型”的 HTML 元素,然后提取它的第一个兄弟。
listing_type的 Xpath :
“//span[@class='listing-detail-stats-main-key'[contains(text(),'ListingType')]/following-sibling::span”
然后,我们可以对房屋大小等做同样的事情……
“//span[@class='listing-detail-stats-main-key'[contains(text(),HouseSize)]/following-sibling::span”
找到所有选择器后,这是我们的爬虫代码:
def 解析(自我,响应):
item_loader = ItemLoader(item=RealEstateItem(),response=response)
item_loader.default_input_processor = MapCompose(remove_tags)
item_loader.default_output_processor = TakeFirst()
item_loader.add_value("url", response.url)
item_loader.add_xpath("listing_type", "")
item_loader.add_css("price", "")
item_loader.add_css("price", "")
item_loader.add_xpath(" house_size", "")
item_loader.add_css("city", "")
item_loader.add_xpath("year_built","")
返回 item_loader.load_item()
如您所见,我正在使用ItemLoader。这样做的原因是对于某些字段,提取的数据是混乱的。例如,这是我们得到的原始房屋面积值:
2,100 平方英尺
该值在当前形式下不可用,因为我们需要一个数字作为房屋大小。不是字符串。所以我们需要去掉单位和逗号。在 Scrapy 中,我们可以为此使用输入处理器。这是带有清洁输入处理器的house_size字段:
house_size = Field(
input_processor=MapCompose(remove_tags, strip, lambda value: float(value.replace(" sqft", "").replace(",", "")))
)
这个处理器做什么,按顺序:
移除 HTML 标签
删除不必要的空格
删除“平方英尺”
删除逗号
输出变成一个数值,可以很容易地插入到任何数据库中:
2100
为每个字段编写清理函数后,这就是我们的项目类的样子:
类 RealEstateItem(项目):
listing_type = Field(
input_processor=MapCompose(remove_tags, strip)
)
price = Field(
input_processor=MapCompose(remove_tags, lambda value: int(value.replace(",", "")))
)
house_size = Field(
input_processor=MapCompose(remove_tags, strip, lambda 值: float(value.replace(" sqft", "").replace(",", "")))
)
year_built = Field(
input_processor=MapCompose(remove_tags, strip) , lambda 值: int(value))
)
city = Field()
url = Field()
数据库管道
至此,我们处理了数据提取部分。为了准备用于分析的数据,我们需要将其存储在数据库中。为此,我们创建了一个自定义的 Scrapy 管道。如果您不确定 Scrapy 中的数据库管道是如何工作的,请查看这篇文章。
类数据库管道(对象):
def __init__(self, db, user, passwd, host):
self.db = db
self.user = user
self.passwd = passwd
self.host = host
@classmethod
def from_crawler(cls, crawler):
db_settings = crawler.settings.getdict("DB_SETTINGS")
如果不是db_settings:
raise NotConfigured
db = db_settings['db']
user = db_settings['user']
passwd = db_settings['passwd ']
host = db_settings['host']
return cls(db, user, passwd, host)
def open_spider(self, spider):
self.conn = MySQLdb.connect(db=self.db,
user=self.user, passwd=self.passwd,
host=self.host,
charset='utf8', use_unicode=True)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
sql = "INSERT INTO Listing (url, price, listing_type, house_size, year_built, city) VALUES (%s, %s, %s, %s, %s, %s)"
self.cursor.execute(sql,
(
item.get("url"),
item.get("价格"),
item.get("listing_type"),
item.get("house_size"),
item.get(" year_built"),
item.get("city")
)
)
self.conn.commit()
返回项目
def close_spider(self, spider):
self.conn.close()
现在我们可以运行我们的蜘蛛:
scrapy 爬行 real_estate
如果我们做的一切都正确,我们应该在数据库中看到提取的记录,格式很好。
数据分析
现在我们要可视化数据。希望能对它有更深入的了解。我们将要遵循的绘制图表的过程:
查询数据库获取需要的数据
将其放入熊猫数据框中
必要时清理、操作数据
使用panda的plot()函数绘制图表
在分析数据集时得出影响深远的结论之前,需要指出几件重要的事情:
我们只有大约 3000 条记录(我没有抓取整个网站,只有一部分)
我删除了土地清单(没有建筑物)
我只抓取了美国 9 个城市的列表
描述性报告
首先,让我们“了解”一下我们的数据库。我们将把整个数据库查询到一个数据框中,并创建一个新字典来获取所有数字数据字段的最小值、最大值、平均值和中值。然后从字典创建一个新的数据框,以便能够将其显示为表格。
query = ("SELECT price, house_size, year_built FROM Listing")
df = pd.read_sql(query, self.conn)
d = {'平均值': df.mean(),
'Min': df.min(),
' Max': df.max(),
'Median': df.median()}
return pd.DataFrame.from_dict(d, dtype='int32')[["Min", "Max", "Mean", "Median "]].转置()
这张表向我们展示了一些信息:
最古老的房子建于1837年
最便宜的房子是 19 900 美元,最贵的是 15 950 000 美元
最小的房子是 256 平方英尺,最大的是 17 875 平方英尺
平均房屋建于 1950 年代
平均房屋面积为 1883 平方英尺(174 平方米)
户型比例
总的来说,我们有三种房源类型:公寓/联排别墅、多户住宅和单户住宅。让我们看看哪些在列表中更受欢迎。
查询 = ("SELECT listing_type, COUNT(*) AS 'count' FROM Listing GROUP BY listing_type")
df = pd.read_sql(query, self.conn, index_col="listing_type") df.plot.pie(y="count ", autopct="%1.1f%%", figsize=(7, 7))
plt.show()
超过一半的房源是单户住宅。其中大约三分之一被认为是公寓/联排别墅。只有 12.3% 是多户住宅。
价格与年份的相关性
接下来,让我们看看价格。最好看看建筑物的价格和年龄之间是否存在任何相关性。
query = "SELECT price, year_built FROM Listing"
df = pd.read_sql(query, self.conn)
x = df["year_built"].tolist()
y = df["price"].tolist()
c = 计数器( zip(x, y))
s = [10 * c[(xx, yy)] for xx, yy in zip(x, y)] df.plot(kind="scatter", x="year_built", y= "price", s=s, color="blue")
yy, locs = plt.yticks()
ll = ['%.0f' % a for a in yy]
plt.yticks(yy, ll)
plt.show( )
我们在这里看到的是,我们几乎每年都有一份清单。但我们并不能从中得出明确的结论。好像20世纪上半叶的房子不少,而且比新造的还贵。
每个城市的平均价格
在这一篇中,我们将查看每个城市的价格标签。
query = “SELECT FLOOR(AVG(price)) AS 'price', city, COUNT(city) AS 'count' FROM Listing GROUP BY city HAVING count > 30”
df = pd.read_sql(query, self.conn)
df = df.drop(df.index[1]) ax = plt.gca() ax.get_yaxis().get_major_formatter().set_useOffset(False) ax.get_yaxis().get_major_formatter().set_scientific(False) df.plot。 barh(x="city", y="price", color="royalblue",
ax=ax, grid=True)
plt.xlabel("AvgPrice ($)")
plt.ylabel("城市")
plt.show ()
在我们分析的城市中,旧金山和西雅图是最贵的。旧金山的平均房价约为 165 万美元。西雅图约为 185 万美元。买房最便宜的城市是奇克托瓦加和布法罗,这两个城市的平均房价约为 25 万美元。
每种户型的平均价格
在接下来的分析中,我们将检查不同房屋类型的平均价格。三种户型中,哪一种最贵,哪一种最便宜?
query = "SELECT FLOOR(AVG(price)) AS 'price', listing_type FROM Listing GROUP BY listing_type"
df = pd.read_sql(query, self.conn)
ax = plt.gca()
df.plot(kind="酒吧", x="listing_type", y="price", color="royalblue", ax=ax, grid=True, rot=0)
plt.xlabel("Type")
plt.ylabel("AvgPrice ($)" )
plt.show()
正如我们所见,Condo/Townhome 和 Multi-Family Type 房屋之间没有太大区别。两者都在 1,000,000 美元至 1,200,000 美元之间。单户住宅要便宜得多,平均价格为 800,000 美元。
本文是与icode9.com icode9技术文章分享的小编一起来为大家呈现这篇技术文章,希望本文能对大家有帮助。感谢阅读!
标签:python爬虫,scrapy爬虫框架,matplotlib数据分析 来源: