Python 之 Scrapy笔记(2)- 完整示例

来源:互联网 发布:王健林 抛售资产知乎 编辑:程序博客网 时间:2024/05/16 19:02

这篇文章我们通过一个比较完整的例子来教你使用Scrapy,我选择爬取虎嗅网首页的新闻列表。

这里我们将完成如下几个步骤:

  • 创建一个新的Scrapy工程
  • 定义你所需要要抽取的Item对象
  • 编写一个spider来爬取某个网站并提取出所有的Item对象
  • 编写一个Item Pipline来存储提取出来的Item对象

Scrapy使用Python语言编写,如果你对这门语言还不熟,请先去学习下基本知识。

创建Scrapy工程

在任何你喜欢的目录执行如下命令

1
scrapy startproject coolscrapy

将会创建coolscrapy文件夹,其目录结构如下:

coolscrapy/    scrapy.cfg            # 部署配置文件    coolscrapy/           # Python模块,你所有的代码都放这里面        __init__.py        items.py          # Item定义文件        pipelines.py      # pipelines定义文件        settings.py       # 配置文件        spiders/          # 所有爬虫spider都放这个文件夹下面            __init__.py            ...

定义我们的Item

我们通过创建一个scrapy.Item类,并定义它的类型为scrapy.Field的属性, 我们准备将虎嗅网新闻列表的名称、链接地址和摘要爬取下来。我们通过将需要的item模型化,来控制从***获得的站点数据,比如我们要获得站点的名字,url和网站描述,我们定义这三种属性的域。要做到这点,我们编辑在coolscrapy目录下的items.py文件,我们的Item类将会是这样

1234567
import scrapyclass HuxiuItem(scrapy.Item):    title = scrapy.Field()    # 标题    link = scrapy.Field()     # 链接    desc = scrapy.Field()     # 简述    posttime = scrapy.Field() # 发布时间

也许你觉得定义这个Item有点麻烦,但是定义完之后你可以得到许多好处,这样你就可以使用Scrapy中其他有用的组件和帮助类。

第一个Spider

Spider是用户编写的类,用于从一个域(或域组)中抓取信息。他们定义了用于下载的URL的初步列表,如何跟踪链接,以及如何来解析这些网页的内容用于提取items。

要建立一个Spider,你必须为scrapy.spider.BaseSpider创建一个子类,并确定三个主要的、强制的属性:

  • name:爬虫的识别名,它必须是唯一的,在不同的爬虫中你必须定义不同的名字.
  • start_urls:爬虫开始爬的一个URL列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些URLS开始。其他子URL将会从这些起始URL中继承性生成。
  • parse():爬虫的方法,调用时候传入从每一个URL传回的Response对象作为参数,response将会是parse方法的唯一的一个参数,

这个方法负责解析返回的数据、匹配抓取的数据(解析为item)并跟踪更多的URL。

我们在coolscrapy/spiders文件夹下面新建huxiu_spider.py,内容如下:

huxiu_spider.py
123456789101112131415161718192021222324
#!/usr/bin/env python# -*- encoding: utf-8 -*-"""Topic: sampleDesc :"""from coolscrapy.items import HuxiuItemimport scrapyclass HuxiuSpider(scrapy.Spider):    name = "huxiu"    allowed_domains = ["huxiu.com"]    start_urls = [        "http://www.huxiu.com/index.php"    ]    def parse(self, response):        for sel in response.xpath('//div[@class="mod-info-flow"]/div/div[@class="mob-ctt"]'):            item = HuxiuItem()            item['title'] = sel.xpath('h3/a/text()')[0].extract()            item['link'] = sel.xpath('h3/a/@href')[0].extract()            url = response.urljoin(item['link'])            item['desc'] = sel.xpath('div[@class="mob-sub"]/text()')[0].extract()            print(item['title'],item['link'],item['desc'])

运行爬虫

为了让我们的爬虫工作,我们返回项目主目录执行以下命令,其中huxiu是你定义的spider名字:

scrapy crawl huxiu

如果一切正常,应该可以打印出每一个新闻:

