网络爬虫Scrapy框架学习2

提取数据

学习如何使用Scrapy提取数据的最佳方法是使用shell Scrapy shell尝试选择器。

scrapy shell "http://quotes.toscrape.com/page/1/"

使用shell,您可以尝试使用CSS和响应对象选择元素:

>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
>>> response.css('title::text').extract()
['Quotes to Scrape']

运行的结果response.css(‘title’)是一个类似于列表的对象 SelectorList,它表示一个Selector包含XML / HTML元素的对象列表
一个是我们已经添加::text到CSS查询中,意味着我们只想在元素内直接选择文本元素 。如果我们不指定::text,我们将获得完整的title元素,包括其标签
调用的结果.extract()是一个列表,因为我们正在处理一个实例SelectorList。当你知道你只想要第一个结果时使用 extract_first()

 response.css('title::text').extract_first()
 response.css('title::text')[0].extract()
 //这2个是一样的
 //但是,当它找不到与选择匹配的任何元素时,使用.extract_first()避免IndexError和返回 None。

除了extract()和 extract_first()方法之外,您还可以使用该re()方法使用正则表达式进行提取:
了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']

XPath:简要介绍

除了CSS,Scrapy选择器还支持使用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。如果仔细阅读shell中选择器对象的文本表示,则可以看到。
使用XPath,您可以选择以下内容:选择包含文本“下一页”的链接。这使得XPath非常适合抓取任务,我们鼓励你学习XPath,即使你已经知道如何构造CSS选择器,它也会使抓取更容易。
整体:

$ scrapy shell 'http://quotes.toscrape.com'
>>>quote = response.css("div.quote")[0]
>>>title = quote.css("span.text::text").extract_first()
>>>tags = quote.css("div.tags a.tag::text").extract()

在我们的蜘蛛中提取数据

到目前为止,它并没有特别提取任何数据,只是将整个HTML页面保存到本地文件中。让我们将上面的提取逻辑集成到我们的蜘蛛中。

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(),
            }

输出如下:

scrapy crawl quotes -o quotes.json

由于历史原因,Scrapy会附加到给定文件而不是覆盖其内容。
您还可以使用其他格式,例如JSON Lines:

scrapy crawl quotes -o quotes.jl

获取页面中的链接

response.css('li.next a::attr(href)').extract_first()

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

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)

创建请求的快捷方式

作为创建Request对象的快捷方式,您可以使用 response.follow:

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

与scrapy.Request不同,它response.follow直接支持相对URL - 无需调用urljoin。注意,response.follow只返回一个Request实例; 你仍然需要提出这个请求。

您也可以传递选择器response.follow而不是字符串; 此选择器应提取必要的属性:

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

对于元素,有一个快捷方式:response.follow自动使用其href属性。所以代码可以进一步缩短:

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

更多示例和模式

import scrapy


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

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

    def parse(self, response):
        # follow links to author pages
        for href in response.css('.author + a::attr(href)'):
            yield response.follow(href, self.parse_author)

        # follow pagination links
        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).extract_first().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'),
        }

这个蜘蛛将从主页面开始,它将跟随作者页面的所有链接,parse_author为每个页面调用回调,以及parse我们之前看到的与回调的分页链接。
这个蜘蛛演示的另一个有趣的事情是,即使同一作者有很多引用,我们也不必担心多次访问同一作者页面。默认情况下,Scrapy会筛选出已访问过的URL的重复请求,从而避免因编程错误而导致服务器过多的问题。

使用蜘蛛参数

您可以-a 在运行蜘蛛时使用该选项为您的蜘蛛提供命令行参数:
scrapy crawl quotes -o quotes-humor.json -a tag=humor
这些参数传递给Spider的init方法,默​​认情况下变为spider属性。
在此示例中,为参数提供的值tag将通过self.tag。您可以使用此选项使您的蜘蛛只获取具有特定标记的引号,并根据参数构建URL:

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
        yield scrapy.Request(url, self.parse)

    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(),
            }

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

如果您将tag=humor参数传递给此蜘蛛,您会注意到它只会访问humor标记中的URL ,例如 http://quotes.toscrape.com/tag/humor

猜你喜欢

转载自blog.csdn.net/xianchanghuang/article/details/81667190