使用Scrapy构建自己的定制网络爬虫

爬虫(Web Spider or ),顾名思义:一个在网站之间互相游走的虫子,专好觅食各类页面数据。随着Spider技术门槛降低,爬虫也开始泛滥起来,很多时候爬虫变成了扒虫。然,技术本无善恶,全在用者之念。此处不做过多计较,我们当下要讨论也仅仅是定制一款自己的网络爬虫,仅此。

爬虫就Mitchell个人经验来说分为两大类:

  • 通用型网络爬虫:该类爬虫并无明确采集目标,每个能够爬及的页面都是其采集对象,除非满足系统指定条件,否则此类爬虫基本全年无休的辛勤劳作。最常见的就是搜索引擎的爬虫,如:Google,Baidu,Yahoo,Bing等
  • 专用型网络爬虫:这种爬虫仅针对自身偏好的网站或者主题作为采集目标,采集到的内容或涉及到的URL为此爬虫不感兴趣的,将被爬虫直接忽略,此类爬虫根据需要采集的目标多寡采集时间有所不同。如:各类垂直搜索站,金融爬虫,站点采集等

如果你是要构建一个通用型的网络爬虫,当前的并不太适合,而要做个专用型的网络爬虫,或者简单的收集某个站点信息,Scrapy还是能够胜任的,本文作为入门,为简单起见,只针对单个站点进行爬取。

 

基本技能要求

  • Python ,能读写Python代码是基础,不然定制无从谈起;
  • xpath,对于xpath也需要了解,数据的提取需要使用xpath来进行(也可以使用css来提取);
  • Python操作存储设备,Python操作文件,数据库中的某一类方式,可用来存储采集的数据(本文不涉及)

 

安装Scrapy

Scrapy是Python开发的爬虫系统,因此:

  • 必须要有Python 2.7+ 的运行环境
  • 同时请准备好pipsetuptools这两个包,将使你后继安装更加简单
  • lxml虽然在大部分Linux中已经有lxml,但安全起见,请检查,没有的话,请移步到lxml官网
  • OpenSSL这个除了Windows之外,其他系统应该都预装了的,不过在你装Python的时候,能免费赠送。
  • Windows下需要加菜,pywin32别忘记了,到sourceforge上找个适合自己版本吧(可能要翻墙)。

有了pip,我们就能使用pip来安装Scrapy,命令行下输入:

pip install Scrapy

针对国内这种和谐的网络环境,你很有可能无法顺利执行完上面这个命令。

此时,一个镜像站点也许能给你带来很大帮助 —— Python已经不再官方授权镜像,如果你信不过其他站点,翻墙(VPN或其他翻墙工具)将是你唯一的选择。

# 此处使用中科大 mirrors
pip install -i https://pypi.mirrors.ustc.edu.cn/simple Scrapy

 如果是Windows,记得将Python和Python下面Scripts的路径加到PATH中。再装个pywin32。

安装好了Scrapy,我们就离成功定制进一步。

 

创建Scrapy爬虫

如果安装好了Scrapy,你应该是能够在命令行下直接访问scrapy命令的:

> scrapy

Scrapy 1.0.1 - no active project

Usage:
  scrapy <command> [options] [args]

Available commands:
  bench         Run quick benchmark test
  commands
  fetch         Fetch a URL using the Scrapy downloader
  runspider     Run a self-contained spider (without creating a project)
  settings      Get settings values
  shell         Interactive scraping console
  startproject  Create new project
  version       Print Scrapy version
  view          Open URL in browser, as seen by Scrapy

  [ more ]      More commands available when run from project directory

Use "scrapy <command> -h" to see more info about a command

 首先切换到需要创建爬虫项目的目录下,使用startproject来创建爬虫项目:

## path/to/....是要切换到的目录路径
## 创建myfirstspiderproject项目
## Mitchell Chu
## Blog:http://blog.usepas.net/

>pushd path/to/create/spider/project
>scrapy startproject myfirstspiderproject

这个时候将会在指定的目录下创建myfirstspiderproject的项目目录,该目录下的内容大致如下:

