Scrapy Tutorial 翻译1

以防睡着,开始翻译吧!

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"

    def start_requests(self):
        urls = [
            'http://quotes.toscrape.com/page/1/',
            'http://quotes.toscrape.com/page/2/',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)
What just happened under the hood?

让我们看看背后究竟是如何处理的?

Scrapy通过调用Spider的start_requests方法,来安排scrapy.Request对象。对每一个请求,一旦获取返回信息,将通过调用callback所指定的parse方式来实例化返回结果并作为参数保存。

start_requests方法的快速实现方式:

直接使用start_urls类属性来定义url list即可。

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
上面代码并未对parse方法显示调用,但parse()仍会被执行,作为Scrapyd的默认回调函数。

提取数据:

最好的办法是使用Scrapy shell,来查看一些基本Scrapy  selectors object。

scrapy shell 'http://quotes.toscrape.com/page/1/'
备注:在命令行中运行scrapy shell时,url需要使用引号;windows下需要使用双引号。

执行后你将会看到:

[ ... Scrapy log here ... ]
2016-09-19 12:09:27 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x7fa91d888c90>
[s]   item       {}
[s]   request    <GET http://quotes.toscrape.com/page/1/>
[s]   response   <200 http://quotes.toscrape.com/page/1/>
[s]   settings   <scrapy.settings.Settings object at 0x7fa91d888c10>
[s]   spider     <DefaultSpider 'default' at 0x7fa91c8af990>
[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
>>>
此时,切换到了scrapy shell状态,可使用response对象的css方法来选择各种元素。

>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
它的效果与使用Selector的SelectorList对象分析XML/HTML达到同样的效果;我们还可以进一步得到更精确的结果。如提取上面Selector对象中的title字段

>>> response.css('title::text').extract()
['Quotes to Scrape']
现在请注意两点:

1. 添加::text到CSS查询内容中,表示了仅仅选择title元素的文本属性,不包含其tag;

>>> response.css('title').extract()
['<title>Quotes to Scrape</title>']
2. 调用.extract()后返回的是一个List(这个list是SelectorList的一个实例),如果仅仅想得到第一个结果,则需要调用:

>>> response.css('title::text').extract_first()
'Quotes to Scrape'
或写为:

>>> response.css('title::text')[0].extract()
'Quotes to Scrape'
建议最好使用extract_first(),在未查找到任何匹配内容时返回None,而不是 IndexError;这样可以防止找不到数据时,程序停止执行。

除了extract()和extract_first()方法,也可使用re()方法及正则表达式,来提取数据。

>>> response.css('title::text').re(r'Quotes.*')
['Quotes to Scrape']
>>> response.css('title::text').re(r'Q\w+')
['Quotes']
>>> response.css('title::text').re(r'(\w+) to (\w+)')
['Quotes', 'Scrape']
为了找到最合适的CSS selectors,及css()中的内容,有时候需要在浏览器中view(response)查看源码。不同的浏览器,scraping的方式会不同。

Selector Gadget是一个好的可视化的工具,可以工作在不同的浏览器上,帮你快速找到CSS selectors。

XPath简要说明

除了css,Scrapy selectors也支持xPath表达式:

>>> response.xpath('//title')
[<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>]
>>> response.xpath('//title/text()').extract_first()
'Quotes to Scrape'
xPath表达式非常强大,是Scrapy selectors的基础。实际上,在后台,CSS会被转换为XPath selectors。如果你仔细阅读shell中的内容,你会看到。

虽然XPath没有CSS流行,但其实功能更强大,使用XPath,你可以处理类似“next page”等这样的链接,从而使Scrapy更加容易,强烈推荐使用XPath。

提取名言和作者

可以看到,http://quotes.toscrape.com HTML文件的内容大概如下:

<div class="quote">
    <span class="text">“The world as we have created it is a process of our
    thinking. It cannot be changed without changing our thinking.”</span>
    <span>
        by <small class="author">Albert Einstein</small>
        <a href="/author/Albert-Einstein">(about)</a>
    </span>
    <div class="tags">
        Tags:
        <a class="tag" href="/tag/change/page/1/">change</a>
        <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
        <a class="tag" href="/tag/thinking/page/1/">thinking</a>
        <a class="tag" href="/tag/world/page/1/">world</a>
    </div>
</div>
打开scrapy shell:
$ scrapy shell 'http://quotes.toscrape.com'

使用quote selectors返回quote 要素列表:

>>> response.css("div.quote")
我们先分析第一个quote selector,将第一个selector付给一个变量

quote = response.css("div.quote")[0]
使用quote对象提取title,author,tags等内容:

>>> title = quote.css("span.text::text").extract_first()
>>> title
'“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'
>>> author = quote.css("small.author::text").extract_first()
>>> author
'Albert Einstein'
因为有很多的tag字符串,可使用.extract()方法状态tag列表。

>>> tags = quote.css("div.tags a.tag::text").extract()
>>> tags
['change', 'deep-thoughts', 'thinking', 'world']
对其它的quote selectors进行迭代,并将所有结果放回一个字典结构中。

>>> for quote in response.css("div.quote"):
...     text = quote.css("span.text::text").extract_first()
...     author = quote.css("small.author::text").extract_first()
...     tags = quote.css("div.tags a.tag::text").extract()
...     print(dict(text=text, author=author, tags=tags))
{'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'}
{'tags': ['abilities', 'choices'], 'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'}
    ... a few more of these, omitted for brevity
>>>
使用spider提取数据

返回我们的spider代码,到目前为止,spider未做任何提取数据的操作,仅仅保存了HTML页面到本地。让我们将上述提取逻辑集中到spider的代码中。

Scrapy spider通常会返回大量数据,我们使用python的yield来迭代。

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }
运行上述代码,在日志中可以看到:

