python3爬虫学习系列08 - scrapy(二)

文章目录

  • 1. 追踪链接(fllow links)
  • 2. 创建request的快捷方式
  • 3. 更多例子
  • 4. 使用 spider 参数
  • 5. 参考文献

之前的博客:
python3爬虫学习系列02-常见的下载和抽取网页的方法
python3爬虫学习系列03-下载缓存
python3爬虫学习系列04 - 并发下载
python3爬虫学习系列05 - 获取动态内容
python3爬虫学习系列06 -表单交互
python3爬虫学习系列07 - 处理验证码
python3爬虫学习系列08 - scrapy(一)

在上一篇博文中,我们学会了Scrapy的简单使用(scrapy的安装、爬取网页、提取数据),这一篇我们将继续对官方文档进行学习。

1. 追踪链接(fllow links)

如何从html中提取链接?爬取的网站与上一博文一致,仍然是 http://quotes.toscrape.com。

第一步,从页面中提取出需要的链接。

分析我们的页面,我们可以看到下一页的链接带有以下标记:

<ul class="pager">
    <li class="next">
        <a href="/page/2/">Next <span aria-hidden="true">span>a>
    li>
ul>

尝试在 scrapy shell 中抽取这一数据。

# 通过上面的CSS选择器,我们提取到了页面中的这个标签,但是我们需要的是这个标签中的href属性
>>> response.css('li.next a').get()
'Next '
# 通过使用attra(href)获得标签中的指定属性
>>> response.css('li.next a::attr(href)').get()
'/page/2/'
# .attrib也能获得指定的属性
>>> response.css('li.next a').attrib['href']
'/page/2'

现在让我们看看我们的爬虫被修改为递归地跟随到下一页的链接,从中提取数据:

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').get(),
                'author': quote.css('small.author::text').get(),
                'tags': quote.css('div.tags a.tag::text').getall(),
            }
		# 提取到下一页的链接
        next_page = response.css('li.next a::attr(href)').get()
        # 递归地获取每一个链接的下一页链接
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

在提取数据之后,parse() 方法查找到当前链接的下一页链接,使用 urljoin() 方法构建完整的绝对URL(因为链接可以是相对的)yield 爬取下一页的新 request ,然后在这个新的 request 中又调用了自身,通过这种递归的方式保证爬取到所有页面。

这就是 Scrapy 中追踪链接的机制,在回调函数中 yield (生成) 了一个新的request,当这个新的 request 完成时,scrapy 又会为这个request 调用回调函数。

2. 创建request的快捷方式

创建 request 的快捷方式就是使用 response.fllow()。

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').get(),
                'author': quote.css('span small::text').get(),
                'tags': quote.css('div.tags a.tag::text').getall(),
            }

        next_page = response.css('li.next a::attr(href)').get()
        if next_page is not None:
            yield response.follow(next_page, callback=self.parse)

与 scrapy.Request 不同的是,response.follow() 支持相对url,这就避免了使用 urljoin() 。response.follow() 返回的是Request 的一个实例,我们仍然可以 yield 这个实例对象。

我们还可以将选择器传递给 response.follow 而不是字符串,然后可以从这个选择器中提取需要的属性:

# response.css() 的返回值是一个选择器的list
for href in response.css('li.next a::attr(href)'):
	# href 是一个选择器
    yield response.follow(href, callback=self.parse)

对于 < a > 标签而言,response.css() 的默认返回值就是其href属性的值,因此,代码还可以简写成:

for a in response.css('li.next a'):
    yield response.follow(a, callback=self.parse)

需要注意的是,不能直接使用 response.follow(response.css('li.next a')),这是因为 response.css() 的返回值是一个选择器的list,而不是单个的选择器对象。

3. 更多例子

下面的代码将会从 http://quotes.toscrape.com/ 这个url开始,跟踪页面中的作者链接和下一页链接,然后分别调用回调函数parse_author() 和 parse() 处理响应。

import scrapy


class AuthorSpider(scrapy.Spider):
    name = 'author'

    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        # follow links to author pages
        # 跟踪作者页面的链接,然后调用parse_author() 回调
        for href in response.css('.author + a::attr(href)'):
            yield response.follow(href, self.parse_author)

        # follow pagination links
        # 跟踪下一页的链接,然后调用parse() 回调
        for href in response.css('li.next a::attr(href)'):
            yield response.follow(href, self.parse)

    def parse_author(self, response):
        def extract_with_css(query):
            return response.css(query).get(default='').strip()

        yield {
            'name': extract_with_css('h3.author-title::text'),
            'birthdate': extract_with_css('.author-born-date::text'),
            'bio': extract_with_css('.author-description::text'),
        }

需要注意的是,在 scrapy 中,同一个 url 并不会爬取多次,这是因为 scrapy 默认会过滤掉那些已经爬取过的 url,避免由于编程逻辑问题造成服务器访问过多的问题,通过 DUPEFILTER_CLASS 参数来配置。

请点击这里查看 官方例子 quotesbot

4. 使用 spider 参数

默认情况下,在命令行中 -a 参数 会将这些参数传递给 Spider 的 __init__方法,然后成为了 spider 的属性。

scrapy crawl quotes -o quotes-humor.json -a tag=humor

在代码中可以通过 self.tag 获得命令行中的 tag 的值。

import scrapy


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

    def start_requests(self):
        url = 'http://quotes.toscrape.com/'
        tag = getattr(self, 'tag', None)
        if tag is not None:
            url = url + 'tag/' + tag
            # 此时 url 的的值是 http://quotes.toscrape.com/tag/humor
        yield scrapy.Request(url, self.parse)

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').get(),
                'author': quote.css('small.author::text').get(),
            }

        next_page = response.css('li.next a::attr(href)').get()
        if next_page is not None:
            yield response.follow(next_page, self.parse)

5. 参考文献

[1] scrapy 官方文档
[2] 爬虫框架Scrapy的安装与基本使用 - 简书

你可能感兴趣的:(python3,爬虫)