• Scrapy简介-快速开始-项目实战-注意事项-踩坑之路


    scrapy项目模板地址:https://github.com/w-x-x-w/Spider-Project

    Scrapy简介

    Scrapy是什么?

    • Scrapy是一个健壮的爬虫框架,可以从网站中提取需要的数据。是一个快速、简单、并且可扩展的方法。Scrapy使用了异步网络框架来处理网络通讯,可以获得较快的下载速度,因此,我们不需要去自己实现异步框架。并且,Scrapy包含了各种中间件接口,可以灵活的完成各种需求。所以我们只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页上的各种内容。
    • Scrapy并不是一个爬虫,它只是一个“解决方案”,也就是说,如果它访问到一个“一无所知”的网站,是什么也做不了的。Scrapy是用于提取结构化信息的工具,即需要人工的介入来配置合适的XPath或者CSS表达式。Scrapy也不是数据库,它并不会储存数据,也不会索引数据,它只能从一堆网页中抽取数据,但是我们却可以将抽取的数据插入到数据库中。

    Scrapy架构

    Scrapy Engine (引擎): 是框架的核心,负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。并在发生相应的动作时触发事件。
    **Scheduler (调度器): **它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,提供给引擎。
    **Downloader (下载器):**负责下载引擎发送的所有Requests请求,并将其获取到的Responses交还给引擎。
    **Spider (爬虫):**负责处理由下载器返回的Responses,并且从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给Scrapy Engine,并且再次进入Scheduler。
    **Item Pipeline (项目管道):**它负责处理Spider中获取到的Item,并进行进行后期处理(清理、验证、持久化存储)的地方.
    **Downloader Middlewares (下载中间件):**引擎与下载器间的特定钩子,一个可以自定义扩展下载功能的组件。处理下载器传递给引擎的Response。
    **Spider Middlewares(爬虫中间件):**引擎和Spider间的特定钩子,(处理进入Spider的Responses,和从Spider出去的Requests)

    快速开始-项目实战

    我们这里以某新闻网站新闻推送为例编写项目,仅用于学习,请勿恶意使用

    安装 Scrapy

    pip install Scrapy
    
    • 1

    创建项目

    scrapy startproject 项目名
    
    • 1
    HuxiuSpider/
        scrapy.cfg
        HuxiuSpider/
            __init__.py
            items.py
            pipelines.py
            settings.py
            spiders/
                __init__.py
                ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这些文件分别是:

    • scrapy.cfg: 项目的配置文件
    • HuxiuSpider/: 该项目的python模块。之后您将在此加入代码。
    • HuxiuSpider/items.py: 项目中的item文件.
    • HuxiuSpider/pipelines.py: 项目中的pipelines文件.
    • HuxiuSpider/settings.py: 项目的设置文件.
    • HuxiuSpider/spiders/: 放置spider代码的目录.

    更改设置

    • 注释robotstxt_obey
    # 第21行
    # Obey robots.txt rules
    # ROBOTSTXT_OBEY = True
    
    • 1
    • 2
    • 3
    • 设置User-Agent
    # 第18行
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    #USER_AGENT = "HuxiuSpider (+http://www.yourdomain.com)"
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'
    
    • 1
    • 2
    • 3
    • 4
    • 设置访问延迟
    # 第29行
    # Configure a delay for requests for the same website (default: 0)
    # See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
    # See also autothrottle settings and docs
    DOWNLOAD_DELAY = 3
    
    • 1
    • 2
    • 3
    • 4
    • 5

    开启pipline

    # Configure item pipelines
    # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
       "HuxiuSpider.pipelines.HuxiuspiderPipeline": 300,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    开启cookie(无需操作)(可选操作)

    # Disable cookies (enabled by default)
    #COOKIES_ENABLED = False
    
    • 1
    • 2

    设置频率(可不操作)

    # The download delay setting will honor only one of:
    # 定义了每个域名同时发送的请求数量
    CONCURRENT_REQUESTS_PER_DOMAIN = 2
    # 定义了每个IP同时发送的请求数量
    #CONCURRENT_REQUESTS_PER_IP = 16
    
    • 1
    • 2
    • 3
    • 4
    • 5

    命令行快速生成模板:

    scrapy genspider huxiu_article api-article.huxiu.com
    
    • 1

    Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类。
    其包含了一个用于下载的初始URL,如何跟进网页中的链接以及如何分析页面中的内容, 提取生成 item 的方法。
    为了创建一个Spider,您必须继承 scrapy.Spider 类, 且定义以下三个属性:

    • name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。
    • start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。(也可以删除此变量,但要重写start_requests方法)
    • parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。

    以下为我们的第一个Spider代码,保存在 HuxiuSpider/spiders 目录下的 huxiu_article.py 文件中:
    我们对于此段代码进行必要的解释:
    向一个url发送post请求,发送一个时间戳,可以获取这个时间戳以后的新闻推送,然后就是推送数据,关于数据提取等操作可以点开链接页自行观察,太过简单。

    爬虫程序模板:

    新闻列表页爬虫

    import json
    import time
    
    import scrapy
    
    from HuxiuSpider.items import HuxiuspiderItem
    
    
    class HuxiuArticleSpider(scrapy.Spider):
        def __init__(self):
            # 'https://www.huxiu.com/article/'
            self.url = 'https://api-article.huxiu.com/web/article/articleList'
    
        name = "huxiu_article"
        allowed_domains = ["api-article.huxiu.com"]
    
        def start_requests(self):
            timestamp = str(int(time.time()))
            form_data = {
                "platform": "www",
                "recommend_time": timestamp,
                "pagesize": "22"
            }
            yield scrapy.FormRequest(url=self.url, formdata=form_data, callback=self.parse)
    
        def parse(self, response):
            item = HuxiuspiderItem()
            res = response.json()
            success = res['success']
            print(res)
            if success:
                data = res['data']
                is_have_next_page = data['is_have_next_page']
                last_dateline = data['last_dateline']
                total_page = data['total_page']
                dataList = data['dataList']
    
                for data_obj in dataList:
                    item['url'] = 'https://www.huxiu.com/article/' + data_obj['aid'] + '.html'
                    item['title'] = data_obj['title']
                    item['author'] = data_obj['user_info']['username']
                    item['allinfo'] = json.dumps(data_obj, ensure_ascii=False)
    
                    item['visited'] = False
                    yield item
    
                if is_have_next_page:
                    form_data = {
                        "platform": "www",
                        "recommend_time": str(last_dateline),
                        "pagesize": "22"
                    }
                    yield scrapy.FormRequest(url=self.url, formdata=form_data, callback=self.parse)
            else:
                raise Exception('请求新闻列表的时候失败了~')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    Item模板:

    Item 是保存爬取到的数据的容器;其使用方法和python字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。
    类似在ORM中做的一样,您可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field 的类属性来定义一个Item。 (如果不了解ORM, 不用担心,您会发现这个步骤非常简单)(ORM其实就是使用类的方式与数据库进行交互)
    首先根据需要从huxiu.com获取到的数据对item进行建模。 我们需要从dmoz中获取名字,url,以及网站的描述。 对此,在item中定义相应的字段。编辑 HuxiuSpider 目录中的 items.py 文件:

    import scrapy
    
    class HuxiuspiderItem(scrapy.Item):
        url = scrapy.Field()
        title = scrapy.Field()
        author = scrapy.Field()
        # 存储尽量多的信息是必要的,以应对需求变更
        allinfo=scrapy.Field()
    
        visited=scrapy.Field()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    一开始这看起来可能有点复杂,但是通过定义item, 您可以很方便的使用Scrapy的其他方法。而这些方法需要知道您的item的定义。

    piplines模板:

    from pymongo import MongoClient
    from pymongo.errors import DuplicateKeyError
    
    class HuxiuspiderPipeline:
        def __init__(self):
            self.client=MongoClient('localhost',
                          username='spiderdb',
                          password='password',
                          authSource='spiderdb',
                          authMechanism='SCRAM-SHA-1')
            self.db = self.client['spiderdb']
            self.collection = self.db['huxiu_links']
    
            self.collection.create_index("url", unique=True)
    
        def process_item(self, item, spider):
            item = dict(item)
    
            try:
                self.collection.insert_one(item)
            except DuplicateKeyError as e:
                pass
    
            return item
    
        def close_spider(self, spider):
            self.client.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行爬虫

    进入项目的根目录,执行下列命令启动spider:

    scrapy crawl huxiu_article
    # scrapy crawl huxiu_article -o dmoz.csv
    
    • 1
    • 2

    完善项目-多层爬取

    yield scrapy.Request(item['url'], meta={'item': item}, callback=self.detail_parse)
    
    • 1

    https://blog.csdn.net/ygc123189/article/details/79160146

    注意事项

    自定义spider起始方式

    也可以是查询数据库的结果,但要注意数据统一性,因为scrapy是异步爬取

    自定义item类型与有无

    spider爬取的结果封装到item对象中,再提交给pipeline持久化,那么当然也可以忽略item对象,传递你想要的数据格式直接到pipeline。

    item与pipeline对应关系

    item的意思是数据实例,一个item提交后,会经过所有的pipeline,pipeline的意思是管道,就是对数据的一系列操作,设置中的管道优先级就是管道处理数据的顺序,比如日志操作等。
    如果要让某一个pipeline只处理某些类型的item,可以在item进入pipelne的时候判断一下是否是你想要处理的item类型。示例如下:

    class doubanPipeline(object):
        def process_item(self, item, spider):
            #判断item是否为Item1类型
            if isinstance(item,doubanTextItem):
                # 操作item
            return item
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    scrapy是异步执行的

    同时运行多个爬虫

    from scrapy.crawler import CrawlerProcess
    from scrapy.utils.project import get_project_settings
    
    settings = get_project_settings()
    
    crawler = CrawlerProcess(settings)
    
    crawler.crawl('exercise')
    crawler.crawl('ua')
    
    crawler.start()
    crawler.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    post表单数据传输需要是字符串

    自定义请求头

    import scrapy
    
    class AddHeadersSpider(scrapy.Spider):
        name = 'add_headers'
        allowed_domains = ['sina.com']
        start_urls = ['https://www.sina.com.cn']
        headers = {
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.5,en;q=0.3",
            "Accept-Encoding": "gzip, deflate",
            'Content-Length': '0',
            "Connection": "keep-alive"
        }
    
        def start_requests(self):
            for url in self.start_urls:
                yield scrapy.Request(url, headers=self.headers, callback=self.parse)
                
        def parse(self,response):
            print("---------------------------------------------------------")
            print("response headers: %s" % response.headers)
            print("request headers: %s" % response.request.headers)
            print("---------------------------------------------------------")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    scrapy的FormRequest发送的是表单数据类型,如果要发送json类型需要使用Request

    ts = round(time.time() * 1000)
    form_data = {
        "nodeId": id_str,
        "excludeContIds": [],
        "pageSize": '20',
        "startTime": str(ts),
        "pageNum": '1'
    }
    yield scrapy.Request(url=self.url,method='POST',headers=self.headers,
                         body=json.dumps(form_data), callback=self.parse,
                         meta={'id_str': id_str})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    将JavaBean交给IoC容器管理和依赖注入DI
    uniapp 扫码功能
    微服务 第三章 Spring Cloud 简介
    稀疏数组举例详解(Java形式表示)
    [springmvc学习]8、JSR 303验证及其国际化
    vue修饰符 lazy number trim
    java游戏服务器性能压测神器:jprofiler
    <C++>vector容器在算法题中应用那么广泛,确定不来深入了解一下吗
    使用nrm 方式 管理npm 仓库
    工具清单 - 项目管理
  • 原文地址:https://blog.csdn.net/weixin_62650212/article/details/132709328