美文网首页
Scrapy初步

Scrapy初步

作者: cheerss | 来源:发表于2019-12-16 17:09 被阅读0次

    安装

    pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple
    

    概述

    scrapy是python下的一个爬虫(spider)库,据说也是目前使用最广的爬虫库

    scrapy包含两种运作方式,一种是在命令行中执行(就像在命令行直接直接python一样),一种是建立项目通过运行项目运行(就像运行python文件一样)

    教程

    网上教程很多,官方的resources里面也有很多链接到其他网站的教程,但是看来看去还是官方的教程讲的最明确,不仅会讲怎么用,还会讲为什么,这里记录一下官网初级教程里面的一点东西和使用注意事项。

    运行方式

    命令行

    安装完成后,可直接在命令行下执行scrapy shell其中scrapy的命令行模式,外观长得和ipython一模一样。下面以一个网页为例说说怎么用:

    1. fetch 会爬去一个网页,不需要接他的返回值,fetch之后会自动有一个response变量里面存了网页的内容
    fetch("http://openaccess.thecvf.com/CVPR2019.py")
    
    1. .css函数用于通过CSS过滤从response提取特定的内容,这里的过滤可以通过CSS中的id、class、标签等,id用'#'开头,class用'.'开头,如果是标签的话,就直接写。这个例子指的是所有class是ptitle的标签中的超链接。"::text"的含义是返回提取出的标签中的文本内容(即写在一对<a></a>之间的内容)。如果去掉::text,则会返回包含标签在内的所有内容。如果把::text替换为::attr(href)则代表返回标签中名字为href的属性,在这个例子中,就是返回超链接的地址。
    response.css(".ptitle a::text").extract()
    

    脚本程序

    scrapy startproject some_name # 在当前目录生成一个名字为some_name的项目
    cd some_name
    scrapy genspider cvpr2019 openaccess.thecvf.com/CVPR2019.py # 在当前项目中生成一个名字为cvpr2019的爬虫,注意这个
    

    项目目录如下,其中生成的所有爬虫都会在spiders目录下,所以现在spiders目录下应该还有一个cvpr2019.py的文件(下面没写出来):

    tutorial/
        scrapy.cfg            # deploy configuration file
    
        tutorial/             # project's Python module, you'll import your code from here
            __init__.py
    
            items.py          # project items definition file
    
            middlewares.py    # project middlewares file
    
            pipelines.py      # project pipelines file
    
            settings.py       # project settings file
    
            spiders/          # a directory where you'll later put your spiders
                __init__.py
    

    项目的修改就从就这里开始。所有的爬虫都必须继承scrapy.Spider。name是当前爬虫的名字,同一个项目中的任意两个爬虫都不能同名。start_requests必须返回一组可迭代的Request,爬虫会首先按照这个list逐个爬去,是爬虫开始的地方。allowed_domains是允许访问的域,所有不以这个域开头的网址在爬去过程中都会被过滤掉。

    class Cvpr2019Spider(scrapy.Spider):
        name = "cvpr2019"
        allowed_domains = ['openaccess.thecvf.com']
    
        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)
    

    为了简便起见,start_requests不写,而是定义一个start_urls的变量,默认的start_requests会读这个变量然后生成对应的一组requests。请求得到的response的默认回调函数是self.parse,当然如果是手写start_requests的话,就可以自定义回调函数的名字了。所以,上述代码等价于下列代码:

    class Cvpr2019Spider(scrapy.Spider):
        name = "cvpr2019"
        allowed_domains = ['openaccess.thecvf.com']
        start_urls = ['http://quotes.toscrape.com/page/1/', 'http://quotes.toscrape.com/page/2/']
    

    每个接受返回的函数都有一个变量response作为返回,response的内容就和命令行执行fetch之后得到的response一样一样的。如下:

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

    parse中yield的内容如果是字典等等,就会直接输出,如果是Request,则这个request会被加入到urls的队列中,因此就可以递归的去爬去网页了,如下面的代码。注意新的request的解析函数也可以不是parse,而定义其他的函数解析新的请求返回。下面的代码中用了urljoin,它的作用是把新的url加载当前url的后面,因为出现在网页中的url经常是相对路径,urljoin就是把当前的网页的路径和相对路径拼接起来形成绝对路径。

    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)
    

    上面的yield新的请求还有一种简便写法:yield response.follow(next_page, callback=self.parse),这里用了follow函数,它会返回一个请求,并且会自动把next_page当做相对路径,免去了自己写urljoin的麻烦。

    注意:请求的url必须是协议开头的(如http://),这个东西不能忽略,例如直接写www.xxx.com不行,而必须是http://www.xx.com。否则会报Missing scheme in request URL的错误。另allowed_domains的末尾不能包含多余的/,否则会有错误,可能把不想过滤的网址也过滤掉。

    写完之后,在项目目录下敲scrapy crawl xx -o output.out -a save_file 123.md即可开始爬虫,其中xx爬虫的明细,即class中的name。后面的两个参数都是可选的,-o表示把输出内容dump到output.out文件中,这里的输出用print是没用的,它特指解析每个请求后parse函数yield的内容。-a是传入的参数,它会被直接传递到爬虫的类的内部成为成员变量,所以,我们可以在函数中直接用self.save_file访问这个变量。

    下面给一个完整的例子,这个例子从cvpr2019的官网爬去了所有论文的题目和摘要并写到文件里,另每篇论文在网页上都有PDF的地址,所以只要稍加修改就能下载全部全文。

    # -*- coding: utf-8 -*-
    import scrapy
    
    
    class Cvpr2019Spider(scrapy.Spider):
        name = 'cvpr2019'
        allowed_domains = ['openaccess.thecvf.com']
        start_urls = ['http://openaccess.thecvf.com/CVPR2019.py/']
    
        def parse(self, response):
            for url in response.css(".ptitle a::attr(href)").extract():
                url = "http://openaccess.thecvf.com/" + url
                yield scrapy.Request(url, callback=self.parse_single_paper)
    
        def parse_single_paper(self, response):
            papertitle = response.css("#papertitle::text").extract_first().lstrip("\n")
            abstract = response.css("#abstract::text").extract_first().lstrip("\n").rstrip("\r")
            save_file = getattr(self, "save_file", None)
            if save_file is None:
                assert False, "save_file should not be None"
            with open(save_file, "a") as file:
                file.write("####%s\n" % papertitle)
                file.write(abstract + "\n\n\n")
    
    
    

    相关文章

      网友评论

          本文标题:Scrapy初步

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