zoukankan      html  css  js  c++  java
  • 6. Scrapy的基本用法

    Scrapy基础入门

    一、什么是Scrapy?

    Scrapy是一个未来爬取网站数据,提取结构性数据而编写的应用框架,在爬虫界非常出名,非常强悍。所谓的框架就是一个已经集成了各种功能(高性能异步下载,队列,分布式,解析,持久化等)的具有很强通用性的项目模板。对于框架的学习,重点是学习框架的特性,各个功能的用法即可。

    二、安装

    # mac、linux
    	pip install scrapy
        
    # windows
    	1.  pip install wheel
        2. 下载twisted    http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
        3.  进入下载目录,执行 pip install Twisted-17.1.0-cp-cp35m-win_amd64.whl
        4. pip install pywin32
        5. pip install scrapy
    

    三、基础使用

    1. 创建项目:scrapy startproject 项目名

    scrapy startproject spiderProject
    
    • 项目目录结构

      - spiderProject
      	- spiderProject
              - spiders  				# 爬虫文件夹,里面必须存放一个爬虫文件
                  - __init__.py       
              - items.py				# 将爬取到的字段进行完整的格式化
              - middlewares.py		# 爬虫工程的的中间件
              - pipelines.py			# 爬虫工程的管道文件
              - settings.py			# 爬虫工程的配置文件
          - scrapy.cfg
      

    2. 进入爬虫项目文件:cd 项目名

    cd spiderProject
    

    3. 创建爬虫文件:scrapy genspider 爬虫文件名 目标网站url

    scrapy genspider FirstBlood www.xxx.com
    
    • 爬虫文件内部代码解释

      import scrapy
      
      class FirstSpider(scrapy.Spider):
          # 爬虫文件名称:当前源文件的唯一标识
          name = 'first'
          # 允许的域名
          # allowed_domains = ['www.xxx.com']
      
          # 其实的url列表:只可以存储url
          # 作用:列表中存储的url都会被进行get请求的发送
          start_urls = ['http://www.baidu.com/', 'http://www.sogou.com']
      
          # 数据解析
          # parse方法调用的次数完全取决于请求的次数
          # 参数response:表示的就是服务器返回的响应对象
          def parse(self, response):
              print(response)
      
      

    4. 执行爬虫项目:scrapy crawl 爬虫问文件名

    scrapy scawl FirlstBlod
    

    5. settings.py文件初始化

    以后只要使用scrapy框架,settings.py文件内上来就要写上这三句话
    1. USER_AGENT = '' # UA伪装
    2. ROBOTSTXT_OBEY = False # 禁用robots协议
    3. LOG_LEVEL = 'ERROR' # 执行日志输出类型
    

    四、简单项目示例

    • 创建项目

      1. scrapy startproject wangziPro2. cd wangziPro3. scrapy genspider duanzi www.xxx.com
      

    settings.py

    BOT_NAME = 'wangziPro'SPIDER_MODULES = ['wangziPro.spiders']NEWSPIDER_MODULE = 'wangziPro.spiders'# Crawl responsibly by identifying yourself (and your website) on the user-agentUSER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'ROBOTSTXT_OBEY = FalseLOG_LEVEL = 'ERROR'ITEM_PIPELINES = {    # 300表示:管道类的优先级,数值越小优先级越高    # 优先级高:优先级越高的管道类先被执行   'wangziPro.pipelines.WangziproPipeline': 300,   'wangziPro.pipelines.MysqlPipeLine': 301,}######################  总结  #######################这里需要按照配置UA、robots协议、日志等级、开启管道
    

    duanzi.py

    import scrapyfrom ..items import WangziproItemclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['www.xxx.com']    start_urls = ['https://duanziwang.com/category/经典段子/index.html']    # 数据解析    # def parse(self, response):    #     # 数据解析名称和内容    #     article_list = response.xpath('/html/body/section/div/div/main/article')    #     for article in article_list:    #         # 下面解析出来的内容不是字符串数据,说明和etree中的xpath使用方式不同    #         # xpath返回的列表中存储的是Selector对象,其实我们想要的字符串数据被存储在该对象的data中    #         # title = article.xpath('./div[1]/h1/a/text()')[0]    #         # content = article.xpath('./div[2]/p/text()')[0]    #    #         # 将Selector对象data属性值取出    #         # extract()就是将data属性值取出(几乎不用)    #         # title = article.xpath('./div[1]/h1/a/text()')[0].extract()    #         # content = article.xpath('./div[2]/p/text()')[0].extract()    #    #         # extract_first():将列表中第一个元素表示的Selector对象中的data值取出    #         title = article.xpath('./div[1]/h1/a/text()').extract_first()    #         content = article.xpath('./div[2]/p/text()')[0].extract_first()    #    #         # 直接使用列表调用extract():可以将列表中每一个元素表示的Selector对象中的data值取出    #         # title = article.xpath('./div[1]/h1/a/text()').extract()    #         # content = article.xpath('./div[2]/p/text()').extract()    #         print(title,content)    #         break    # 将解析到的数据进行持久化存储    # 基于终端指令的持久化存储    # def parse(self, response):    #     all_data = []    #     # 数据解析名称和内容    #     article_list = response.xpath('/html/body/section/div/div/main/article')    #     for article in article_list:    #         title = article.xpath('./div[1]/h1/a/text()').extract_first()    #         content = article.xpath('./div[2]/p/text()').extract_first()    #         dic = {    #             'title':title,    #             'content':content    #         }    #         all_data.append(dic)    #     return all_data    # 基于管道的持久化存储    def parse(self, response):        # 数据解析名称和内容        article_list = response.xpath('/html/body/section/div/div/main/article')        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()').extract_first()            content = article.xpath('./div[2]/p/text()').extract_first()            # 实例化一个item类型的对象,将解析到的数据存储到该对象中            item = WangziproItem()            # 不可以通过.的方式调用属性            item['title'] = title            item['content'] = content            # 将item对象提交给管道            yield item            # item会不会依次提交给三个管道类######################  总结  #######################1. 这里的数据解析使用的xpath,但是和lxml中的xpath用法有点不同	- article.xpath('./div[1]/h1/a/text()')[0]此种方法获取到的是Selector对象    - scrapy中的用法    	- article.xpath('./div[1]/h1/a/text()')[0].extract()    # 返回的是异地个元素中的文本        - article.xpath('./div[1]/h1/a/text()').extract_first() # 返回的是异地个元素中的文本        - article.xpath('./div[1]/h1/a/text()').extract()  		# 返回的是一个列表        2. 爬虫文件与items文件对接	- 在items文件中定义一个对接类	- 在爬虫文件中实例化一个items中的对接类对象    	- 此处的对接类对象不能使用.的方式传值    - 通过yield方式将items对接类中的对象传给pipelines文件
    

    items.py

    import scrapyclass WangziproItem(scrapy.Item):    # define the fields for your item here like:    # name = scrapy.Field()    # Field()订阅号的属性当做是一个万能类型的属性    title = scrapy.Field()    content = scrapy.Field()    ######################  总结  #######################1. 爬虫文件中解析出几个字段,这里就写几个字段2. 这里的item本质就是一个字典
    

    pipelines.py

    from itemadapter import ItemAdapterimport pymysqlclass WangziproPipeline(object):    fp = None    # 重写父类的两个方法    def open_spider(self,spider):        print('我是open_spider(),我只会在爬虫开始的时候执行一次!')        self.fp = open('duanzi.txt','w',encoding='utf-8')    def close_spider(self,spider):        print('我是close_spider(),我只会在爬虫结束的时候执行一次!')        self.fp.close()    # 该方法是用来接收item对象,一次只只能接收一个item,说明该方法会被调用多次    # 参数item:就是接收的item对象    def process_item(self, item, spider):        # print(item) # item其实就是一个字典        self.fp.write(item['title'] + ':' + item['content'] + '
    ')        # 将item存储到本地文件        return item# 将数据存储到mysql中class MysqlPipeLine(object):    conn = None    cursor = None    def open_spider(self,spider):        self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123456',db='spider',charset='utf8')        print(self.conn)    def process_item(self,item,spider):        self.cursor = self.conn.cursor()        sql = 'insert into duanziwang values ("{}","{}")'.format(item['title'],item['content'])        # 事务处理        try:            self.cursor.execute(sql)            self.conn.commit()        except Exception as e:            print(e)            self.conn.rollback()    def close_spider(self,spider):        self.cursor.close()        self.conn.close()        ######################  总结  #######################1. 一个scrapy项目中只能有一个pipelines文件2. 一个pipelines文件中可以有多个管道类close_spider(s)3. 管道类直接对接item对象4. 每个管道类中必须有一个process_item()方法5. 可以重写父类方法open_spider(self,spider)和close_spider(self,spider)。这两个方法只会执行一次
    

    五、持久化存储

    1. 基于终端指令的持久化存储

    • 要求:该种方式只可以将parse方法(数据解析)的返回值存储到本地指定后缀的文件中

    • 执行命令:scrapy crawl spiderName -o filePath

    2. 基于管道的持久化存储步骤

    1. 在爬虫文件中进行数据解析
    2. 在items.py中定义相关属性
      • 步骤1中解析出几个字段,就在此处定义几个字段
    3. 在爬虫文件中将解析到的数据存储封装到Item类型的对象中
    4. 将Item类型的对象提交给管道
    5. 在管道文件(pipelines.py)中,接收爬虫文件提交过来的Item类型对象,且对其进行任意形式的持久化存储操作
    6. 在配置文件(setting.py)中开的管道机制

    3. 基于管道实现数据的备份

    • 将爬取到的数据分别存储到不同的载体中。
    • 实现:将数据一份存储到MySQL中,一份存储到Redis中。
    • 问题1:管件中的一个管道类表示怎样的一组操作?
      • 一个管道类对应一种形式的持久化存储操作。如果将数据存储到不同的载体中就需要使用多个管道类。
    • 问题2:item会不会依次提交给三大管道类?
      • 不会,爬虫文件的item只会被提交给优先级最高的管道类
      • 优先级高的管道类需要在process_item中将item对象传出去,给到下一个管道类。

    六、scrapy手动发送请求

    1. 为什么start_urls列表中的url会自动进行get请求发送?

    # 查看源码发现
    def start_requests(self):
    	cls = self.__class__
        if not self.start_urls and hasattr(self, 'start_url'):
    		raise AttributeError(
                "Crawling could not start: 'start_urls' not found "
                "or empty (but found 'start_url' attribute instead, "
                "did you miss an 's'?)")
    	if method_is_overridden(cls, Spider, 'make_requests_from_url'):
            warnings.warn(
                "Spider.make_requests_from_url method is deprecated; it "
                "won't be called in future Scrapy releases. Please "
                "override Spider.start_requests method instead "
                f"(see {cls.__module__}.{cls.__name__}).",
            )
            for url in self.start_urls:
                yield self.make_requests_from_url(url)
    	else: # 重点
            for url in self.start_urls:
                yield Request(url, dont_filter=True)
    

    2. 如何将start_urls列表中的url进行post请求发送

    重写Spider类中start_requests()方法

    def start_requests(self):	cls = self.__class__    if not self.start_urls and hasattr(self, 'start_url'):		raise AttributeError(            "Crawling could not start: 'start_urls' not found "            "or empty (but found 'start_url' attribute instead, "            "did you miss an 's'?)")	if method_is_overridden(cls, Spider, 'make_requests_from_url'):        warnings.warn(            "Spider.make_requests_from_url method is deprecated; it "            "won't be called in future Scrapy releases. Please "            "override Spider.start_requests method instead "            f"(see {cls.__module__}.{cls.__name__}).",        )        for url in self.start_urls:            yield self.make_requests_from_url(url)	else: # 重点        for url in self.start_urls:            yield FormRequest(url, callback=回调函数,formdata='请求需携带的参数')
    

    3. 实现手动发送请求爬取全站数据

    import scrapyfrom handReqPro.items import HandreqproItemclass DuanziSpider(scrapy.Spider):    name = 'duanzi'    # allowed_domains = ['https://duanziwang.com/']    start_urls = ['https://duanziwang.com/category/经典段子/index.html']    # 通用模板    url = 'https://duanziwang.com/category/经典段子/%d/index.html'    page_num = 2    # 将段子王中所有的页码对应的数据进行爬取    def parse(self, response):        # 数据解析名称和内容        article_list = response.xpath('/html/body/section/div/div/main/article')        for article in article_list:            title = article.xpath('./div[1]/h1/a/text()').extract_first()            content = article.xpath('./div[2]/p/text()').extract_first()            item = HandreqproItem()            item['title'] = title            item['content'] = content            yield item        if self.page_num < 5: # 递归结束的条件            new_url = format(self.url%self.page_num) # 其他页码对应的完整url            self.page_num += 1            # 对新的页码对应你的url进行请求发送(手动请求get发送)            yield scrapy.Request(url=new_url,callback=self.parse)
    
  • 相关阅读:
    【数学】三分法
    【数学】【背包】【NOIP2018】P5020 货币系统
    【数学】【CF27E】 Number With The Given Amount Of Divisors
    【单调队列】【P3957】 跳房子
    【极值问题】【CF33C】 Wonderful Randomized Sum
    【DP】【CF31E】 TV Game
    【神仙题】【CF28D】 Don't fear, DravDe is kind
    【线段树】【CF19D】 Points
    【字符串】KMP字符串匹配
    【二维树状数组】【CF10D】 LCIS
  • 原文地址:https://www.cnblogs.com/borntodie/p/14849062.html
Copyright © 2011-2022 走看看