什么是爬虫?
如果是没有接触过爬虫的人可能会有些许疑惑,爬虫是个什么东西呢?其实爬虫的概念很简单,在互联网时代,万维网已然是大量信息的载体,如何有效地利用并提取这些信息是一个巨大的挑战。当我们使用浏览器对某个网站发送请求时,服务器会响应HTML文本并由浏览器来进行渲染显示。爬虫正是利用了这一点,通过程序模拟用户的请求,来获得HTML的内容,并从中提取需要的数据和信息。如果把网络想象成一张蜘蛛网,爬虫程序则像是蜘蛛网上的蜘蛛,不断地爬取数据与信息。
爬虫的概念非常简单易懂,利用python内置的urllib库都可以实现一个简单的爬虫,下面的代码是一个非常简单的爬虫,只要有基本的python知识应该都能看懂。它会收集一个页面中的所有<a>标签(没有做任何规则判断)中的链接,然后顺着这些链接不断地进行深度搜索。
|
|
但是我们如果想要实现一个性能高效的爬虫,那需要的复杂度也会增长,本文旨在快速实现,所以我们需要借助他人实现的爬虫框架来当做脚手架,在这之上来构建我们的图片爬虫(如果有时间的话当然也鼓励自己造轮子啦)。
本文作者为: SylvanasSun(sylvanas.sun@gmail.com).转载请务必将下面这段话置于文章开头处(保留超链接).
本文首发自SylvanasSun Blog,原文链接: https://sylvanassun.github.io/2017/09/20/2017-09-20-PictureSpider/
BeautifulSoup
BeautifulSoup是一个用于从HTML和XML中提取数据的python库。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。
利用好BeautifulSoup可以为我们省去许多编写正则表达式的时间,如果当你需要更精准地进行搜索时,BeautifulSoup也支持使用正则表达式进行查询。
BeautifulSoup3已经停止维护了,现在基本使用的都是BeautifulSoup4,安装BeautifulSoup4很简单,只需要执行以下的命令。
|
|
然后从bs4模块中导入BeautifulSoup对象,并创建这个对象。
|
|
创建BeautifulSoup对象需要传入两个参数,第一个是需要进行解析的HTML内容,第二个参数为解析器的名字(如果不传入这个参数,BeautifulSoup会默认使用python内置的解析器html.parser)。BeautifulSoup支持多种解析器,有lxml、html5lib、html.parser。
第三方解析器需要用户自己安装,本文中使用的是lxml解析器,安装命令如下(它还需要先安装C语言库)。
|
|
下面以一个例子演示使用BeautifulSoup的基本方式,如果还想了解更多可以去参考BeautifulSoup文档。
|
|
Scrapy
Scrapy是一个功能强大的爬虫框架,它已经实现了一个性能高效的爬虫结构,并提供了很多供程序员自定义的配置。使用Scrapy只需要在它的规则上编写我们的爬虫逻辑即可。
首先需要先安装Scrapy,执行命令pip install scrapy。然后再执行命令scrapy startproject 你的项目名来生成Scrapy的基本项目文件夹。生成的项目结构如下。
|
|
scrapy.cfg: 项目的配置文件。items.py:物品模块,用户需要在这个模块中定义数据封装的实体类。pipelines.py:管道模块,用户需要在这个模块中定义处理数据的逻辑(如存储到数据库等)。settings.py:这个模块定义了整个项目中的各种配置变量。spiders/:在这个包中定义用户自己的爬虫模块。
启动Scrapy的爬虫也很简单,只需要执行命令scrapy crawl 你的爬虫名。下面介绍Scrapy中的关键模块的演示案例,如果想要了解有关Scrapy的更多信息,请参考Scrapy官方文档。
items
items模块主要是为了将爬取到的非结构化数据封装到一个结构化对象中,自定义的item类必须继承自scrapy.Item,且每个属性都要赋值为scrapy.Field()。
|
|
操作item对象就像操作一个dict对象一样简单。
|
|
pipelines
当一个Item经由爬虫封装之后将会到达Pipeline类,你可以定义自己的Pipeline类来决定将Item的处理策略。
每个Pipeline可以实现以下函数。
process_item(item, spider): 每个Pipeline都会调用此函数来处理Item,这个函数必须返回一个Item,如果在处理过程中遇见错误,可以抛出DropItem异常。open_spider(spider): 当spider开始时将会调用此函数,可以利用这个函数进行打开文件等操作。close_spider(spider):当spider关闭时将会调用此函数,可以利用这个函数对IO资源进行关闭。from_crawler(cls, crawler): 这个函数用于获取settings.py模块中的属性。注意这个函数是一个类方法。
|
|
当定义完你的Pipeline后,还需要在settings.py中对你的Pipeline进行设置。
|
|
spiders
在spiders模块中,用户可以通过自定义Spider类来制定自己的爬虫逻辑与数据封装策略。每个Spider都必须继承自class scrapy.spider.Spider,这是Scrapy中最简单的爬虫基类,它没有什么特殊功能,Scrapy也提供了其他功能不同的Spider类供用户选择,这里就不多叙述了,可以去参考官方文档。
用户可以通过以下属性来自定义配置Spider:
name: 这是Spider的名称,Scrapy需要通过这个属性来定位Spider并启动爬虫,它是唯一且必需的。allowed_domains: 这个属性规定了Spider允许爬取的域名。start_urls:Spider开始时将抓取的网页列表。start_requests(): 该函数是Spider开始抓取时启动的函数,它只会被调用一次,有的网站必须要求用户登录,可以使用这个函数先进行模拟登录。make_requests_from_url(url): 该函数接收一个url并返回Request对象。除非重写该函数,否则它会默认以parse(response)函数作为回调函数,并启用dont_filter参数(这个参数是用于过滤重复url的)。parse(response): 当请求没有设置回调函数时,则会默认调用parse(response)。log(message[, level, component]): 用于记录日志。closed(reason): 当Spider关闭时调用。
|
|
其他依赖库
Requests
Requests也是一个第三方python库,它比python内置的urllib更加简单好用。只需要安装(pip install requests),然后导包后,即可轻松对网站发起请求。
|
|
关于更多的参数与内容请参考Requests文档。
BloomFilter
BloomFilter是一个用于过滤重复数据的数据结构,我们可以使用它来对重复的url进行过滤。本文使用的BloomFilter来自于python-bloomfilter,其他操作系统用户请使用pip install pybloom命令安装,windows用户请使用pip install pybloom-live(原版对windows不友好)。
分析
介绍了需要的依赖库之后,我们终于可以开始实现自己的图片爬虫了。我们的目标是爬https://www.deviantart.com/网站中的图片,在写爬虫程序之前,还需要先分析一下页面的HTML结构,这样才能针对性地找到图片的源地址。
为了保证爬到的图片的质量,我决定从热门页面开始爬,链接为https://www.deviantart.com/whats-hot/。
打开浏览器的开发者工具后,可以发现每个图片都是由一个a标签组成,每个a标签的class为torpedo-thumb-link,而这个a标签的href正好就是这张图片的详情页面(如果我们从这里就开始爬图片的话,那么爬到的可都只是缩略图)。

