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)
    
  • 相关阅读:
    python给邮箱发送消息
    shell 的echo和 printf
    shell 基本运算符
    shell傳遞參數
    shell變量和數組
    pycharm的放大和缩小字体的显示 和ubunt的截圖工具使用 ubuntu上安装qq微信等工具
    flask的g对象
    mysqlcilent的安装
    Ubuntu安装 和 python开发
    使用python来建立http服务
  • 原文地址:https://www.cnblogs.com/borntodie/p/14849062.html
Copyright © 2011-2022 走看看