目录

python爬虫系列(四)

一、前言:

  • 我们在之前学习了robots.txt文件的读取及解析,站点技术的分析以及用简单的demo演示了网页的下载过程。
  • 本篇我们将以爬取安居客站点为例子,希望大家学会了爬虫后,直接用爬虫来找房子会更方便哦,再也不用久不久盯着软件看了。
  • 我们本次的目标是抓取安居客的顶部信息如下:

http://upload-images.jianshu.io/upload_images/1811893-05c6d4e1a2fd1e9d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

二、Download函数的重构

  • 在开始之前,我们先对之前编写的download函数做一次封装,将其封装为类。

  • 这样做的目的主要是为后续的扩展做准备,程序就像滚雪球一样,我们会越滚越大的哦。

  • OK,我也不瞎逼逼了,直接上代码:

      from urllib import request, error, response
    
      # 默认的重试次数
      DEFAULT_RETRIES = 1
      DEFAULT_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 ';
    
      class Download:
      	# 重试次数
        num_retries = 0;
      	user_agent = None;
    
      	def __init__(self, num_retries=DEFAULT_RETRIES,user_agent=DEFAULT_AGENT):
      		self.num_retries = num_retries;
      		self.user_agent = user_agent;
    
      	def download(self,url):
      		print('download url is %s' % url);
      		html = None;
      		try:
      			rq = request.Request(url=url);
      			rq.add_header('User-Agent',self.user_agent);
      			rp = request.urlopen(rq);
      			print('download over url is: %s' % rp.geturl())
      			html = rp.read();
      		except error.URLError as e:
      			print('download  error :%s' % e.reason);
      			html = None;
      			if self.num_retries > 0:
      				if hasattr(e, 'code') and 500 <= e.code < 600:
      					return self.download(url, self.num_retries - 1);
      				pass;
    
      		return html;
    
      if __name__ == '__main__':
      	d = Download();
      	print(d.download('http://www.baidu.com'))
    
  • 代码如上,我们还将重试次数,代理等抽出来作为对象的属性,后续我们的属性会更多,这次我为什么添加了代理呢?因为安居客对爬虫做了限制,不设置代理就获取不了数据的哦。

三、Spider类的编写:

  • 我们再创建一个spider类来负责爬虫的控制,其获取了url后就会调用我们的download进行下载。

  • 刚开始我们的类也很简单,内部目前主要实例化一个download对象来下载url,一个crawl_queue存储等待下载的url,代码如下:

      from com.anjie.download import Download;
    
      class Spider:
      	#待抓取队列
        crawl_queue = [];
      	download = None;
      	def __init__(self):
      		self.download = Download();
      		pass;
    
      	#调用该函数开始让爬虫开始工作
        def start_craw(self,start_url):
      		self.crawl_queue = [start_url];
    
      		while self.crawl_queue:
      			url = self.crawl_queue.pop();
      			html = self.download.download(url);
      			print(html)
    
      if __name__=='__main__':
      	s = Spider();
      	#该url,你直接用浏览器打开安居客,点击租房即可获取
      	s.start_craw('https://gz.zu.anjuke.com/?from=navigation');
    
  • Ok,我们的Spider就写好来,此时我们运行时会发现。真的下载了哦,小编没有骗你哦,不过下载后返回的html小编表示看的好郁闷。

http://upload-images.jianshu.io/upload_images/1811893-7bd7c9e015e3fdc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

  • 不要着急,此时我们该使用神器了。
  • 此时在家可以借助Chrome的调试工具来分析数据的标签,然后通过正则表达式来获取,不过实际开发过程中,这种做法肯定是行不通的。
  • 接下来我们将为大家介绍两个神器:Beautiful Soup和Lxml

四、数据格式化神器:Beautiful Soup和Lxml

Beautiful Soup
  • Beautiful Soup是一个可以从 HTML 或 XML 文件中提取数据的Python库,该模块可以帮助我们解析网页, 并提供定位内容的便捷接口。
Beautiful Soup使用知识补充:
  • Beautiful Soup能将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,而所有对象我们可以可以归纳为以下4种:
1、Tag
  • HTML 中的一个个标签,例如:<title></title>
  • 使用也是非常简单的,如我想获取html中的title标签的内容:

soup = BeautifulSoup(html) #加载html,返回一个BeautifulSoup对象 soup.title : 如此便是取里面的title标签的内容了

  • 对于Tag来说,其有两个属性:
  • name : 其为标签的名字,如<a></a>的name就是a
  • attrs:标签上的属性集合,返回的是一个字典,如a标签有class属性,id等。如果想获取属性的值可以:soup.a[‘class’]即可
  • 注意事项:虽然这样子很好用,但是他取的都是第一个标签哦,也就是说假设你有很多 a 标签,他也会支取第一个,这样子局限性肯定很大是不。