进入到详情页后,不要马上爬取当前图片的源地址,因为当前页显示的图片并不是原始格式,我们对图片双击放大之后再使用开发者工具抓到这个图片所在的img标签后,再让爬虫获取这个标签中的源地址。

在获得图片的源地址之后,我的策略是让爬虫继续爬取该页中推荐的更多图片,通过开发者工具,可以发现这些图片都被封装在一个class为tt-crop thumb的div标签中,而该标签里的第一个a子标签正好就是这个图片的详情页链接。

初始配置
在对网页的HTML进行分析之后,可以开始写程序了,首先先用Scrapy的命令来初始化项目。之后在settings.py中做如下配置。
|
|
然后定义我们的Item。
|
|
创建自己的spider模块与Spider类。
|
|
DeviantArtImageSpider继承自CrawlSpider,该类是Scrapy最常用的Spider类,它通过Rule类来定义爬取链接的规则,上述代码中使用了正则表达式https://www.deviantart.com/whats-hot/[\?\w+=\d+]*,这个正则表达式将访问每一页的热门页面。
解析热门页面
爬虫启动时将会先访问热门页面,请求得到响应之后会调用回调函数,我们需要在这个回调函数中获取上述分析中得到的<a class = 'torpedo-thumb-link'>标签,然后抽取出每张图片的详情页链接。
|
|
解析详情页
parse_page()函数会不断地发送请求到详情页链接,解析详情页的回调函数需要处理数据封装到Item,还需要提取详情页中更多图片的详情链接然后发送请求。
|
|
处理Item
对于Item的处理,只是简单地将图片命名与下载到本地。我没有使用多进程或者多线程,也没有使用Scrapy自带的ImagePipeline(自由度不高),有兴趣的童鞋可以自己选择实现。
|
|
在settings.py中注册该Pipeline
|
|
IP代理池
有些网站会有反爬虫机制,为了解决这个问题,每次请求都使用不同的IP代理,有很多网站提供IP代理服务,我们需要写一个爬虫从云代理中抓取它提供的免费IP代理(免费IP很不稳定,而且我用了代理之后反而各种请求失败了Orz…)。
|
|
得到了IP代理池之后,还要在Scrapy的middlewares.py模块定义代理中间件类。
|
|
最后在settings.py中进行注册。
|
|
End
我们的图片爬虫已经完成了,执行命令scrapy crawl deviant_art_image_spider,然后尽情搜集图片吧!

想要获得本文中的完整源代码与P站爬虫请点我,顺便求个star…
最近心血来潮想要写爬虫,所以花了点时间过了一遍
python语法便匆匆上手了,代码写的有点丑也不够pythonic,各位看官求请吐槽。