您的位置:首页 > 其它

Scrapy Tutorial 翻译1

2018-02-28 18:41 260 查看
以防睡着,开始翻译吧!

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">→</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爬取的基本原理。

基于此,可以通过自定义规则来创建更复杂的爬虫,访问不同的内容,获取不同的数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: