美文网首页程序员
Scrapy快速入门实战

Scrapy快速入门实战

作者: 天凉玩个锤子 | 来源:发表于2019-10-07 19:40 被阅读0次
    Scrapy架构图

    创建一个Scrapy项目

    1.创建项目文件夹tutorial

    scrapy startproject tutorial
    

    2.创建Spider类

    Spider的用法

    使用命令行创建一个Spider

    cd tutorial
    scrapy genspider quotes quotes.toscrape.com
    

    第一个参数是Spider的名称,第二个参数是网站域名
    执行完毕后,spider文件夹中多了一个quotes.py。

    • name 项目的名字,用于区分不同的Spider
    • allowed_domains,允许爬取的域名
    • start_urls,它包含了Spider在启动时爬取的url列表,初始请求是由它来定义的。
    • parse,Spider的一个方法,负责解析下载start_urls里面的连接构成的请求后返回的相应、提取数据或者进一步生成要处理的请求
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapyspider.items import QuoteItem
    
    
    class QuotesSpider(scrapy.Spider):
        name = 'quotes'
        allowed_domains = ['quotes.toscrape.com']
        start_urls = ['http://quotes.toscrape.com/']
    
        def parse(self, response):
            quotes = response.css('.quote')
            for quote in quotes:
                item = QuoteItem()
                item['item'] = quote.css('.text::text').extract_first()
                item['author'] = quote.css('.author::text').extract_first()
                item['tags'] = quote.css('.tags::text').extract()
                yield item
    
            next = response.css('.pager .next a::attr(href)').extract_first()
            url = response.urljoin(next)
            yield scrapy.Request(url=url,callback=self.parse)
    

    3.创建Item

    Item是保存爬取数据的容器。
    类似于字典+额外的保护机制,避免拼写错误或者定义字段错误。
    创建时需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段。

    # -*- coding: utf-8 -*-
    
    # Define here the models for your scraped items
    #
    # See documentation in:
    # https://doc.scrapy.org/en/latest/topics/items.html
    
    import scrapy
    
    class QuoteItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        text = scrapy.Field()
        author = scrapy.Field()
        tags = scrapy.Field()
    

    4.解析Response&使用Item

    parse()方法中,直接对response变量包含的内容进行解析,解析**text、author、tags。

    页面结构
    def parse(self, response):
            quotes = response.css('.quote')
            for quote in quotes:
                item = QuoteItem()
                item['item'] = quote.css('.text::text').extract_first()
                item['author'] = quote.css('.author::text').extract_first()
                item['tags'] = quote.css('.tags::text').extract()
                yield item
    

    选择器selector:CSS或XPath

    Scrapy提供了两个实用的快捷方法
    response.xpath()和response.css()
    二者功能完全等同于
    response.selector.xpath()和response.selector.css()

    CSS

    这里使用CSS选择器,给出CSS选择器的基础使用规则。

    选择器 例子 含义
    .class .aaa 选择class="aaa"的所有元素
    #id #firstname 选择 id="firstname" 的所有元素
    * 选择所有元素
    element p 选择所有<p>元素
    element element div p 选择 <div> 元素内部的所有 <p> 元素
    a[title] 选取所有拥有title属性的a元素

    对于text
    extract_first()方法获取结果的第一个元素,可以避免空列表取[0]导致数组越界。extract_first('Default Image')匹配不到结果时返回该参数Default Image
    extract()方法获取所有结果组成的列表

    XPath

    表达式 描述
    nodename 选取此节点的所有子节点
    / 从当前节点选取直接子节点
    // 从当前节点选取子孙节点
    . 选取当前节点
    .. 选取当前节点的父节点
    @ 选取属性/属性获取
    [ ] 增加限制条件
    text() 文本获取

    属性多值匹配-某些节点的某个属性可能有多个值。

    使用contains()函数,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配了。
    例如代码:

    from lxml import etree
    text = '''
    <li class="li li-first"><a href="link.html">first item</a></li>
    html = etree.HTML(text)
    result = html.xpath('//li[contains(@class,"li")]/a/text()')
    print(result)
    
    //运行结果
    ['first item']
    

    多属性匹配-根据多个属性确定一个节点

    from lxml import etree
    text = '''
    <li class="li li-first" name="item"><a href="link.html">first item</a></li>
    html = etree.HTML(text)
    result = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
    print(result)
    
    //运行结果
    ['first item']
    
    运算符及其介绍

    正则匹配

    response对象不能直接调用re()和re_first()方法。如果想要对全文进行正则匹配,可以先调用xpath()方法再正则匹配。

    5.后续Request

    上面操作实现从初始页面抓取内容。

    1. 我们需要从当前页面中找到信息来生成下一个请求
    2. 在下一个请求的页面里找到信息再构造再下一个请求。
    3. 这样循环往复迭代,从而实现整站的爬取。
      Next按钮
      基于按钮的链接:http://quotes.toscrape.com/page/2
      利用scrapy.Request构造下一个请求
    scrapy.Request两个参数
    • url 请求链接
    • callback 回调函数。当指定了该回调函数的请求完成后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求,回调函数如上文的

    parse()所示。
    由于每页的结构相同,可以再次使用parse()方法来做页面解析

    next = response.css('.pager .next a::attr(href)').extract_first()
            url = response.urljoin(next)
            yield scrapy.Request(url=url,callback=self.parse)
    
    1. 获取a超链接中的href属性,这里用到了::attr(href)操作。然后再调用extract_first()方法获取内容。
    2. urljoin()方法,将相对URL构造成一个绝对的URL(补全URL)
    3. 利用scrapy.Request构造新请求

    6.运行

    scrapy crawl quotes
    

    这里运行的时候有各种可能的原因导致不能从目标获取内容

    1. 网址的错误
    2. 格式错误如缩进问题
    3. 网页有反爬虫,
    • 在settings文件中添加USER_AGENT
    • 添加IP代理
    • 改机器人协议以及cookie
    • 设置延迟
    1. 分段函数中所要爬取的url有反爬虫,可以添加print(requests.get(url).status_code)调试

    错误排查

    7.保存到文件

    Scrapy提供的Feed Exports可以轻松抓取结果输出。

    #json
    scrapy crawl quotes -o quotes.json
    #jsonline 每一个Item输出一行JSON
    scrapy crawl quotes -o quotes.jl
    scrapy crawl quotes -o quotes.csv
    scrapy crawl quotes -o quotes.xml
    scrapy crawl quotes -o quotes.pickle
    scrapy crawl quotes -o quotes.marshal
    #ftp 远程输出
    scrapy crawl quotes -o ftp://user::pass@ftp.example.com/path/to/quotes.csv
    

    8.使用Item Pipeline(项目管道)

    调用发生在Spider产生Item之后。

    当Spider解析完Response之后,Item就会传递到Item Pipeline,被定义的Item Pipeline组件会顺次调用,完成一连串的处理过程,比如数据清洗、存储等。

    主要功能有4个:

    • 清理HTML数据
    • 验证爬取数据,检查爬取字段。
    • 查重并丢弃重复内容。
    • 将爬取结果保存到数据库

    将结果保存到MongoDB或者筛选Item。

    核心方法

    • 必须要实现的一个方法是:process_item(item,spider)
      被定义的Item Pipeline会默认调用这个方法对Item进行处理。
      返回:Item类型的值(此Item会被低优先级的Item Pipeline的process_item()方法处理,直到所有的方法被调用完毕)或者抛出一个DropItem异常(丢弃该Item,不再进行处理)。
    • 实用方法
      open_spider(self,spider)
      在Spider开启的时候被自动调用,可以在这里做一些初始化操作,如开启数据库连接等。
    • 实用方法
      close_spider(spider)
      在Spider关闭时自动调用,可以在这里做一些收尾工作,如关闭数据库等。
    • 实用方法
      from_crawler(cls,crawler)
      是一个类方法,用@classmethod标识,是一种依赖注入的方式。可以通过crawler对象拿到Scrapy的所有核心组件。
      参数:cls就是Class
      返回:Class实例

    9.Downloader Middleware的用法

    下载中间件,是处于Scrapy的Request和Response之间的处理模块。

    1. 在Scheduler调度出队列的Request发送给Downloader下载之前,也就是可以在Request执行下载之前对其进行修改。
    2. 下载后生成的Response发送给Spider之前,可以在生成Response被Spider解析之前对其进行修改。
      内置的Downloader Middleware

    优先级问题
    数字越小越靠近Scrapy引擎,数字越大越靠近Downloader,数字小的Downloader Middleware会被优先调用。

    返回类型不同,产生的效果也不同。

    • None。Scrapy继续处理该Request,按照设置的优先级顺序依次执行其他Downloader Middleware的process_request()方法对Request进行修改,一直到Downloader把Request执行后得到Response才结束。
    • Response对象。不再继续调用更低优先级的Downloader Middleware的process_request()和process_exception()方法,每个Downloader Middleware的process_response()方法转而被依次调用。调用完毕之后,直接将Response对象发送给Spider来处理。
    • Request对象。停止执行更低优先级的Downloader Middleware的process_request()方法。将此Request重新放到调度队列里,相当于一个全新的Request,等待被调度。
    • IgnoreRequest异常抛出。所有的Downloader Middleware的process_exception()方法会依次执行。若没有一个方法处理这个异常,则Request的errorback()方法就会回调。如果该异常还没有被处理,那么忽略。

    核心方法有三个,只需要实现至少一个方法,就可以定义一个Downloader Middleware

    • process_request(request, spider)
      Request被Scrapy引擎调度给Downloader之前,该方法会被调用。也就是在Request从队列里调度出来到Downloader下载执行之前,我们都可以用process_request()方法对Request进行处理。
      返回:None/Response/Request/抛出IgnoreRequest异常。
    • process_response(request, response, spider)
      收到Response后,在Scrapy引擎将Response发送给Spider进行解析之前,可以用该方法对Response进行处理。
      返回:Request对象/Response对象/抛出IgnoreRequest异常。
    • process_exception(request, exception, spider)
      当Downloader或process_request()方法抛出异常时该方法被调用。
      返回:None/Response对象/Request对象。

    修改请求时的User-Agent的两种方式:

    1. 只需要在setting.py中添加一行USER_AGENT的定义即可。
    USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
    
    1. 设置随机的User-Agent,需要借助Downloader Middlewareprocess_request()方法。
      在middlewares.py里面添加一个RandomUserAgentMiddleware的类,如下所示:
    import random
    
    class RandomUserAgentMiddleware():
      def __init__(self):
        self.user_agents = [    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
        "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
        "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
        "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
        "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
        "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
        "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
        "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
        "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
        "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
        "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52"
        ]
      def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)
    

    要使之生效,我们需要去调用这个Downloader Middleware。在settings.py中将DOWNLOADER_MIDDLEWARES取消注释并设置成如下内容:

    DOWNLOADER_MIDDLEWARES = { 'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
    }
    

    注意:scrapydownloadertest是你项目的文件夹名称,记得修改哦。

    10. Spider Middleware的用法

    scrapy提供了内置的基础Spider Middleware。如果想要扩展其功能,只需要实现某几个方法即可。
    每个Spider Middleware都定义了以下一个或多个方法的类,只需要实现其中一个方法就可以定义一个Spider Middleware,核心方法有如下4个:

    • process_spider_input(response,spider)
      当Response被Spider Middleware处理时,该方法被调用。
      参数:response,即被处理的Response;spider,即该Response对应的Spider。
      返回:None(继续处理Response,调用其他的Spider Middleware,指导Spider处理该Response)或抛出一个异常(调用Request的errback()方法,errback的输出重新输入到中间件中,使用process_spider_output()方法来处理,当其抛出异常时则调用process_spider_exception()来处理)
    • process_spider_output(response,result,spider)
      当Spider处理Response返回结果时,process_spider_output()方法被调用。
      返回:包含Request或Item对象的可迭代对象。
    • process_spider_exception(response,exception,spider)
      当Spider或Spider Middleware的process_spider_input()方法抛出异常时,该方法被调用。
      返回:None(Scrapy继续调用其他Spider Middleware中的process_spider_exception()方法处理该异常,直到所有Spider Middleware都被调用);可迭代对象(其他Spider Middleware的process_spider_output()方法被调用)
    • process_start_requests(start_requests,spider)
      以Spider启动的Request为参数被调用,执行过程类似于process_spider_output(),但没有相关联的Response,并且必须返回Request。
      返回:必须返回一个包含Request对象的可迭代对象。

    可迭代对象:Python中经常使用for来对某个对象进行遍历,此时被遍历的这个对象就是可迭代对象,像常见的list,tuple都是。如果给一个准确的定义的话,就是只要它定义了可以返回一个迭代器的__ iter __ 方法,或者定义了可以支持下标索引的__ getitem __方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。

    相关文章

      网友评论

        本文标题:Scrapy快速入门实战

        本文链接:https://www.haomeiwen.com/subject/vjduuctx.html