分布式爬虫:使用Scrapy抓取数据
2016-07-23 10:57
477 查看
Scrapy是Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
官方主页: http://www.scrapy.org/
中文文档:Scrapy 0.22 文档
GitHub项目主页:https://github.com/scrapy/scrapy
Scrapy 使用了 Twisted 异步网络库来处理网络通讯。整体架构大致如下(注:图片来自互联网):
Scrapy主要包括了以下组件:
引擎,用来处理整个系统的数据流处理,触发事务。
调度器,用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。
下载器,用于下载网页内容,并将网页内容返回给蜘蛛。
蜘蛛,蜘蛛是主要干活的,用它来制订特定域名或网页的解析规则。
项目管道,负责处理有蜘蛛从网页中抽取的项目,他的主要任务是清晰、验证和存储数据。当页面被蜘蛛解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
下载器中间件,位于Scrapy引擎和下载器之间的钩子框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
蜘蛛中间件,介于Scrapy引擎和蜘蛛之间的钩子框架,主要工作是处理蜘蛛的响应输入和请求输出。
调度中间件,介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。
使用Scrapy可以很方便的完成网上数据的采集工作,它为我们完成了大量的工作,而不需要自己费大力气去开发。
Scrapy 目前最新版本为0.22.2,该版本需要 python 2.7,故需要先安装 python 2.7。这里我使用 centos 服务器来做测试,因为系统自带了 python ,需要先检查 python 版本。
查看python版本:
升级版本到2.7:
建立软连接,使系统默认的 python指向 python2.7
再次查看python版本:
这里使用 wget 的方式来安装 setuptools :
Scrapy 使用了 Twisted 异步网络库来处理网络通讯,故需要安装 twisted。
安装 twisted 前,需要先安装 gcc:
然后,再通过 easy_install 安装 twisted:
如果出现下面错误:
请安装 python-devel 然后再次运行:
如果出现下面异常:
请手动下载然后安装,下载地址在这里
先安装一些依赖:
然后,再通过 easy_install 安装 pyOpenSSL:
先安装一些依赖:
最后再来安装 Scrapy :
在安装成功之后,你可以了解一些 Scrapy 的基本概念和使用方法,并学习 Scrapy 项目的例子 dirbot 。
Dirbot 项目位于 https://github.com/scrapy/dirbot,该项目包含一个 README 文件,它详细描述了项目的内容。如果你熟悉
git,你可以 checkout 它的源代码。或者你可以通过点击 Downloads 下载 tarball 或 zip 格式的文件。
下面以该例子来描述如何使用 Scrapy 创建一个爬虫项目。
在抓取之前,你需要新建一个 Scrapy 工程。进入一个你想用来保存代码的目录,然后执行:
这个命令会在当前目录下创建一个新目录 tutorial,它的结构如下:
这些文件主要是:
scrapy.cfg: 项目配置文件
tutorial/: 项目python模块, 呆会代码将从这里导入
tutorial/items.py: 项目items文件
tutorial/pipelines.py: 项目管道文件
tutorial/settings.py: 项目配置文件
tutorial/spiders: 放置spider的目录
Items是将要装载抓取的数据的容器,它工作方式像 python 里面的字典,但它提供更多的保护,比如对未定义的字段填充以防止拼写错误。
它通过创建一个
我们通过将需要的item模型化,来控制从 dmoz.org 获得的站点数据,比如我们要获得站点的名字,url 和网站描述,我们定义这三种属性的域。要做到这点,我们编辑在 tutorial 目录下的 items.py 文件,我们的 Item 类将会是这样
刚开始看起来可能会有些困惑,但是定义这些 item 能让你用其他 Scrapy 组件的时候知道你的 items 到底是什么。
Spider 是用户编写的类,用于从一个域(或域组)中抓取信息。们定义了用于下载的URL的初步列表,如何跟踪链接,以及如何来解析这些网页的内容用于提取items。
要建立一个 Spider,你可以为
URL 中继承性生成。
这个方法负责解析返回的数据、匹配抓取的数据(解析为 item )并跟踪更多的 URL。
在 tutorial/spiders 目录下创建 DmozSpider.py
该命令从 dmoz.org 域启动爬虫,第三个参数为 DmozSpider.py 中的 name 属性值。
Scrapy 使用一种叫做 XPath selectors 的机制,它基于 XPath 表达式。如果你想了解更多selectors和其他机制你可以查阅资料。
这是一些XPath表达式的例子和他们的含义:
标签元素
这只是几个使用 XPath 的简单例子,但是实际上 XPath 非常强大。如果你想了解更多 XPATH 的内容,我们向你推荐这个XPath
教程
为了方便使用 XPaths,Scrapy 提供 Selector 类, 有三种方法
我们可以通过如下命令选择每个在网站中的
然后是网站描述:
网站标题:
网站链接:
如前所述,每个
django 的 Model 与
Item 通常是在 Spider 的 parse 方法里使用,它用来保存解析到的数据。
最后修改爬虫类,使用 Item 来保存数据,代码如下:
现在,可以再次运行该项目查看运行结果:
在 settings.py 中设置
django 的
从 Spider 的 parse 返回的 Item 数据将依次被
一个 Item Pipeline 类必须实现以下方法:
item 将不会被之后的 pipeline 处理。参数:
也可额外的实现以下两个方法:
保存信息的最简单的方法是通过 Feed exports,命令如下:
除了 json 格式之外,还支持 JSON lines、CSV、XML格式,你也可以通过接口扩展一些格式。
对于小项目用这种方法也足够了。如果是比较复杂的数据的话可能就需要编写一个 Item Pipeline 进行处理了。
所有抓取的 items 将以 JSON 格式被保存在新生成的 items.json 文件中
上面描述了如何创建一个爬虫项目的过程,你可以参照上面过程联系一遍。作为学习的例子,你还可以参考这篇文章:scrapy 中文教程(爬cnbeta实例) 。
这篇文章中的爬虫类代码如下:
需要说明的是:
该爬虫类继承的是
该类并没有实现parse方法,并且规则中定义了回调函数
接触 Scrapy,是因为想爬取一些知乎的数据,最开始的时候搜索了一些相关的资料和别人的实现方式。
Github 上已经有人或多或少的实现了对知乎数据的爬取,我搜索到的有以下几个仓库:
https://github.com/KeithYue/Zhihu_Spider 实现先通过用户名和密码登陆再爬取数据,代码见 zhihu_spider.py。
https://github.com/immzz/zhihu-scrapy 使用 selenium 下载和执行 javascript
代码。
https://github.com/tangerinewhite32/zhihu-stat-py
https://github.com/Zcc/zhihu 主要是爬指定话题的topanswers,还有用户个人资料,添加了登录代码。
https://github.com/pelick/VerticleSearchEngine 基于爬取的学术资源,提供搜索、推荐、可视化、分享四块。使用了
Scrapy、MongoDB、Apache Lucene/Solr、Apache Tika等技术。
https://github.com/geekan/scrapy-examples scrapy的一些例子,包括获取豆瓣数据、linkedin、腾讯招聘数据等例子。
https://github.com/owengbs/deeplearning 实现分页获取话题。
https://github.com/gnemoug/distribute_crawler 使用scrapy、redis、mongodb、graphite实现的一个分布式网络爬虫,底层存储mongodb集群,分布式使用redis实现,爬虫状态显示使用graphite实现
https://github.com/weizetao/spider-roach 一个分布式定向抓取集群的简单实现。
其他资料:
http://www.52ml.net/tags/Scrapy 收集了很多关于 Scrapy 的文章,推荐阅读
用Python Requests抓取知乎用户信息
使用scrapy框架爬取自己的博文
Scrapy 深入一点点
使用python,scrapy写(定制)爬虫的经验,资料,杂。
Scrapy 轻松定制网络爬虫
在scrapy中怎么让Spider自动去抓取豆瓣小组页面
scrapy 和 javascript 交互例子:
用scrapy框架爬取js交互式表格数据
scrapy + selenium 解析javascript 实例
还有一些待整理的知识点:
如何先登陆再爬数据
如何使用规则做过滤
如何递归爬取数据
scrapy的参数设置和优化
如何实现分布式爬取
官方主页: http://www.scrapy.org/
中文文档:Scrapy 0.22 文档
GitHub项目主页:https://github.com/scrapy/scrapy
Scrapy 使用了 Twisted 异步网络库来处理网络通讯。整体架构大致如下(注:图片来自互联网):
Scrapy主要包括了以下组件:
引擎,用来处理整个系统的数据流处理,触发事务。
调度器,用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。
下载器,用于下载网页内容,并将网页内容返回给蜘蛛。
蜘蛛,蜘蛛是主要干活的,用它来制订特定域名或网页的解析规则。
项目管道,负责处理有蜘蛛从网页中抽取的项目,他的主要任务是清晰、验证和存储数据。当页面被蜘蛛解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
下载器中间件,位于Scrapy引擎和下载器之间的钩子框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
蜘蛛中间件,介于Scrapy引擎和蜘蛛之间的钩子框架,主要工作是处理蜘蛛的响应输入和请求输出。
调度中间件,介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。
使用Scrapy可以很方便的完成网上数据的采集工作,它为我们完成了大量的工作,而不需要自己费大力气去开发。
1. 安装
安装 python
Scrapy 目前最新版本为0.22.2,该版本需要 python 2.7,故需要先安装 python 2.7。这里我使用 centos 服务器来做测试,因为系统自带了 python ,需要先检查 python 版本。查看python版本:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>python -<span class="hljs-constant" style="color: rgb(136, 0, 0);">V</span> <span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span> <span class="hljs-number" style="color: rgb(0, 136, 0);">2.6</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span> </code>
升级版本到2.7:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span><span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span> <span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span><span class="hljs-symbol" style="color: rgb(136, 0, 0);">:</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>wget <span class="hljs-symbol" style="color: rgb(136, 0, 0);">http:</span>/<span class="hljs-regexp" style="color: rgb(0, 136, 0);">/python.org/ftp</span><span class="hljs-regexp" style="color: rgb(0, 136, 0);">/python/</span><span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span>/<span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span>.tar.xz <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>tar xf <span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span>.tar.xz <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>cd <span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>./configure --prefix=<span class="hljs-regexp" style="color: rgb(0, 136, 0);">/usr/local</span> --enable-unicode=ucs4 --enable-shared <span class="hljs-constant" style="color: rgb(136, 0, 0);">LDFLAGS</span>=<span class="hljs-string" style="color: rgb(136, 136, 255);">"-Wl,-rpath /usr/local/lib"</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>make && make altinstall </code>
建立软连接,使系统默认的 python指向 python2.7
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>mv /usr/bin/python /usr/bin/python2.<span class="hljs-number" style="color: rgb(0, 136, 0);">6.6</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>ln -s /usr/local/bin/python2.<span class="hljs-number" style="color: rgb(0, 136, 0);">7</span> /usr/bin/python </code>
再次查看python版本:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>python -<span class="hljs-constant" style="color: rgb(136, 0, 0);">V</span> <span class="hljs-constant" style="color: rgb(136, 0, 0);">Python</span> <span class="hljs-number" style="color: rgb(0, 136, 0);">2.7</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">6</span> </code>
安装
这里使用 wget 的方式来安装 setuptools :<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>wget <span class="hljs-symbol" style="color: rgb(136, 0, 0);">https:</span>/<span class="hljs-regexp" style="color: rgb(0, 136, 0);">/bootstrap.pypa.io/ez</span>_setup.py -<span class="hljs-constant" style="color: rgb(136, 0, 0);">O</span> - | python </code>
安装 zope.interface
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">$ easy_install zope.<span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">interface</span> </code>
安装 twisted
Scrapy 使用了 Twisted 异步网络库来处理网络通讯,故需要安装 twisted。安装 twisted 前,需要先安装 gcc:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>yum install gcc -y </code>
然后,再通过 easy_install 安装 twisted:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>easy_install twisted </code>
如果出现下面错误:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">$ easy_install twisted Searching <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">for</span> twisted Reading <span class="hljs-attribute" style="color: rgb(136, 0, 0);">https</span>:<span class="hljs-regexp" style="color: rgb(0, 136, 0);">//</span>pypi.python.org<span class="hljs-regexp" style="color: rgb(0, 136, 0);">/simple/twisted/</span> Best <span class="hljs-attribute" style="color: rgb(136, 0, 0);">match</span>: Twisted <span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span> Downloading <span class="hljs-attribute" style="color: rgb(136, 0, 0);">https</span>:<span class="hljs-regexp" style="color: rgb(0, 136, 0);">//</span>pypi.python.org/packages/source/T/Twisted/Twisted-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span>.tar.bz2<span class="hljs-comment" style="color: rgb(136, 136, 136);">#md5=9625c094e0a18da77faa4627b98c9815</span> Processing Twisted-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span>.tar.bz2 Writing /tmp/easy_install-kYHKjn/Twisted-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span>/setup.cfg Running Twisted-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span>/setup.py -q bdist_egg --dist-dir /tmp/easy_install-kYHKjn/Twisted-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span><span class="hljs-number" style="color: rgb(0, 136, 0);">.0</span>/egg-dist-tmp-vu1n6Y twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">10</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">20</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: Python.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">h</span>: No such file <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">or</span> directory twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">14</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: expected ‘=’, ‘,’, ‘;’, ‘asm’ <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">or</span> ‘__attribute__’ before ‘*’ token twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">31</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: expected ‘=’, ‘,’, ‘;’, ‘asm’ <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">or</span> ‘__attribute__’ before ‘*’ token twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">45</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: expected ‘=’, ‘,’, ‘;’, ‘asm’ <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">or</span> ‘__attribute__’ before ‘PortmapMethods’ twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>: In <span class="hljs-reserved">function</span> ‘initportmap’: twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">55</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">warning</span>: implicit declaration <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">of</span> <span class="hljs-reserved">function</span> ‘Py_InitModule’ twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">55</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: ‘PortmapMethods’ undeclared (first use <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">in</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">this</span> <span class="hljs-reserved">function</span>) twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">55</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: (Each undeclared identifier <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">is</span> reported only once twisted/runner/portmap.<span class="hljs-attribute" style="color: rgb(136, 0, 0);">c</span>:<span class="hljs-number" style="color: rgb(0, 136, 0);">55</span>: <span class="hljs-attribute" style="color: rgb(136, 0, 0);">error</span>: <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">for</span> each <span class="hljs-reserved">function</span> it appears <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">in</span>.) </code>
请安装 python-devel 然后再次运行:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>yum install python-devel -y <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>easy_install twisted </code>
如果出现下面异常:
<code style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-attribute">error</span>: <span class="hljs-string" style="color: rgb(136, 0, 0);">Not a recognized archive type: /tmp/easy_install-tVwC5O/Twisted-14.0.0.tar.bz2</span> </code>
请手动下载然后安装,下载地址在这里
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>wget <span class="hljs-symbol" style="color: rgb(136, 0, 0);">https:</span>/<span class="hljs-regexp" style="color: rgb(0, 136, 0);">/pypi.python.org/packages</span><span class="hljs-regexp" style="color: rgb(0, 136, 0);">/source/</span><span class="hljs-constant" style="color: rgb(136, 0, 0);">T</span>/<span class="hljs-constant" style="color: rgb(136, 0, 0);">Twisted</span>/<span class="hljs-constant" style="color: rgb(136, 0, 0);">Twisted</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">0</span>.tar.bz2<span class="hljs-comment" style="color: rgb(136, 136, 136);">#md5=9625c094e0a18da77faa4627b98c9815</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>tar -vxjf <span class="hljs-constant" style="color: rgb(136, 0, 0);">Twisted</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">0</span>.tar.bz2 <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>cd <span class="hljs-constant" style="color: rgb(136, 0, 0);">Twisted</span>-<span class="hljs-number" style="color: rgb(0, 136, 0);">14.0</span>.<span class="hljs-number" style="color: rgb(0, 136, 0);">0</span> <span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>python setup.py install </code>
安装 pyOpenSSL
先安装一些依赖:<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>yum install libffi libffi-devel openssl-devel -y </code>
然后,再通过 easy_install 安装 pyOpenSSL:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>easy_install pyOpenSSL </code>
安装 Scrapy
先安装一些依赖:<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>yum install libxml2 libxslt libxslt-devel -y </code>
最后再来安装 Scrapy :
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>easy_install scrapy</code>
2. 使用 Scrapy
在安装成功之后,你可以了解一些 Scrapy 的基本概念和使用方法,并学习 Scrapy 项目的例子 dirbot 。Dirbot 项目位于 https://github.com/scrapy/dirbot,该项目包含一个 README 文件,它详细描述了项目的内容。如果你熟悉
git,你可以 checkout 它的源代码。或者你可以通过点击 Downloads 下载 tarball 或 zip 格式的文件。
下面以该例子来描述如何使用 Scrapy 创建一个爬虫项目。
新建工程
在抓取之前,你需要新建一个 Scrapy 工程。进入一个你想用来保存代码的目录,然后执行:<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>scrapy startproject tutorial </code>
这个命令会在当前目录下创建一个新目录 tutorial,它的结构如下:
<code style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">. ├── scrapy.cfg └── tutorial ├── __init__.py ├── items.py ├── pipelines.py ├── settings.py └── spiders └── __init__.py </code>
这些文件主要是:
scrapy.cfg: 项目配置文件
tutorial/: 项目python模块, 呆会代码将从这里导入
tutorial/items.py: 项目items文件
tutorial/pipelines.py: 项目管道文件
tutorial/settings.py: 项目配置文件
tutorial/spiders: 放置spider的目录
定义Item
Items是将要装载抓取的数据的容器,它工作方式像 python 里面的字典,但它提供更多的保护,比如对未定义的字段填充以防止拼写错误。它通过创建一个
scrapy.item.Item类来声明,定义它的属性为
scrpy.item.Field对象,就像是一个对象关系映射(ORM).
我们通过将需要的item模型化,来控制从 dmoz.org 获得的站点数据,比如我们要获得站点的名字,url 和网站描述,我们定义这三种属性的域。要做到这点,我们编辑在 tutorial 目录下的 items.py 文件,我们的 Item 类将会是这样
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.item <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> Item, Field <span class="hljs-class"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">class</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">DmozItem</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(Item)</span>:</span> title = Field() link = Field() desc = Field() </code>
刚开始看起来可能会有些困惑,但是定义这些 item 能让你用其他 Scrapy 组件的时候知道你的 items 到底是什么。
编写爬虫(Spider)
Spider 是用户编写的类,用于从一个域(或域组)中抓取信息。们定义了用于下载的URL的初步列表,如何跟踪链接,以及如何来解析这些网页的内容用于提取items。要建立一个 Spider,你可以为
scrapy.spider.BaseSpider创建一个子类,并确定三个主要的、强制的属性:
name:爬虫的识别名,它必须是唯一的,在不同的爬虫中你必须定义不同的名字.
start_urls:爬虫开始爬的一个 URL 列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些 URLS 开始。其他子 URL 将会从这些起始
URL 中继承性生成。
parse():爬虫的方法,调用时候传入从每一个 URL 传回的 Response 对象作为参数,response 将会是 parse 方法的唯一的一个参数,
这个方法负责解析返回的数据、匹配抓取的数据(解析为 item )并跟踪更多的 URL。
在 tutorial/spiders 目录下创建 DmozSpider.py
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.spider <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> BaseSpider <span class="hljs-class"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">class</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">DmozSpider</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(BaseSpider)</span>:</span> name = <span class="hljs-string" style="color: rgb(136, 0, 0);">"dmoz"</span> allowed_domains = [<span class="hljs-string" style="color: rgb(136, 0, 0);">"dmoz.org"</span>] start_urls = [ <span class="hljs-string" style="color: rgb(136, 0, 0);">"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/"</span>, <span class="hljs-string" style="color: rgb(136, 0, 0);">"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"</span> ] <span class="hljs-function"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">def</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">parse</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(self, response)</span>:</span> filename = response.url.split(<span class="hljs-string" style="color: rgb(136, 0, 0);">"/"</span>)[-<span class="hljs-number" style="color: rgb(0, 136, 0);">2</span>] open(filename, <span class="hljs-string" style="color: rgb(136, 0, 0);">'wb'</span>).write(response.body) </code>
运行项目
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>scrapy crawl dmoz </code>
该命令从 dmoz.org 域启动爬虫,第三个参数为 DmozSpider.py 中的 name 属性值。
xpath选择器
Scrapy 使用一种叫做 XPath selectors 的机制,它基于 XPath 表达式。如果你想了解更多selectors和其他机制你可以查阅资料。这是一些XPath表达式的例子和他们的含义:
/html/head/title: 选择HTML文档
<head>元素下面的
<title>标签。
/html/head/title/text(): 选择前面提到的
<title>元素下面的文本内容
//td: 选择所有
<td>元素
//div[@class="mine"]: 选择所有包含
class="mine"属性的div
标签元素
这只是几个使用 XPath 的简单例子,但是实际上 XPath 非常强大。如果你想了解更多 XPATH 的内容,我们向你推荐这个XPath
教程
为了方便使用 XPaths,Scrapy 提供 Selector 类, 有三种方法
xpath():返回selectors列表, 每一个select表示一个xpath参数表达式选择的节点.
extract():返回一个unicode字符串,该字符串为XPath选择器返回的数据
re(): 返回unicode字符串列表,字符串作为参数由正则表达式提取出来
css()
提取数据
我们可以通过如下命令选择每个在网站中的 <li>元素:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul/li'</span>) </code>
然后是网站描述:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul/li/text()'</span>).extract() </code>
网站标题:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul/li/a/text()'</span>).extract() </code>
网站链接:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul/li/a/@href'</span>).extract() </code>
如前所述,每个
xpath()调用返回一个 selectors 列表,所以我们可以结合
xpath()去挖掘更深的节点。我们将会用到这些特性,所以:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;">sites = sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul/li'</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">for</span> site in sites: title = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'a/text()'</span>).extract() <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">link</span> = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'a/@href'</span>).extract() desc = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'text()'</span>).extract() <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">print</span> title, <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">link</span>, desc </code>
使用Item
scrapy.item.Item的调用接口类似于 python 的 dict ,Item 包含多个
scrapy.item.Field。这跟
django 的 Model 与
Item 通常是在 Spider 的 parse 方法里使用,它用来保存解析到的数据。
最后修改爬虫类,使用 Item 来保存数据,代码如下:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.spider <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> Spider <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.selector <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> Selector <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> dirbot.items <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> Website <span class="hljs-class"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">class</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">DmozSpider</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(Spider)</span>:</span> name = <span class="hljs-string" style="color: rgb(136, 0, 0);">"dmoz"</span> allowed_domains = [<span class="hljs-string" style="color: rgb(136, 0, 0);">"dmoz.org"</span>] start_urls = [ <span class="hljs-string" style="color: rgb(136, 0, 0);">"http://www.dmoz.org/Computers/Programming/Languages/Python/Books/"</span>, <span class="hljs-string" style="color: rgb(136, 0, 0);">"http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"</span>, ] <span class="hljs-function"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">def</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">parse</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(self, response)</span>:</span> <span class="hljs-string" style="color: rgb(136, 0, 0);">""" The lines below is a spider contract. For more info see: http://doc.scrapy.org/en/latest/topics/contracts.html @url http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/ @scrapes name """</span> sel = Selector(response) sites = sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//ul[@class="directory-url"]/li'</span>) items = [] <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">for</span> site <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">in</span> sites: item = Website() item[<span class="hljs-string" style="color: rgb(136, 0, 0);">'name'</span>] = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'a/text()'</span>).extract() item[<span class="hljs-string" style="color: rgb(136, 0, 0);">'url'</span>] = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'a/@href'</span>).extract() item[<span class="hljs-string" style="color: rgb(136, 0, 0);">'description'</span>] = site.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'text()'</span>).re(<span class="hljs-string" style="color: rgb(136, 0, 0);">'-\s([^\n]*?)\\n'</span>) items.append(item) <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">return</span> items </code>
现在,可以再次运行该项目查看运行结果:
<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>scrapy crawl dmoz </code>
使用Item Pipeline
在 settings.py 中设置 ITEM_PIPELINES,其默认为
[],与
django 的
MIDDLEWARE_CLASSES等相似。
从 Spider 的 parse 返回的 Item 数据将依次被
ITEM_PIPELINES列表中的 Pipeline 类处理。
一个 Item Pipeline 类必须实现以下方法:
process_item(item, spider)为每个 item pipeline 组件调用,并且需要返回一个
scrapy.item.Item实例对象或者抛出一个
scrapy.exceptions.DropItem异常。当抛出异常后该
item 将不会被之后的 pipeline 处理。参数:
item (Item object)– 由 parse 方法返回的 Item 对象
spider (BaseSpider object)– 抓取到这个 Item 对象对应的爬虫对象
也可额外的实现以下两个方法:
open_spider(spider)当爬虫打开之后被调用。参数:
spider (BaseSpider object)– 已经运行的爬虫
close_spider(spider)当爬虫关闭之后被调用。参数:
spider (BaseSpider object)– 已经关闭的爬虫
保存抓取的数据
保存信息的最简单的方法是通过 Feed exports,命令如下:<code class="lang-bash" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-variable" style="color: rgb(102, 0, 102);">$ </span>scrapy crawl dmoz -o items.json -t json </code>
除了 json 格式之外,还支持 JSON lines、CSV、XML格式,你也可以通过接口扩展一些格式。
对于小项目用这种方法也足够了。如果是比较复杂的数据的话可能就需要编写一个 Item Pipeline 进行处理了。
所有抓取的 items 将以 JSON 格式被保存在新生成的 items.json 文件中
总结
上面描述了如何创建一个爬虫项目的过程,你可以参照上面过程联系一遍。作为学习的例子,你还可以参考这篇文章:scrapy 中文教程(爬cnbeta实例) 。这篇文章中的爬虫类代码如下:
<code class="lang-python" style="font-family: Consolas, Menlo, Monaco, "Courier New", monospace; font-size: 1em; padding: 0px; color: inherit; background-color: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.contrib.spiders <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> CrawlSpider, Rule <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.contrib.linkextractors.sgml <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> SgmlLinkExtractor <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> scrapy.selector <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> Selector <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">from</span> cnbeta.items <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">import</span> CnbetaItem <span class="hljs-class"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">class</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">CBSpider</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(CrawlSpider)</span>:</span> name = <span class="hljs-string" style="color: rgb(136, 0, 0);">'cnbeta'</span> allowed_domains = [<span class="hljs-string" style="color: rgb(136, 0, 0);">'cnbeta.com'</span>] start_urls = [<span class="hljs-string" style="color: rgb(136, 0, 0);">'http://www.cnbeta.com'</span>] rules = ( Rule(SgmlLinkExtractor(allow=(<span class="hljs-string" style="color: rgb(136, 0, 0);">'/articles/.*\.htm'</span>, )), callback=<span class="hljs-string" style="color: rgb(136, 0, 0);">'parse_page'</span>, follow=<span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">True</span>), ) <span class="hljs-function"><span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">def</span> <span class="hljs-title" style="color: rgb(136, 0, 0); font-weight: bold;">parse_page</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(self, response)</span>:</span> item = CnbetaItem() sel = Selector(response) item[<span class="hljs-string" style="color: rgb(136, 0, 0);">'title'</span>] = sel.xpath(<span class="hljs-string" style="color: rgb(136, 0, 0);">'//title/text()'</span>).extract() item[<span class="hljs-string" style="color: rgb(136, 0, 0);">'url'</span>] = response.url <span class="hljs-keyword" style="color: rgb(0, 0, 136); font-weight: bold;">return</span> item </code>
需要说明的是:
该爬虫类继承的是
CrawlSpider类,并且定义规则,rules指定了含有
/articles/.*\.htm的链接都会被匹配。
该类并没有实现parse方法,并且规则中定义了回调函数
parse_page,你可以参考更多资料了解 CrawlSpider 的用法
3. 学习资料
接触 Scrapy,是因为想爬取一些知乎的数据,最开始的时候搜索了一些相关的资料和别人的实现方式。Github 上已经有人或多或少的实现了对知乎数据的爬取,我搜索到的有以下几个仓库:
https://github.com/KeithYue/Zhihu_Spider 实现先通过用户名和密码登陆再爬取数据,代码见 zhihu_spider.py。
https://github.com/immzz/zhihu-scrapy 使用 selenium 下载和执行 javascript
代码。
https://github.com/tangerinewhite32/zhihu-stat-py
https://github.com/Zcc/zhihu 主要是爬指定话题的topanswers,还有用户个人资料,添加了登录代码。
https://github.com/pelick/VerticleSearchEngine 基于爬取的学术资源,提供搜索、推荐、可视化、分享四块。使用了
Scrapy、MongoDB、Apache Lucene/Solr、Apache Tika等技术。
https://github.com/geekan/scrapy-examples scrapy的一些例子,包括获取豆瓣数据、linkedin、腾讯招聘数据等例子。
https://github.com/owengbs/deeplearning 实现分页获取话题。
https://github.com/gnemoug/distribute_crawler 使用scrapy、redis、mongodb、graphite实现的一个分布式网络爬虫,底层存储mongodb集群,分布式使用redis实现,爬虫状态显示使用graphite实现
https://github.com/weizetao/spider-roach 一个分布式定向抓取集群的简单实现。
其他资料:
http://www.52ml.net/tags/Scrapy 收集了很多关于 Scrapy 的文章,推荐阅读
用Python Requests抓取知乎用户信息
使用scrapy框架爬取自己的博文
Scrapy 深入一点点
使用python,scrapy写(定制)爬虫的经验,资料,杂。
Scrapy 轻松定制网络爬虫
在scrapy中怎么让Spider自动去抓取豆瓣小组页面
scrapy 和 javascript 交互例子:
用scrapy框架爬取js交互式表格数据
scrapy + selenium 解析javascript 实例
还有一些待整理的知识点:
如何先登陆再爬数据
如何使用规则做过滤
如何递归爬取数据
scrapy的参数设置和优化
如何实现分布式爬取
相关文章推荐
- Python动态类型的学习---引用的理解
- Python3写爬虫(四)多线程实现数据爬取
- 垃圾邮件过滤器 python简单实现
- 下载并遍历 names.txt 文件,输出长度最长的回文人名。
- install and upgrade scrapy
- install scrapy with pip and easy_install
- Scrapy的架构介绍
- Centos6 编译安装Python
- 使用Python生成Excel格式的图片
- 让Python文件也可以当bat文件运行
- [Python]推算数独
- Python中zip()函数用法举例
- Python中map()函数浅析
- Python将excel导入到mysql中
- Python在CAM软件Genesis2000中的应用
- 使用Shiboken为C++和Qt库创建Python绑定