用 Scrapy 抓取某家的楼盘信息
来源:互联网 发布:广州数据恢复 价格 编辑:程序博客网 时间:2024/04/28 19:42
最近想爬点东西,又不想造轮子,就用上了scrapy,顺便记录下自己踩过的坑和都做了些什么。
使用的软件版本:
Python 3.5.x
ipython 5.1.x
scrapy 1.4
准备阶段(在动手写之前,一定要先观察好标签位置!):
这里使用Firefox的插件firebug对进行页面标签确定:
该页面有好几个楼盘信息,所以在看到上面的<li>标签后,应该再找一下它的父节点<ul>:
这些就是想要抓的新楼盘列表,id也说明了该ul列表的作用。在子节点<li>中继续寻找到自己想要的信息,找完差不多就可以开始爬虫的编写了。
编写阶段:
scrapy startproject house(项目名字,我这里用了house)其文件夹内容:
house
├── house
│ ├── __init__.py
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── __pycache__
│ ├── settings.py
│ └── spiders
│ ├── __init__.py
│ └── __pycache__
└── scrapy.cfg
在items.py 文件里添加 item(保存爬取到的数据的容器;其使用方法和python字典类似)
import scrapyclass HouseItem(scrapy.Item): city = scrapy.Field() title = scrapy.Field() region = scrapy.Field() room = scrapy.Field() area = scrapy.Field() average = scrapy.Field() other = scrapy.Field()上面我加了城市,楼盘名字,位置,房间,面积,均价和其他,具体可以看自己需求定义。
在 house
/spiders/
目录下添加house
_spider.py ,并添加以下内容:
import scrapy# 从上一层目录导入items.py的HouseItem类from house.items import HouseItemclass HouseSpider(scrapy.Spider): # 爬虫名,不能冲突 name = 'house' # 请求开始 def start_requests(self): urls = [ 'http://bj.fang.lianjia.com/loupan/' ] # 对urls列表进行迭代请求 for url in urls: yield scrapy.Request(url=url, callback=self.parse) # 对响应的数据进行有选择性的抓取 def parse(self, response): for house in response.xpath('//ul[contains(@id, "house-lst")]/li'): item = HouseItem() item['title'] = house.xpath('.//h2').xpath('.//a[contains(@data-el, "xinfang")]/text()').extract() item['region'] = house.xpath('.//span[contains(@class, "region")]/text()').extract() item['room'] = house.xpath('.//div[contains(@class, "area")]/text()').re('\S*\w') item['area'] = house.xpath('.//div[contains(@class, "area")]/span/text()').extract() item['other'] = list(set(house.xpath('.//div[contains(@class, "other")]').xpath('.//span/text()').extract() + house.xpath('.//div[contains(@class, "type")]').xpath('.//span/text()').extract())) item['average'] = house.xpath('.//div[contains(@class, "average")]').re('.*\s*(\w*)\s.*>(.\d*).*\s*(\w.*)') yield item
PS:正则写得有点丑,以后再修改,先用着。
PPS:scrapy shell 网址,用来调试还不错,不过建议先装ipython,能补全关键字。
留给自己:这里的选择器一开始用得不对,卡了很久,总是多了些空元素[ ],后来重新观察web页面元素,才发现自己写得不对,改成上面这样才好了,所以准备阶段很重要!
做到这里,就可以运行爬虫了:
scrapy crawl house(上面定义的爬虫名)
不过 这样没有保存下数据,可以使用-o输出json格式数据
scrapy crawl house -o data.json当然还有其他格式的输出,可以看官网:https://docs.scrapy.org/en/latest/topics/feed-exports.html
只有第一页明显不够用,如何抓下一页呢?
同样,先进行下一页标签的获取:
本来很简单的,直接用 response.xpath("//a[contains(., '下一页')]//@href").extract_first() 应该提取到这个标签的href,不过不行,一番折腾也没发现有其他直接获取到该下一页标签的方法,没办法只能用它的父节点:
<div class="page-box house-lst-page-box" comp-module="page" data-xftrack="10139" page-url="/loupan/pg{page}/" page-data="{"totalPage":26,"curPage":1}">
这个父节点的page-data属性中包括了总页数和当前页,所以在当页基础上加1就可以到达下一页:
next_page = '/loupan/pg' + str(int(page[1]) + 1)
而且还要用总页数和当前页比较,来确定是最后一页,所以加上这些的house_spider.py 代码如下:
import scrapy# 从上一层目录导入items.py的HouseItem类from house.items import HouseItemclass HouseSpider(scrapy.Spider): # 爬虫名,不能冲突 name = 'house' # 请求开始 def start_requests(self): urls = [ 'http://bj.fang.lianjia.com/loupan/' ] # 对urls列表进行迭代请求 for url in urls: yield scrapy.Request(url=url, callback=self.parse) # 对响应的数据进行有选择性的抓取 def parse(self, response): for house in response.xpath('//ul[contains(@id, "house-lst")]/li'): item = HouseItem() item['title'] = house.xpath('.//h2').xpath('.//a[contains(@data-el, "xinfang")]/text()').extract() item['region'] = house.xpath('.//span[contains(@class, "region")]/text()').extract() item['room'] = house.xpath('.//div[contains(@class, "area")]/text()').re('\S*\w') item['area'] = house.xpath('.//div[contains(@class, "area")]/span/text()').extract() item['other'] = list(set(house.xpath('.//div[contains(@class, "other")]').xpath('.//span/text()').extract() + house.xpath('.//div[contains(@class, "type")]').xpath('.//span/text()').extract())) item['average'] = house.xpath('.//div[contains(@class, "average")]').re('.*\s*(\w*)\s.*>(.\d*).*\s*(\w.*)') yield item # 对属性中的总页数和当前页进行提取 page = response.xpath('//div[contains(@class, "page-box")]/@page-data').re('totalPage":(\w*).*:(\w*)') # 最后一页的页码和总页数一致 if page[0] != page[1]: next_page = '/loupan/pg' + str(int(page[1]) + 1) yield scrapy.Request(response.urljoin(next_page))
再运行一下爬虫,已经能抓到很多页的数据了。不过还是有些问题,有些楼盘信息出现缺失,这是怎么回事呢?
{"title": ["中骏西山天璟"], "room": ["3居/2居"], "area": ["建面 102~155m²"], "other": ["五证齐全", "在售", "低密度", "住宅"], "region": ["门头沟-龙泉镇城子大街东侧"], "average": ["均价", "67000", "元/平"]}{"title": ["炫立方"], "room": [], "area": [], "other": ["五证齐全", "在售", "商铺"], "region": ["顺义-南法信顺平路与南焦路交汇处向南50米路东"], "average": ["均价", "43000", "元/平"]}
找到对应的楼盘看了下缺失信息对应的标签,发现这些标签原本就没数据:
<div class="area"><span></span></div>
就是和爬虫本身并没有关系,所以后续要在pipelines.py 中添加过滤函数,对这些缺失信息的楼盘进行删除。
官方文档:https://docs.scrapy.org/en/latest/topics/item-pipeline.html
在数据比较少的情况,可以用json文件保存。但是数据多了json已经不够用了,这时候需要将它们保存到数据库中。
这里就用NoSQL非关系型的MongoDB来保存。
首先在settings.py 中添加:
MONGODB_HOST = 'localhost'MONGODB_PORT = 27017MONGODB_DB = 'house'MONGODB_COLLECTION = 'lianjia'
然后去掉ITEM_PIPELINES 那行的注释,并添加在pipelines.py 中自定义的类MongoDBPipeline:
ITEM_PIPELINES = { 'house.pipelines.HousePipeline': 200, 'house.pipelines.MongoDBPipeline': 300,}
在pipelines.py 中添加类MongoDBPipeline:
class MongoDBPipeline(object): def open_spider(self, spider): # 连接mongodb数据库 self.client = pymongo.MongoClient(host=settings['MONGODB_HOST'], port=settings['MONGODB_PORT']) # 设置数据库 self.db = self.client[settings['MONGODB_DB']] # 设置文档 self.collection = self.db[settings['MONGODB_COLLECTION']] def process_item(self, item, spider): # 向文档中插入一条数据 self.collection.insert_one(dict(item)) return item def close_spider(self, spider): # 数据库连接关闭 self.client.close()
这章就完成了对一个地区的新楼盘信息的抓取,以及存储。如果想抓其他地区的,只要在urls列表中添加其他地区的代码即可。
代码(与本文有些差别):Github
- 用 Scrapy 抓取某家的楼盘信息
- 配合scrapy,用请求方式抓取一些网站内容。例如抓取360手机应用APP信息。
- scrapy抓取网页信息插入MySQL
- 如何运用SCRAPY+MySQL抓取相关信息
- scrapy 抓取豆瓣Top250书籍信息
- scrapy抓取一个电影网站信息
- Scrapy框架抓取四川大学公共管理学院教师信息
- Scrapy抓取框架的介绍
- 用scrapy进行网页抓取
- 用scrapy进行网页抓取
- 用scrapy进行网页抓取
- 用scrapy进行网页抓取
- 用Scrapy抓取Yahoo! WOEID
- 『Scrapy』登录人人贷并抓取借贷人信息中遇到的坑
- 使用scrapy+IP代理+多线程爬虫对拉钩网在杭州互联网职位信息的抓取
- scrapy的简单应用-抓取链家数据
- 一个网站的诞生02--用Scrapy抓取数据
- 网络数据抓取及其R实现(以链家楼盘为例)
- 【每日一题】实现一个栈Stack,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值的操作)的时间复杂度为O(1)
- Liunx中find命令详解
- Unity3D UniRx ReactiveX响应式编程初探1 双击的检测
- PyQuery库使用
- H5页面调用客户端登陆说明----转(收藏)
- 用 Scrapy 抓取某家的楼盘信息
- 贪心,模拟,构造(HDU 5414,CRB and String)
- c xml 格式化对齐
- 第六周错题回顾
- 剑指offer--面试题25:合并两个排序的链表
- POJ-1149 PIGS(最大流)
- 进程和线程的简单解释
- java多线程之间通信
- 用tarjan求割点