scrapy.cfg
myfirstspiderproject/
    __init__.py
    items.py
    pipelines.py
    settings.py
    spiders/
        __init__.py

这仅是创建好了爬虫项目,还没有爬虫,因此还需要使用命令行创建一个爬虫:

## 切到项目目录内
## 创建一个叫spider007的爬虫
## Mitchell Chu
## Blog: http://blog.useasp.net/

>cd myfirstspiderproject
>scrapy genspider spider007 crawlerdomain.com

此时目录变成了:

scrapy.cfg
myfirstspiderproject/
    __init__.py
    items.py
    pipelines.py
    settings.py
    spiders/
        __init__.py
        spider007.py

会发现,多了一个spider007的文件,这就是我们的爬虫了。接下来我们的定制之旅也就围绕这个文件进行。

 

我们创建的spider007文件内容如下:

# -*- coding: utf-8 -*-
import scrapy


class Spider007Spider(scrapy.Spider):
    name = "spider007"
    allowed_domains = ["crawlerdomain.com"]
    start_urls = (
        'http://www.crawlerdomain.com/',
    )

    def parse(self, response):
        pass

恩,等等,,,要定制之前,我们至少得知道我们的定制目标,来让我们来想想:

我们要采集那个网站? 恩,就用本站吧,域名:http://blog.useasp.net/

我们要采集什么内容?我们只需要文章标题!

恩,OK,大概的目标既定,开始定制吧。

首先,我们需要将allowed_domains改成我们要采集的网站了,至少也得让他出现在这个列表,不然会被scrapy过滤掉——没错,我们是专用型网络爬虫,不感兴趣的统统不要。

其次,start_urls也换成http://blog.useasp.net/吧,我们都要采集文章标题了,像Mitchell的这个博客小站,没有入口,是很难到的了的。

恩,先这么些,改好之后想下面这样:

# -*- coding: utf-8 -*-
import scrapy


class Spider007Spider(scrapy.Spider):
    name = "spider007"
    allowed_domains = ["blog.useasp.net"]
    start_urls = (
        'http://blog.useasp.net/',
    )

    def parse(self, response):
        pass

现在,我们就有了一个采集Mitchell Chu‘s Blog的专用网络爬虫了! —— 不过只是爬,啥都没干。

我们还得取个数据不是?要标题,我们得到哪里找最快?

经过分析,应该是右侧的数据归档下面最快了,全部都有,进去的第一个页面就能获取到,应该属于最短有效路径了。

数据采集时,选取最快的方式获取数据能很大程度提升程序的性能,并有效降低对目标网站的压力。

恩,既然已经出来,我们理下思路:

  • 数据归档获取页面文章归档地址(非归档地址忽略)
  • 归档页面提取文章标题

 有了这个思路,我们就将思路放到parse里面:

# -*- coding: utf-8 -*-
import scrapy


class Spider007Spider(scrapy.Spider):
    name = "spider007"
    allowed_domains = ["blog.useasp.net"]
    start_urls = (
        'http://blog.useasp.net/',
    )

    def parse(self, response):
        """
            提取页面URI,只要归档地址
        """
        for url in response.xpath(
                '//div[@class="links box-wrap"]' +
                '//a[starts-with(@href, "/archive/")]/@href').extract():
            yield scrapy.Request(
                                response.urljoin(url),
                                self.parse_useasp_net_blog_title)

    def parse_useasp_net_blog_title(self, response):
        """
            解析归档页面的文章标题
            注意:
            进入这的URL是归档页面
            http://blog.useasp.net/archive/*
        """
        blog_title = response.xpath('//h4/a/text()').extract()
        # extract出来的是数组.
        print blog_title[0].encode('gb2312')

说明:

为了清晰起见,我们将解析URI的功能放在了parse,而解析标题的单独放在了parse_useasp_net_blog_title里面。

在解析URI之后,我们生成了scrapy的Request对象,并返回给scrapy进行调度采集,response.urljoin()是用来生成绝对地址的方法,并告知Request,请求的页面使用parse_useasp_net_blog_title这个方法来解析。

