什么是爬虫?
如果是没有接触过爬虫的人可能会有些许疑惑,爬虫是个什么东西呢?其实爬虫的概念很简单,在互联网时代,万维网已然是大量信息的载体,如何有效地利用并提取这些信息是一个巨大的挑战。当我们使用浏览器对某个网站发送请求时,服务器会响应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,各位看官求请吐槽。