2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['life', 'love'], 'author': 'André Gide', 'text': '“It is better to be hated for what you are than to be loved for what you are not.”'}
2016-09-19 18:57:19 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['edison', 'failure', 'inspirational', 'paraphrased'], 'author': 'Thomas A. Edison', 'text': "“I have not failed. I've just found 10,000 ways that won't work.”"}

存储爬取的数据

最简单的方式是利用Feed exports:

scrapy crawl quotes -o quotes.json
将生成一个quotes.json文件,包含所有爬取的数据,用JSON格式。

由于历史原因,对于给定文件,Scrapy会直接在后面添加内容,而不是重新写入。所以执行两遍,会损害其结构。

也可以存储为JSON Lines格式。

scrapy crawl quotes -o quotes.jl
由于JSON Lines类似于stream,添加数据时,不会影响到其结构,要由于JSON。并且,每一条记录都会是单独的一行,处理大文件时,不需要一次昂将所有内容添加到内存中。可借助工具JQ来操作。

如果数据量较小,使用上述方式即可。但当需要处理大量复杂数据时,最好使用Item Pipeline。在创建工程中,该文件就已经被创建,你可以在工程目录下找到它。如果仅仅只是为了保存数据,我们可以不管它。

更多的链接

目前为止,我们只是从http://quotes.toscrape.com中爬取了两页的内容,让我们看看如何爬取更多链接下的更多内容。

首先,获取我们想要的链接。如HTML文件中,下页的表现方式为:

<ul class="pager">
    <li class="next">
        <a href="/page/2/">Next <span aria-hidden="true">&rarr;</span></a>
    </li>
</ul>
使用shell,可得到

>>> response.css('li.next a').extract_first()
'<a href="/page/2/">Next <span aria-hidden="true">→</span></a>'
获取href属性的内容:

>>> response.css('li.next a::attr(href)').extract_first()
'/page/2/'
将上述内容集成到spider的代码中:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)
可以看到,在Parser方法中,在获取数据后,因为这些链接地址都是相对的,可直接调用urljoin来创建完整的url地址,并对每个地址使用yeild来进行request迭代,并爬取每个页面的数据。

以上为Scrapy爬取的基本原理。

基于此,可以通过自定义规则来创建更复杂的爬虫,访问不同的内容,获取不同的数据。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值