复制代码
T:\tutorial>scrapy crawl dmoz2012-07-13 19:14:45+0800 [scrapy] INFO: Scrapy 0.14.4 started (bot: tutorial)2012-07-13 19:14:45+0800 [scrapy] DEBUG: Enabled extensions: LogStats, TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState2012-07-13 19:14:45+0800 [scrapy] DEBUG: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, RedirectMiddleware, CookiesMiddleware, HttpCompressionMiddleware, ChunkedTransferMiddleware, DownloaderStats2012-07-13 19:14:45+0800 [scrapy] DEBUG: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware2012-07-13 19:14:45+0800 [scrapy] DEBUG: Enabled item pipelines:2012-07-13 19:14:45+0800 [dmoz] INFO: Spider opened2012-07-13 19:14:45+0800 [dmoz] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)2012-07-13 19:14:45+0800 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:60232012-07-13 19:14:45+0800 [scrapy] DEBUG: Web service listening on 0.0.0.0:60802012-07-13 19:14:46+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)2012-07-13 19:14:46+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)2012-07-13 19:14:46+0800 [dmoz] INFO: Closing spider (finished)2012-07-13 19:14:46+0800 [dmoz] INFO: Dumping spider stats:        {'downloader/request_bytes': 486,         'downloader/request_count': 2,         'downloader/request_method_count/GET': 2,         'downloader/response_bytes': 13063,         'downloader/response_count': 2,         'downloader/response_status_count/200': 2,         'finish_reason': 'finished',         'finish_time': datetime.datetime(2012, 7, 13, 11, 14, 46, 703000),         'scheduler/memory_enqueued': 2,         'start_time': datetime.datetime(2012, 7, 13, 11, 14, 45, 500000)}2012-07-13 19:14:46+0800 [dmoz] INFO: Spider closed (finished)2012-07-13 19:14:46+0800 [scrapy] INFO: Dumping global stats:        {}
复制代码

注意包含 [dmoz]的行 ,那对应着我们的爬虫。你可以看到start_urls中定义的每个URL都有日志行。因为这些URL是起始页面,所以他们没有引用(referrers),所以在每行的末尾你会看到 (referer: <None>). 
有趣的是,在我们的 parse  方法的作用下,两个文件被创建:分别是 Books 和 Resources,这两个文件中有URL的页面内容。 

发生了什么事情?

Scrapy为爬虫的 start_urls属性中的每个URL创建了一个 scrapy.http.Request 对象 ,并将爬虫的parse 方法指定为回调函数。 
这些 Request首先被调度,然后被执行,之后通过parse()方法,scrapy.http.Response 对象被返回,结果也被反馈给爬虫。

提取Item

选择器介绍

我们有很多方法从网站中提取数据。Scrapy 使用一种叫做 XPath selectors的机制,它基于 XPath表达式。如果你想了解更多selectors和其他机制你可以查阅资料http://doc.scrapy.org/topics/selectors.html#topics-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教程http://www.w3schools.com/XPath/default.asp

为了方便使用XPaths,Scrapy提供XPathSelector 类, 有两种口味可以选择, HtmlXPathSelector (HTML数据解析) 和XmlXPathSelector (XML数据解析)。 为了使用他们你必须通过一个 Response 对象对他们进行实例化操作。你会发现Selector对象展示了文档的节点结构。因此,第一个实例化的selector必与根节点或者是整个目录有关 。 
Selectors 有三种方法

  • path():返回selectors列表, 每一个select表示一个xpath参数表达式选择的节点.
  • extract():返回一个unicode字符串,该字符串为XPath选择器返回的数据
  • re(): 返回unicode字符串列表,字符串作为参数由正则表达式提取出来

尝试在shell中使用Selectors

为了演示Selectors的用法,我们将用到 内建的Scrapy shell,这需要系统已经安装IPython (一个扩展python交互环境) 。

附IPython下载地址:http://pypi.python.org/pypi/ipython#downloads

要开始shell,首先进入项目顶层目录,然后输入

T:\tutorial>scrapy shell http://www.dmoz.org/Computers/Programming/Languages/Python/Books/

输出结果类似这样:

