前言
上一篇文章中有朋友问不能正确获取页面,一个原因是没有设置不遵守爬虫协议,设置方法如下,在settings.py文件中,将图中字段设置为False即可:
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
在上一篇文章中,我们通过相关命令,引入了Scrapy框架,并且成功运行了第一个爬虫,获取了北京朝阳地区某个小区的名称。在这篇文章中,我们将继续获取小区其他信息,比如小区位置、建筑年代、楼栋数、户数等。
1. 构建小区信息数据结构
在上一篇文章中项目结构简介章节中,我们介绍了items.py文件的用处,简单来说,该文件是用来定义爬虫最终返回数据的具体内容。我们在该文件中通过XiaoquItem类定义小区所有相关信息如下:
# '小区ID': xiao_id,
# '小区名称': xiao_name,
# '位置': position
# '挂牌平均价': average_price,
# '产权类型': right_type
# '供暖方式' heat
# '房屋总数': total_house,
# '楼栋总数': total_building
# '建成年代': complete_year,
# '开发商': developer,
# '用水类型': water,
# '用电类型': electricity,
# '绿化率': green_ratio,
# '容积率': plot_ratio,
# '价格变动记录': price_info
# '连接': url
class XiaoquItem(scrapy.Item):
xiao_id = scrapy.Field()
xiao_name = scrapy.Field()
position = scrapy.Field()
average_price = scrapy.Field()
right_type = scrapy.Field()
heat = scrapy.Field()
total_house = scrapy.Field()
complete_year = scrapy.Field()
developer = scrapy.Field()
total_building = scrapy.Field()
water = scrapy.Field()
electricity = scrapy.Field()
green_ratio = scrapy.Field()
plot_ratio = scrapy.Field()
price_info = scrapy.Field()
url = scrapy.Field()
pass
在该文件中,通过class来定义小区信息结构体,该类继承了scrapy.Item类。按照官方文档解释,Field类相当于Python内置字典类的一个别名,并没有提供额外的方法和属性。在此阶段,我们可以默认为这是Scrapy中通用写法即可,如果想深入了解Field()实现,可以进入对应源码查看。
2. 解析页面中数据
定义完小区数据结构后,就需要在链家页面中找到数据结构中对应字段的值。Scrapy中使用 Xpath作为HTML页面解析器,可以帮助爬虫定位和提取 HTML 或 XML 文档中的数据。在具体解析页面之前,可以安装一个浏览器Xpath插件,该类型插件具体作用是帮助我们快速定位元素,即告诉我们页面中某个dom元素的Xpath表示方法。某款插件效果如下:
在该图片中,顶部QUERY为Xpath语法,RESULTS为页面元素。如果我们想在页面中定位芍药居北里8月二手房参考均价的dom位置,就应该使用如下Xpath语法进行定位。
/div[@class='leftContent']/ul[@class='listContent']/li[@class='clear xiaoquListItem'][1]/div[@class='xiaoquListItemRight']/div[@class='xiaoquListItemPrice']/div[@class='totalPrice']/span
链家小区详情页面如下:
由于链家页面布局一致性,在某Xpath插件辅助下,得到了XiaoquItem结构体中数据的值在页面中对应定位方法如下:
('//div[@class="xiaoquDetailHeaderContent clear"]/div[@class="detailHeader fl"]/h1/text()').extract_first()
('//div[@class="xiaoquDetailHeaderContent clear"]/div[@class="detailHeader fl"]/div[@class="detailDesc"]/text()').extract_first()
('//div[@class="xiaoquPrice clear"]/div[@class="fl"]/span[@class="xiaoquUnitPrice"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][2]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][2]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][1]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][3]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemOneLine"]/div[@class="xiaoquInfoItem outerItem"][4]/span[@class="xiaoquInfoContent outer"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][3]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][4]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][5]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][4]/span[@class="xiaoquInfoContent"]/text()').extract_first()
('//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][5]/span[@class="xiaoquInfoContent"]/text()').extract_first()
在页面找到了对应元素的值,然后我们将这些值赋给XiaoquItem结构体。我们将lianjiaxiaoqu.py文件改造如下:
import scrapy
from lianjia.items import XiaoquItem
class LianjiaxiaoquSpider(scrapy.Spider):
name = "lianjiaxiaoqu"
allowed_domains = ["lianjia.com"]
start_urls = ["https://bj.lianjia.com/xiaoqu/chaoyang/y4/"]
def parse(self, response):
url = response.xpath('//div[@class="info"]/div[@class="title"]/a/@href').extract()[0]
yield scrapy.Request(url, callback=self.parse_info)
def parse_info(self, response):
item = XiaoquItem()
item['xiao_id'] = response.url.split('/')[-2]
item["xiao_name"] = response.xpath(
'//div[@class="xiaoquDetailHeaderContent clear"]/div[@class="detailHeader fl"]/h1/text()').extract_first()
item["position"] = response.xpath(
'//div[@class="xiaoquDetailHeaderContent clear"]/div[@class="detailHeader fl"]/div[@class="detailDesc"]/text()').extract_first()
item["average_price"] = response.xpath(
'//div[@class="xiaoquPrice clear"]/div[@class="fl"]/span[@class="xiaoquUnitPrice"]/text()').extract_first()
item["total_house"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][2]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["complete_year"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][2]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["right_type"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][1]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["heat"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][3]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["developer"] = response.xpath(
'//div[@class="xiaoquInfoItemOneLine"]/div[@class="xiaoquInfoItem outerItem"][4]/span[@class="xiaoquInfoContent outer"]/text()').extract_first()
item["total_building"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][3]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["water"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][4]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["electricity"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][2]/div[@class="xiaoquInfoItem"][5]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["green_ratio"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][4]/span[@class="xiaoquInfoContent"]/text()').extract_first()
item["plot_ratio"] = response.xpath(
'//div[@class="xiaoquInfoItemMulty"]/div[@class="xiaoquInfoItemCol"][1]/div[@class="xiaoquInfoItem"][5]/span[@class="xiaoquInfoContent"]/text()').extract_first()
print(item)
相比于(一)中lianjiaxiaoqu.py文件,主要变化:(1)将页面信息解析过程封装成parse_info()方法,供pars()方法调用。(2)在parse()方法中,首先获取小区展示页面中隐藏的小区详情页面url,其次对小区详情页面中内容发起请求,获得相应页面后,通过parse_info()方法进行解析,获得相应信息并赋值给item。(3)最后打印item,获得某个小区信息。
之所以在parse方法中再次进行url请求,是因为链家页面组织在组织结构上进行类分级。通过爬虫入口得到的页面,是小区展示列表页,该页面只包含小区部分信息。通过点击列表页中某个小区标题,才能跳转至小区详情页面,该页面包含小区具体信息。
改造完lianjiaxiaoqu.py爬虫文件后,通过以下命令运行该爬虫:
scrapy crawl lianjiaxiaoqu
成功运行后,得到如下结果:
可以发现我们成功获取某个小区详细信息,同时小区信息上发两次请求过程也证实了parse()方法中请求顺序的正确性。
3. 总结
通过上述内容,我们成功获取了某个小区全部详情信息。下一片文章将介绍如何获取小区展示页面中全部小区详细信息以及信息入库。就上述内容如有问题,欢迎留言。
标签:span,text,链家,爬取,Scrapy,div,extract,class,first From: https://blog.csdn.net/somanybeans/article/details/142443057