2、NavigableString:
  • 这个属性很有意思:我们前面已经获取了tag是不?我们想获取tag里的值该怎么办呢?此时我们可以使用:soup.a.string, 这样便获取了里面的值,而执行这句返回的就是一个NavigableString类型的对象了。
3、BeautifulSoup :
  • BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性
4、Comment:
  • Comment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。
  • 也就是他能打印标签里的注释内容。
Beautiful Soup安装:

python3如下:pip3 install beautifulsup4 python2如下:pip install beautifulsup4

  • Ok,安装好之后,我们来试一下。

  • 代码修改如下:

      from com.anjie.download import Download;
      from bs4 import BeautifulSoup
    
      class Spider:
      	# 待抓取队列
        crawl_queue = [];
      	download = None;
    
      	def __init__(self):
      		self.download = Download();
      		pass;
    
      	# 调用该函数开始让爬虫开始工作
        def start_craw(self, start_url):
      		self.crawl_queue = [start_url];
    
      		while self.crawl_queue:
      			url = self.crawl_queue.pop();
      			bad_html = self.download.download(url);
      			soup = BeautifulSoup(bad_html, 'html.parser');
      			#执行了prettify我们的html就会变得很美丽哦,这里就不打印出来了
      			html = soup.prettify();
      			tag = soup.find_all('ul', attrs={'class': 'nav-site'});
      			print(tag)
    
      if __name__ == '__main__':
      	s = Spider();
      	s.start_craw('https://gz.zu.anjuke.com/?from=navigation');
    
  • 运行结果如下:

http://upload-images.jianshu.io/upload_images/1811893-7f17858e0b3a4c37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240

  • 好开心,Ok,我们拿到了顶部导航的数据。而且BeautifulSoup返回给我们的是一个BeautifulSoup对象,其提供了非常丰富的api供我们查找相关的tag和内容。
  • 如查找所有的a标签内的href的内容:

for link in soup.find_all(‘a’): print(link.get(‘href’))

  • 虽然Beautiful Soup已经很强大了,但是我想说,我更喜欢lxml,因为它支持xpath语法。
Lxml
  • Lxml 是基于 libxml2 这一XML解析库的Python封装。 该模块使用C语言编写,解析速度比Beautiful Soup更快,最新版本的lxml支持CPython2.6至3.6的版本。
  • Lxml也是唯一支持解析XMl的库哦。
  • 其实我安装时感觉挺简单的,虽然网上说安装比较复杂。
  • 我一不小心执行了:pip3 install lxml 就搞定了
Lxml常用方法及属性详解:
  • 1、fromstring(html, base_url=None, parser=None, **kwargs) :将字符型html文档转换为节点树或文档树
  • 2、 tostring(doc, pretty_print=False, include_meta_content_type=False, encoding=None, method=“html”, with_tail=True, doctype=None) : 将节点树或文档树序列化为字符型
  • 3、base_url : 文档url
  • 4、head : 标签部分
  • 5、body : 标签部分
  • 6、forms : 返回全部form列表
  • 7、label : 元素的label标签
  • 8、classes : class属性值的集合
  • 9、drop_tag(self) : 移除标签,但不移除其子标签和text文本,将其合并到父节点
  • 10、drop_tree(self) : 移除节点树(包含子节点和text),但不移除它的tail文本,将其合并到父节点或前一个兄弟节点
  • 11、find_class(self, class_name) : 根据class属性值查找节点元素
  • 12、get_element_by_id(self, rel) : 根据id属性值查找节点元素
  • 13、set(self, key, value=None) :设置节点元素的属性
  • 14、text_content(self) : 返回其后代节点与其自身的全部text内容
  • 示例代码如下:

      from com.anjie.download import Download;
    
      from lxml import html as lhtml;
    
      class Spider:
      	# 待抓取队列
        crawl_queue = [];
      	download = None;
    
      	def __init__(self):
      		self.download = Download();
      		pass;
    
      	# 调用该函数开始让爬虫开始工作
        def start_craw(self, start_url):
      		self.crawl_queue = [start_url];
    
      		while self.crawl_queue:
      			url = self.crawl_queue.pop();
      			bad_html = self.download.download(url);
      			bad_html = bad_html.decode('utf-8')
      			root =  lhtml.fromstring(bad_html);
      			result = root.xpath('//div[@class="topbar "]//ul/li/*')
    
      			for s in result:
      				print(s.text)
    
      if __name__ == '__main__':
      	s = Spider();
      	s.start_craw('https://gz.zu.anjuke.com/?from=navigation');
    
  • 输出为:

      download url is https://gz.zu.anjuke.com/?from=navigation
      download over url is: https://gz.zu.anjuke.com/?from=navigation
      首 页
      新 房
      二手房
      租 房
      商铺写字楼
      海外地产
      楼讯
      房 价
      问 答
    
  • Lxml不仅与Beautiful Soup一样支持api获取数据,还支持xpath和css抽取器。

  • 由于其功能强大,后面我们将单独抽出一篇来讲解lxml的使用,这里我们只需要知道有这么个东西即可。