Scrapy

作者: 奇而思 | 来源:发表于2020-03-18 15:45 被阅读0次

    基本用法

    1. python -m scrapy startproject yourproject 建立project
    2. 在project下的spider文件夹下,新建文件定义一个类,这个类要继承自scrapy.Spider。必须具有属性name,这个属性的值在整个project中不能重复。
      其他属性值可以在命令行中通过-a property_name=value的形式动态传入。
    3. start_urls属性,定义起始url。(也可以使用函数start_requests)
    4. parse函数(默认的回调函数),在该函数中对页面进行解析,并产生新的请求。这些新的请求由scrapy统一调度和去重。在parse函数中,yield新请求可以使用其他回调函数。结果可以通过dict形式yield输出。
    5. 运行
      scrapy crawl quotes -o quotes-humor.json -a tag=humor
      scrapy每次运行都会在旧文件上进行append,因此,对于json文件可能存在文件结构破坏的问题。可以使用ql

    Scrapy 更推荐使用XPath来解析
    使用scrapy shell来调试
    python -m scrapy shell "http://quotes.toscrape.com/page/1/"
    注意在windows中使用双引号

    Items

    import scrapy
    
    class Product(scrapy.Item):
        name = scrapy.Field()
        price = scrapy.Field()
        stock = scrapy.Field()
        tags = scrapy.Field()
        last_updated = scrapy.Field(serializer=str)
    

    items用于存放数据。
    通过实例的fields属性可以得到所有Field。访问和字典操作一致。
    深拷贝:product2 = product.deepcopy()

    CrawlSpider

    一个基于Spider但是功能丰富一点的spider,可以基于这个开始写你自己的spider。
    可以定义Rule和对应的callback函数。(注意,这里不要使用parse作为回调函数)

    ItemLoader

    Items提供存储数据的容器,ItemLoader提供批量产出的机制,便于维护

    使用

    在spider的parse回调函数中使用
    示例

    def parse(self, response):
        l = ItemLoader(item=Product(), response=response)
        l.add_xpath('name', '//div[@class="product_name"]')
        l.add_xpath('name', '//div[@class="product_title"]')
        l.add_xpath('price', '//p[@id="price"]')
        l.add_css('stock', 'p#stock]')
        l.add_value('last_updated', 'today') # you can also use literal values
        return l.load_item()
    

    过程:
    在ItemLoader中,每一个field都有一个input processor和output processor。input processor负责收集信息并保存在ItemLoader内部,调用load_item()函数后,由output processor产出item。

    如果需要在一个页面获取多个item,那么可以通过在Loader初始化时传入selector的形式。看例子:

    import scrapy
    from itemloader.items import quoteItem
    from itemloader.loaders import quoteLoader
    
    class QuotesSpider(scrapy.Spider):
        name = 'quotes'
        allowed_domains = ['127.0.0.1:8080']
        start_urls = ['http://127.0.0.1:8080/Quotes%20to%20Scrape.htm/']
    
        def parse(self, response):
            quotes = response.css('.quote')
            for quote in quotes:
                loader = quoteLoader(item=quoteItem(), selector=quote)
                loader.add_css('text', '.text::text')
                loader.add_css('author', '.author::text')
                loader.add_css('tags', '.tag::text')
                yield loader.load_item()
    

    声明

    通过继承ItemLoader来声明你自己的Itemloader类。每一个field有对应的_in, _out后缀的变量指明对应的processor,也可以通过default_input_processor default_output_processor指定默认的processor。

    当然也可以在Item定义时,在Field()内部定义。

    优先级为:

    1. ItemLoader中定义的field_in,field_out
    2. Item的Field定义的
    3. default_input_processor

    上下文:

    通过在函数中增加loader_context变量,可以使用ItemLoader内部的上下文
    这个上下文一般就是指初始化__init__()时传入的关键字参数或内部的一些属性设置。

    嵌套的Loader

    对于一些比较长的XPath,可以通过嵌套的方式简化代码或便于调试
    例如

    loader = ItemLoader(item=Item())
    # load stuff not in the footer
    footer_loader = loader.nested_xpath('//footer')
    footer_loader.add_xpath('social', 'a[@class = "social"]/@href')
    footer_loader.add_xpath('email', 'a[@class = "email"]/@href')
    # no need to call footer_loader.load_item()
    loader.load_item()
    

    processors

    1. 可以通过继承扩展processor,便于维护。例子参考官网
    2. output processor一般在field中定义。因为它一般取决于Field的内容,而不是和网站的parsing规则有关

    内置的processor

    1. scrapy.loader.processors.Identity: 原样返回输入内容,不接受loader_context参数
    2. TakeFirst:返回第一个非空值,对于单值的field,这个一般用于output processor,不接受loader_context
    3. Join(separator=u' '): 返回空格连接的数据,不接受loader_context
    4. Compose(*functions, **default_loader_context): 输入数据依次经过functions的处理,最终返回最后一个函数的结果。默认当返回None时退出,可以通过关键字参数stop_on_none=False改变。每个函数可以接受loader_context参数,如果有则会将当前的context传入。
    5. MapCompose(*functions, **default_loader_context)
      与Compose的区别在于参数传递的方式
      这个processor的输入被迭代,每个元素传递给第一个函数,得到的结果组成一个新的iteriable,然后依次传递给第二个函数,结果再组成iterable。如果返回的是list,会被展平,如果返回none会被忽略。最终,最后一个函数的结果组成iterable返回。
      这个一般用于input_processor,因为抽取数据使用的extract()一般返回一个列表。每个函数可以接受loader_context参数,如果有则会将当前的context传入。
    6. SelectJmes(json_path): 查询__init__方法中指定的json并返回结果,需要依赖 jmespath (https://github.com/jmespath/jmespath.py)

    pipeline

    对获取的item进行处理,典型的作用

    1. 清理html数据
    2. 验证获取的数据(是否包含某些特定的field)
    3. 检查重复数据
    4. 存储数据到数据库中

    pipeline也是python的类,必须包含以下方法

    1. process_item(self,item,spider)
      这个函数要么返回dict, Item,要么返回Deferred,或者raise DropItem exception
    2. open_spider(self,spider): 当spider开启时调用
    3. close_spider(self,spider)
    4. from_crawler(cls,crawler)
      如果这个函数存在,则这个类方法会在Crawler中调用以创建一个pipeline,必须返回一个pipeline实例。
      Crawler对象能够访问所有的Scrapy核心components,例如setting, signals。这是pipeline访问这些部件的一个方法。

    激活pipeline

    要使用pipeline,必须将对应的类放进setting中的ITEM_PIPELINES,

    ITEM_PIPELINES = {
        'myproject.pipelines.PricePipeline': 300,
        'myproject.pipelines.JsonWriterPipeline': 800,
    }
    

    后面的数字为优先级,从0-1000的数字,先经过数值低的pipeline

    selector

    推荐使用xpath。通过response.xpath("//div")的形式会返回一个selector对象。如果要从中取值,推荐使用get() getall()函数(2.0)替代之前的extract(), extract_first()函数。
    因为,get()函数总是返回第一个值,且为单值。getall()函数总是返回列表,即使只有一个值。这两个函数的结果更加通用。

    xpath中的相对路径

    如果使用嵌套selector,那么xpath如果以/开头,那么会认为是绝对路径。
    看例子,例如你想抽取文档中<div>下的所有<p>,你先写了:
    divs = response.xpath('//div')
    那么下面的语句是错误的,会找到所有的p

    for p in divs.xpath("//p"):
         print(p.get())
    

    应该写为:

    >>> for p in divs.xpath('.//p'):  # extracts all <p> inside
    ...     print(p.get())
    

    或者

    >>> for p in divs.xpath('p'):
    ...     print(p.get())
    

    查询class时考虑使用CSS

    CSS查询类更简洁。selector可以连接,因此可以用css查询类,后面再用xpath查询

    小区别

    注意//node[1](//node)[1]的区别
    //node[1] 选择每个在相应父节点下的node的第一个
    (//node)[1] 选择所有node的第一个

    XPath中使用变量

    response.xpath('//div[count(a)=$cnt]/@id', cnt=5).get()

    问题

    1. scrapy创建项目时报错:Fatal error in launcher: Unable to create process using 。。。

    这个问题是python的安装位置问题。解决方案:
    运行python -m scrapy startproject yourproject的方式来运行。

    相关文章

      网友评论

          本文标题:Scrapy

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