这样,我们就获得了一个完整功能的爬虫了,它爬取blog.useasp.net网站的所有博文标题,并显示到命令行下。

让我们试试效果,在命令行下输入:

scrapy crawl spider007

等待片刻,我们将能够看到输出了:

Chrome卡死无响应的问题解决方法
Python函数的各种参数(含星号参数)
Linux普通用户无法使用sudo处理及sudoers设置
MySql用户权限配置管理--查看,赋予,回收(GRANT,REVOKE)Linux下如何批量建立文件软连接(极简版)
2016-03-12 22:47:38 [scrapy] DEBUG: Crawled (200) <GET http://blog.useasp.net/archive/2014/05.aspx> (referer: http://blog.useasp.net/)

 恩,这正是我们所要的,目标达成,完成了一次定制Scrapy的过程。

 

Scrapy调试

scrapy开发中,肯定不会像我们上面如此顺利,很多时候需要进行调试,我们可以在scrapy使用下面的命令进入shell模式,进行必要测试:

scrapy shell http://blog.useasp.net/

scrapy会选择spider007作为默认的spider,并请求提供的url,成功后,进入shell的环境:

2016-03-12 11:43:04 [scrapy] DEBUG: Crawled (200) <GET http://blog.useasp.net> (referer: None)
[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x00000000049BCC50>
[s]   item       {}
[s]   request    <GET http://blog.useasp.net>
[s]   response   <200 http://blog.useasp.net>
[s]   settings   <scrapy.settings.Settings object at 0x00000000049BCB70>
[s]   spider     <Spider007Spider 'spider007' at 0x72bbf98>
[s] Useful shortcuts:
[s]   shelp()           Shell help (print this help)
[s]   fetch(req_or_url) Fetch request (or URL) and update local objects
[s]   view(response)    View response in a browser
2016-03-12 11:43:07 [root] DEBUG: Using default logger
2016-03-12 11:43:07 [root] DEBUG: Using default logger

In [1]:

进入shell后,我们就可以使用语法进行操作了:

In [2]: response.xpath('//h3/a/text()')
Out[2]:
[<Selector xpath='//h3/a/text()' data=u'C# HttpWebResponse\u4e0b\u8f7d\u9650\u901f'>,
 <Selector xpath='//h3/a/text()' data=u'BitConverter.IsLittleEndian\u5728x86\u7684\u673a\u5668\u4e0a\u8fd4\u56defal'> ]

In [3]: response.xpath('//h3/a/text()').extract()
Out[3]:
[u'C# HttpWebResponse\u4e0b\u8f7d\u9650\u901f',
 u'BitConverter.IsLittleEndian\u5728x86\u7684\u673a\u5668\u4e0a\u8fd4\u56defalse',
 u'FLV\u97f3\u9891\u89c6\u9891\u6587\u4ef6\u7684\u52a0\u5bc6\u89e3\u5bc6\u65b9\u5f0f']

 是不是很方便?

使用完毕后,可以直接使用exit()退出shell即可。

 

现在开始,你也可以开始定制属于自己的了,试试吧。

Mitchell偷偷告诉你:要获取blog.useasp.net的blog其实不用再这么复杂的,本站有RSS的。仅用来作为scrapy试手的示例而已。

 

后记

本文只是对scrapy的使用的一个概况,仅涉及scrapy的基本功能,大部分时候需要对scrapy进行配置,启用DNS cache,Http Cache,job等来提升scrapy的性能,同时涉及定制pipeline,item,middleware等,留待Mitchell后继为你介绍吧。

 

参考

  1. scrapy帮助文档
  2. scrapy官方网站
Saturday, March 12, 2016 | 其他技术 编程语言

文章评论

  • # RE:使用Scrapy构建自己的定制网络爬虫
    怎么好文章没有人评论

发表评论

Please add 3 and 7 and type the answer here:

关于博主

  一枚成分复杂的网络IT分子,常年游弋于电子商务,属于互联网行业分类中的杂牌军。当前正在待业中...