复制代码
2012-07-16 10:58:13+0800 [scrapy] INFO: Scrapy 0.14.4 started (bot: tutorial)2012-07-16 10:58:13+0800 [scrapy] DEBUG: Enabled extensions: TelnetConsole, CloseSpider, WebService, CoreStats, SpiderState2012-07-16 10:58:13+0800 [scrapy] DEBUG: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, RedirectMiddleware, CookiesMiddleware, HttpCompressionMiddleware, ChunkedTransferMiddleware, DownloaderStats2012-07-16 10:58:13+0800 [scrapy] DEBUG: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware2012-07-16 10:58:13+0800 [scrapy] DEBUG: Enabled item pipelines:2012-07-16 10:58:13+0800 [scrapy] DEBUG: Telnet console listening on 0.0.0.0:60232012-07-16 10:58:13+0800 [scrapy] DEBUG: Web service listening on 0.0.0.0:60802012-07-16 10:58:13+0800 [dmoz] INFO: Spider opened2012-07-16 10:58:18+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)[s] Available Scrapy objects:[s]   hxs        <HtmlXPathSelector xpath=None data=u'<html><head><meta http-equiv="Content-Ty'>[s]   item       {}[s]   request    <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>[s]   response   <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>[s]   settings   <CrawlerSettings module=<module 'tutorial.settings' from 'T:\tutorial\tutorial\settings.pyc'>>[s]   spider     <DmozSpider 'dmoz' at 0x1f68230>[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 browserWARNING: Readline services not available or not loaded.WARNING: Proper color support under MS Windows requires the pyreadline library.You can find it at:http://ipython.org/pyreadline.htmlGary's readline needs the ctypes module, from:http://starship.python.net/crew/theller/ctypes(Note that ctypes is already part of Python versions 2.5 and newer).Defaulting color scheme to 'NoColor'Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)]Type "copyright", "credits" or "license" for more information.IPython 0.13 -- An enhanced Interactive Python.?         -> Introduction and overview of IPython's features.%quickref -> Quick reference.help      -> Python's own help system.object?   -> Details about 'object', use 'object??' for extra details.In [1]:
复制代码

Shell载入后,你将获得回应,这些内容被存储在本地变量 response 中,所以如果你输入response.body 你将会看到response的body部分,或者输入response.headers 来查看它的 header部分。 
Shell也实例化了两种selectors,一个是解析HTML的  hxs 变量,一个是解析 XML 的 xxs 变量。我们来看看里面有什么:

复制代码
In [1]: hxs.path('//title')Out[1]: [<HtmlXPathSelector xpath='//title' data=u'<title>Open Directory - Computers: Progr'>]In [2]: hxs.path('//title').extract()Out[2]: [u'<title>Open Directory - Computers: Programming: Languages: Python: Books</title>']In [3]: hxs.path('//title/text()')Out[3]: [<HtmlXPathSelector xpath='//title/text()' data=u'Open Directory - Computers: Programming:'>]In [4]: hxs.path('//title/text()').extract()Out[4]: [u'Open Directory - Computers: Programming: Languages: Python: Books']In [5]: hxs.path('//title/text()').re('(\w+):')Out[5]: [u'Computers', u'Programming', u'Languages', u'Python']In [6]:
复制代码

 

 

提取数据

现在我们尝试从网页中提取数据。 
你可以在控制台输入 response.body, 检查源代码中的 XPaths 是否与预期相同。然而,检查HTML源代码是件很枯燥的事情。为了使事情变得简单,我们使用Firefox的扩展插件Firebug。更多信息请查看Using Firebug for scraping 和Using Firefox for scraping.
txw1958注:事实上我用的是Google Chrome的Inspect Element功能,而且可以提取元素的XPath。
检查源代码后,你会发现我们需要的数据在一个 <ul>元素中,而且是第二个<ul>。 
我们可以通过如下命令选择每个在网站中的 <li> 元素:

hxs.path('//ul/li') 

然后是网站描述:

hxs.path('//ul/li/text()').extract()

网站标题:

hxs.path('//ul/li/a/text()').extract()

网站链接:

hxs.path('//ul/li/a/@href').extract()

如前所述,每个path()调用返回一个selectors列表,所以我们可以结合path()去挖掘更深的节点。我们将会用到这些特性,所以:

复制代码
sites = hxs.path('//ul/li')for site in sites:    title = site.path('a/text()').extract()    link = site.path('a/@href').extract()    desc = site.path('text()').extract()    print title, link, desc
复制代码

 

Note 
更多关于嵌套选择器的内容,请阅读Nesting selectors 和 Working with relative XPaths

将代码添加到爬虫中:

txw1958注:代码有修改,绿色注释掉的代码为原教程的,你懂的

复制代码
from scrapy.spider import BaseSpiderfrom scrapy.selector import HtmlXPathSelectorclass DmozSpider(BaseSpider):    name = "dmoz"    allowed_domains = ["dmoz.org"]    start_urls = [        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"]          def parse(self, response):        hxs = HtmlXPathSelector(response)        sites = hxs.path('//fieldset/ul/li')        #sites = hxs.path('//ul/li')        for site in sites:            title = site.path('a/text()').extract()            link = site.path('a/@href').extract()            desc = site.path('text()').extract()            #print title, link, desc            print title, link
复制代码

现在我们再次抓取dmoz.org,你将看到站点在输出中被打印 ,运行命令

T:\tutorial>scrapy crawl dmoz

 

 

使用条目(Item)

Item 对象是自定义的python字典,使用标准字典类似的语法,你可以获取某个字段(即之前定义的类的属性)的值:

>>> item = DmozItem() >>> item['title'] = 'Example title' >>> item['title'] 'Example title' 

Spiders希望将其抓取的数据存放到Item对象中。为了返回我们抓取数据,spider的最终代码应当是这样:

复制代码
from scrapy.spider import BaseSpiderfrom scrapy.selector import HtmlXPathSelectorfrom tutorial.items import DmozItemclass DmozSpider(BaseSpider):   name = "dmoz"   allowed_domains = ["dmoz.org"]   start_urls = [       "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",       "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"   ]   def parse(self, response):       hxs = HtmlXPathSelector(response)       sites = hxs.path('//fieldset/ul/li')       #sites = hxs.path('//ul/li')       items = []       for site in sites:           item = DmozItem()           item['title'] = site.path('a/text()').extract()           item['link'] = site.path('a/@href').extract()           item['desc'] = site.path('text()').extract()           items.append(item)       return items
复制代码

现在我们再次抓取 : 

复制代码
2012-07-16 14:52:36+0800 [dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>        {'desc': [u'\n\t\t\t\n\t',                  u' \n\t\t\t\n\t\t\t\t\t\n - Free Python books and tutorials.\n \n'],         'link': [u'http://www.techbooksforfree.com/perlpython.shtml'],         'title': [u'Free Python books']}2012-07-16 14:52:36+0800 [dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Books/>        {'desc': [u'\n\t\t\t\n\t',                  u' \n\t\t\t\n\t\t\t\t\t\n - Annotated list of free online books on Python scripting language. Topics range from beginner to advanced.\n \n          '],         'link': [u'http://www.freetechbooks.com/python-f6.html'],         'title': [u'FreeTechBooks: Python Scripting Language']}2012-07-16 14:52:36+0800 [dmoz] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)2012-07-16 14:52:36+0800 [dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/>        {'desc': [u'\n\t\t\t\n\t',                  u' \n\t\t\t\n\t\t\t\t\t\n - A directory of free Python and Zope hosting providers, with reviews and ratings.\n \n'],         'link': [u'http://www.oinko.net/freepython/'],         'title': [u'Free Python and Zope Hosting Directory']}2012-07-16 14:52:36+0800 [dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/>        {'desc': [u'\n\t\t\t\n\t',                  u' \n\t\t\t\n\t\t\t\t\t\n - Features Python books, resources, news and articles.\n \n'],         'link': [u'http://oreilly.com/python/'],         'title': [u"O'Reilly Python Center"]}2012-07-16 14:52:36+0800 [dmoz] DEBUG: Scraped from <200 http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/>        {'desc': [u'\n\t\t\t\n\t',                  u' \n\t\t\t\n\t\t\t\t\t\n - Resources for reporting bugs, accessing the Python source tree with CVS and taking part in the development of Python.\n\n'],         'link': [u'http://www.python.org/dev/'],         'title': [u"Python Developer's Guide"]}
复制代码


 

保存抓取的数据

保存信息的最简单的方法是通过Feed exports,命令如下:

T:\tutorial>scrapy crawl dmoz -o items.json -t json

所有抓取的items将以JSON格式被保存在新生成的items.json 文件中

在像本教程一样的小型项目中,这些已经足够。然而,如果你想用抓取的items做更复杂的事情,你可以写一个 Item Pipeline(条目管道)。因为在项目创建的时候,一个专门用于条目管道的占位符文件已经随着items一起被建立,目录在tutorial/pipelines.py。如果你只需要存取这些抓取后的items的话,就不需要去实现任何的条目管道。


导出抓取数据

最简单的保存抓取数据的方式是使用json格式的文件保存在本地,像下面这样运行:

1
scrapy crawl huxiu -o items.json

在演示的小系统里面这种方式足够了。不过如果你要构建复杂的爬虫系统, 最好自己编写Item Pipeline。

保存数据到数据库

上面我们介绍了可以将抓取的Item导出为json格式的文件,不过最常见的做法还是编写Pipeline将其存储到数据库中。我们在coolscrapy/pipelines.py定义

1234567891011121314151617181920212223242526272829303132333435363738
# -*- coding: utf-8 -*-import datetimeimport redisimport jsonimport loggingfrom contextlib import contextmanagerfrom scrapy import signalsfrom scrapy.exporters import JsonItemExporterfrom scrapy.pipelines.images import ImagesPipelinefrom scrapy.exceptions import DropItemfrom sqlalchemy.orm import sessionmakerfrom coolscrapy.models import News, db_connect, create_news_table, Articleclass ArticleDataBasePipeline(object):    """保存文章到数据库"""    def __init__(self):        engine = db_connect()        create_news_table(engine)        self.Session = sessionmaker(bind=engine)    def open_spider(self, spider):        """This method is called when the spider is opened."""        pass    def process_item(self, item, spider):        a = Article(url=item["url"],                    title=item["title"].encode("utf-8"),                    publish_time=item["publish_time"].encode("utf-8"),                    body=item["body"].encode("utf-8"),                    source_site=item["source_site"].encode("utf-8"))        with session_scope(self.Session) as session:            session.add(a)    def close_spider(self, spider):        pass

上面我使用了python中的SQLAlchemy来保存数据库,这个是一个非常优秀的ORM库,我写了篇关于它的入门教程,可以参考下。

然后在setting.py中配置这个Pipeline,还有数据库链接等信息:

123456789101112
ITEM_PIPELINES = {    'coolscrapy.pipelines.ArticleDataBasePipeline': 5,}# linux pip install MySQL-pythonDATABASE = {'drivername': 'mysql',            'host': '192.168.203.95',            'port': '3306',            'username': 'root',            'password': 'mysql',            'database': 'spider',            'query': {'charset': 'utf8'}}

再次运行爬虫

1
scrapy crawl huxiu

那么所有新闻的文章都存储到数据库中去了。

结束语

本教程简要介绍了Scrapy的使用,但是许多其他特性并没有提及。对于基本概念的了解,请访问Basic concepts我们推荐你继续学习Scrapy项目的例子dirbot,你将从中受益更深,该项目包含本教程中提到的dmoz爬虫。Dirbot项目位于https://github.com/scrapy/dirbot项目包含一个README文件,它详细描述了项目的内容。如果你熟悉git,你可以checkout它的源代码。或者你可以通过点击Downloads下载tarball或zip格式的文件。另外这有一个代码片断共享网站,里面共享内容包括爬虫,中间件,扩展应用,脚本等。网站名字叫Scrapy snippets,有好的代码要记得共享哦:-) 

本教程的源代码下载:http://files.cnblogs.com/txw1958/scrapy_tutorial.rar

下一步

本章只是带你领略了scrapy最基本的功能,还有很多高级特性没有讲到。接下来会通过多个例子向你展示scrapy的其他特性,然后再深入讲述每个特性。

原文链接:http://www.pycoding.com/2016/03/10/scrapy-02.html

0 0
原创粉丝点击