Python爬虫系列(六)数据处理篇
一、前言:
- 我们在之前学习了爬虫的页面下载以及演示了如何用lxml和xpath来抽取数据。
- 本篇我们主要学习目标是:
- 1、将数据抽取部的代码分抽取出来进一步封装
- 2、实现安居客租房列表数据的抽取及下一页链接的抽取
实战部分:
- 原本我们启动爬虫的代码以及接收下载数据,数据解析等代码都集中在了Spider类中。
- 本次我们将增加一个Scheduler来负责爬虫的调度工作,sprider专注于数据的处理
SpiderException异常类代码(创建一个异常类来统一来管理异常):
-
代码如下:
class SpiderException(Exception): def __init__(self): super(SpiderException,self).__init__();
Scheduler类代码
-
代码如下:
from com.anjie.download import Download; from com.anjie.spider_exception import SpiderException; class Scheduler: # 待抓取队列 crawl_queue = []; download = None; spider = None; def __init__(self, download=None, spider=None): self.download = download; self.spider = spider; pass; # 调用该函数开始让爬虫开始工作 def start_craw(self): if not self.spider: raise SpiderException("spider obeject is None") if not hasattr(self.spider, 'start_url'): raise SpiderException("spider must have an start_url attribute") if not hasattr(self.spider, 'pager_back'): raise SpiderException("spider must have an pager_back method") self.crawl_queue.extend(self.spider.start_url); next_link = []; while self.crawl_queue: url = self.crawl_queue.pop(); html = self.download.download(url=url); temp_next_link = self.spider.pager_back(url, html); print('抽取的链接:') print(temp_next_link); if temp_next_link: next_link.extend(temp_next_link);
-
代码解析:
-
start_url字段:spider类增加一个start_url来存储起始链接。
-
pager_back回调:增加一个pager_back回调函数来接收下载好的页面,pager_back回调会返回一个需要继续请求的url列表集合。
Spider类代码
-
代码如下:
from com.anjie.download import Download; from lxml.html import etree; from com.anjie.scheduler import Scheduler; class Spider: # 待抓取队列 start_url = ['https://gz.zu.anjuke.com/?from=navigation']; def __init__(self): pass; def pager_back(self,current_url, html): next_link = []; root = etree.HTML(html); result = root.xpath('//div[@class="topbar "]//ul/li/*') for s in result: print(s.text); return next_link if __name__ == '__main__': sp = Spider(); sc = Scheduler(download=Download(),spider=sp); sc.start_craw();
-
代码解析:
-
在pager_back函数里面进行数据的解析,如果有需要继续请求的url,就放入next_link中返回。
-
这样我们就完成了代码的改造升级。
-
这样做的好处是将爬虫进行模块化,对于维护性与扩展性都是极好的。
抓取真实数据
-
接下来我们将完成本次的目标,通过使用lxml与xpath抽取出安居客的租房信息列表以及下一页的链接。
-
以上为我们通过使用chrome浏览器查看的数据列表对应的html代码。
-
我们可以通过class属性进行定位。
-
OK,我们看下编写了xpath抽取代码的版本:
from com.anjie.download import Download; from lxml.html import etree; from com.anjie.scheduler import Scheduler; class Spider: # 待抓取队列 start_url = ['https://gz.zu.anjuke.com/?from=navigation']; def __init__(self): pass; def pager_back(self,current_url, html): next_link = []; root = etree.HTML(html); list_result = root.xpath('//div[@class="maincontent"]//div[@class="zu-itemmod "]') for s in list_result: print(etree.tostring(s,encoding="utf-8",pretty_print=True,method="html").decode()); return next_link if __name__ == '__main__': sp = Spider(); sc = Scheduler(download=Download(),spider=sp); sc.start_craw();
-
代码解析:当然你也可以直接//div[@class=“zu-itemmod “]直接定位到列表,注意后面的空格,虽然我们在chrome浏览器中看不到空格,因为浏览器自动帮我们处理了。
-
我们看下抽取结果:
-
可以看到,我们成功获得了列表数据,接下来我们开始进行数据的精抽取,首先先封装一个house类来存储数据
class House: #标题 title=""; #房子请求链接 url=""; #房子类型 house_type="" #销售类型、出租类型 sale_type="" #精装修等 level = "" #楼层 floor_number="" # 房子所在地 area_name="" #具体地址 addr="" #联系人 user= "" #补充 supplement= ""
-
OK,类定义好之后我们就可以开始解析数据了。
-
代码修改如下:
from com.anjie.download import Download; from lxml.html import etree; from com.anjie.scheduler import Scheduler; class Spider: # 待抓取队列 start_url = ['https://gz.zu.anjuke.com/?from=navigation']; def __init__(self): pass; def pager_back(self, current_url, html): next_link = []; root = etree.HTML(html); list_result = root.xpath('//div[@class="maincontent"]//div[@class="zu-itemmod "]') house_list = []; house = None; print(len(list_result)) for node in list_result: print(list_result.index(node)) # print(etree.tostring(node,encoding="utf-8",pretty_print=True,method="html").decode()) # 抽取 title = node.xpath('.//div[@class="zu-info"]/h3/a/text()'); print("标题:%s" % title) # 抽取链接 url = node.xpath('.//div[@class="zu-info"]/h3/a/@href') print("url:%s" % url) temp = node.xpath('.//div[@class="zu-info"]/p[1]/text()') (house_type, sale_type, level, floor_number) = temp; # 抽取房屋类型 print("房屋类型:%s" % house_type) # 销售类型 print("销售类型:%s" % sale_type) # 房屋等级 print("房屋等级:%s" % level) # 房屋楼层 print("楼层:%s" % floor_number) # 抽取地址 area_name = node.xpath('.//div[@class="zu-info"]/address/a/text()') print("所在区域:%s" % area_name) area = node.xpath('.//div[@class="zu-info"]/address/text()') for ele in area: if len(ele.strip()) > 0: area = ele.strip(); break; print("详细地址:%s" % area); # 抽取联系人 user = node.xpath('.//div[@class="zu-info"]/p[2]/span/text()') print("联系人:%s" % user) supplement = node.xpath('.//div[@class="zu-info"]/p[2]/em/text()') print("补充:%s" % supplement) return next_link if __name__ == '__main__': sp = Spider(); sc = Scheduler(download=Download(), spider=sp); sc.start_craw();
-
运行结果如下:
-
第一页一共有60条数据,xpath的写法需要多写多练,找个demo实战一下,相信就能基本掌握了。
-
OK,数据数据拿到了接下来我们看下如何获取下一请求的链接。
-
我们先打开chrome看下下一页链接的抽取模式
-
xpath规则代码为:links = root.xpath(’//*[@class=“multi-page”]/a/@href’);
-
代码修改为:
from com.anjie.download import Download; from lxml.html import etree; from com.anjie.scheduler import Scheduler; class Spider: # 待抓取队列 start_url = ['https://gz.zu.anjuke.com/?from=navigation']; def __init__(self): pass; def pager_back(self, current_url, html): next_link = []; root = etree.HTML(html); list_result = root.xpath('//div[@class="maincontent"]//div[@class="zu-itemmod "]') house_list = []; house = None; print(len(list_result)) for node in list_result: print(list_result.index(node)) # print(etree.tostring(node,encoding="utf-8",pretty_print=True,method="html").decode()) # 抽取 title = node.xpath('.//div[@class="zu-info"]/h3/a/text()'); print("标题:%s" % title) # 抽取链接 url = node.xpath('.//div[@class="zu-info"]/h3/a/@href') print("url:%s" % url) temp = node.xpath('.//div[@class="zu-info"]/p[1]/text()') (house_type, sale_type, level, floor_number) = temp; # 抽取房屋类型 print("房屋类型:%s" % house_type) # 销售类型 print("销售类型:%s" % sale_type) # 房屋等级 print("房屋等级:%s" % level) # 房屋楼层 print("楼层:%s" % floor_number) # 抽取地址 area_name = node.xpath('.//div[@class="zu-info"]/address/a/text()') print("所在区域:%s" % area_name) area = node.xpath('.//div[@class="zu-info"]/address/text()') for ele in area: if len(ele.strip()) > 0: area = ele.strip(); break; print("详细地址:%s" % area); # 抽取联系人 user = node.xpath('.//div[@class="zu-info"]/p[2]/span/text()') print("联系人:%s" % user) supplement = node.xpath('.//div[@class="zu-info"]/p[2]/em/text()') print("补充:%s" % supplement) links = root.xpath('//*[@class="multi-page"]/a/@href'); #利用集合过滤重复的链接 s = set(links) next_link = list(s); print('抽取的链接:%s'%next_link) return next_link if __name__ == '__main__': sp = Spider(); sc = Scheduler(download=Download(), spider=sp); sc.start_craw();
-
运行结果:
-
可以看到,我们抽取到了多条请求链接,接下来我们再调整一下Scheduler的代码,让其将抽取的链接加入请求队列中
from com.anjie.download import Download; from com.anjie.spider_exception import SpiderException; class Scheduler: # 待抓取队列 crawl_queue = []; #已爬取url crawl_over_queue = []; download = None; spider = None; def __init__(self, download=None, spider=None): self.download = download; self.spider = spider; pass; # 调用该函数开始让爬虫开始工作 def start_craw(self): if not self.spider: raise SpiderException("spider obeject is None") if not hasattr(self.spider, 'start_url'): raise SpiderException("spider must have an start_url attribute") if not hasattr(self.spider, 'pager_back'): raise SpiderException("spider must have an pager_back method") self.crawl_queue.extend(self.spider.start_url); while self.crawl_queue: url = self.crawl_queue.pop(); html = self.download.download(url=url); if html: self.crawl_over_queue.append(url); temp_next_link = self.spider.pager_back(url, html); for u in temp_next_link: if not u in self.crawl_over_queue and u not in self.crawl_queue: self.crawl_queue.append(u);
-
ok,我们还增加了crawl_over_queue来存储已经抓取的url,在获得next_url后我们需要先过滤,看url是否已在已请求队列中或者是否已在待请求队列中,如果都不在我们才将其加入待请求队列。如此便可自动增加下一页请求的url了。
-
本次我们学习了数据的解析抽取部分,但是数据取出来了,总要个地方存放。
-
我们将在下篇继续学习数据的存储,欢迎关注。