scrapy基本操作流程

基本配置与命令

1.安装

win系统下有5个步骤

         pip3 install wheel
         twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
         pip3 install Twisted 17.1.0 cp35 cp35m win_amd64.wheel
         pip install pywin32
         pip install scrapy

2.创建

       scrapy startproject 项目名

         - 创建普通爬虫文件

        cd 项目名 (进入项目目录之后再创建爬虫文件)
        scrapy genspider 爬虫文件名 www.xxx.com   

         - 创建crawlspider的爬虫文件

        scrapy genspider -t crawl 爬虫文件名 www.xxx.com

         - 一般注释掉 allowed_domains = ['www.xxx.com']

3.配置项目

   ROBOTSTXT_OBEY = False 不遵守机器人规则
   USER_AGENT = '' 配置UA
   LOG_LEVEL = 'ERROR'  配置日志级别

         中间件配置,管道配置时将注释取消即可  

4.执行项目

   scrapy crawl 爬虫文件名
   scrapy crawl 爬虫文件名 --nolog 不显示日志运行
   scrapy crawl 爬虫文件名 -o 文件名.csv/json 保存结果到文件

应用

1.持久化本地存储

         1.使用命令,保存的本地文件格式限制为 (json csv xml jl jsonlines marshal pickle)

        scrapy crawl 爬虫文件名 -o 本地文件名.csv 

         2.使用管道

                  - 在settings.py中开启管道!!!

                  - items.py文件中添加字段,如  name = scrapy.Field() 

                  - 注意在爬虫文件中要yield item 管道类中才能接受到item

                  - 在piplines.py文件中的管道类中的process_item函数中处理

                          - 爬虫类

        •  open_spider()
        •  close_spider()
        •  process_item()

2.爬取全部网站的数据

         手动请求发送,在爬虫文件中的parse中,(callback是回调另一个parse解析函数)

       yield scrapy.Request(url,callback)

3.进行post请求发送

         spider类中重写start_requests(self)方法,yield scrapy.FormRequest(url,parse,formdata)

       def start_requestS(self):
           for url in self.start_urls:
               data = { 'kw':'cat'}
               yield scrapy.FormRequest(url=url,callback=self.parse,formdata=data)

4.处理cookie

         - scrapy默认情况下自动处理cookie,在settings.py中配置  

          COOKIES_ENABLED = False

5.请求传参

  - 使用场景:如果使用scrapy爬取的数据没有在同一张页面中,则必须使用请求传参

  - 编码流程:

  - 需求:爬取的是 首页中电影的名称和 详情页中电影的简介(全站数据爬取)

  - 基于起始url进行数据解析(parse)

    - 解析数据

      • 电影的名称
      • 详情页的url
      • 对详情页的url发起手动请求(指定的回调函数parse_detail),进行请求传参(meta)meta传递给parse_detail这个回调函数

               - 封装一个其他页码对应url的一个通用的URL模板

               - 在for循环外部,手动对其他页的url进行手动请求发送(需要指定回调函数==》parse)

               - 定义parse_detail回调方法,在其内部对电影的简介进行解析。解析完毕后,需要将解析到的电影名称和电影的简介封装到同一个item中。

               - 接收传递过来的item,并且将解析到的数据存储到item中,将item提交给管道 

         def parse_news(self,response):
                  item = WangyiproItem()
                  item['title'] = title
                  yield scrapy.Request(url=news_detail_url,callback=self.parse_detail,meta={'item':item})
              
         def parse_detail(self,response):
                  item = response.meta['item']
                  item['content'] = content
                  yield item

5.中间件

         - 在settings中要取消注释才能生效

         - DOWNLOADER_MIDDLEWARES 下载中间件: 批量拦截所有的请求和响应

         - process_request 拦截请求

    1.  用来进行UA伪装,结合UA列表使用
    2.  使用代理ip,结合ip池使用

         - process_response 拦截相应

                  可以在爬取动态加载的页面时结合selenium使用

         from scrapy import signals

         import random

 

         #批量拦截所有的请求和响应

         class MiddlewearproDownloaderMiddleware(object):

                  #UA池

                  user_agent_list = [

                          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "

                          "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",

                          "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "

                          "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",

                          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "

                          "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",

                          "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "

                          "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",

                          "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "

                          "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",

                          "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "

                          "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",

                          "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "

                          "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",

                          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",

                          "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",

                          "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "

                          "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",

                          "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "

                          "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",

                          "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "

                          "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"

                  ]

                  #代理池

                  PROXY_http = [

                          '153.180.102.104:80',

                          '195.208.131.189:56055',

                  ]

                  PROXY_https = [

                          '120.83.49.90:9000',

                          '95.189.112.214:35508',

                  ]

 

                  #拦截正常请求:request就是该方法拦截到的请求,spider就是爬虫类实例化的一个对象

                  def process_request(self, request, spider):

                          print('this is process_request!!!')

                          #UA伪装

                          request.headers['User-Agent'] = random.choice(self.user_agent_list)

                          return None

 

                  #拦截所有的响应

                  def process_response(self, request, response, spider):

 

                          return response

 

                  #拦截发生异常的请求对象

                  def process_exception(self, request, exception, spider):

                          print('this is process_exception!!!!')

                          #代理ip的设定

                          if request.url.split(':')[0] == 'http':

                                   request.meta['proxy'] = random.choice(self.PROXY_http)

                          else:

                                   request.meta['proxy'] = random.choice(self.PROXY_https)

 

                          #将修正后的请求对象重新进行请求发送

                          return request
View Code

6.scrapy中使用selenium

         1.spider类中定义一个属性 (类属性)

         browser = Chrome(executable_path='chromedriver.exe')

         2.spider类中重写一个方法closed, 在该方法中关闭browser

         def closed(self,spider):
             self.browser.quit()

         3.在中间件类的process_response中编写selenium自动化的相关操作

                 class DownloaderMiddleware(object):

                          #拦截整个工程中所有的响应对象

                          def process_response(self, request, response, spider):

                                   if request.url in spider.urls:

                                            #获取了在爬虫类中定义好的浏览器对象

                                            bro = spider.bro

                                            bro.get(request.url)

 

                                            bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')

                                            sleep(1)

 

                                            #获取携带了新闻数据的页面源码数据

                                            page_text = bro.page_source

                                            #实例化一个新的响应对象

                                            new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)

                                            return new_response

                                   else:

                                            return response
View Code

7.CrawlSpider 自动爬取全站的数据

         - 作用:就是用于进行全站数据的爬取

         - 新建一个CrawlSpider类的爬虫文件       

         scrapy genspider -t crawl xxx www.xxx.com

         - LinkExtractor 连接提取器:根据指定规则(正则表达式)进行连接的提取

         - Rule规则解析器:

                  将链接提取器提取到的链接进行请求发送,然后对获取的页面数据进行指定规则的解析(callback)

         一个连接提取器对应唯一一个规则解析器

        

         - 示例 使用CrawlSpider爬取阳光问政王标题及详情页内容

                  # -*- coding: utf-8 -*-

                  import scrapy

                  from scrapy.linkextractors import LinkExtractor

                  from scrapy.spiders import CrawlSpider, Rule

 

                  from sunLinePro.items import SunlineproItem,ContentItem

                  class SunSpider(CrawlSpider):

                          name = 'sun'

                          # allowed_domains = ['www.xxx.com']

                          start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type=4&page=']

 

                          link= LinkExtractor(allow=r'type=4&page=\d+')#提取页码连接

 

                          link1 = LinkExtractor(allow=r'question/2019\d+/\d+\.shtml')#提取详情页的链接

                          rules = (

                                   Rule(link, callback='parse_item', follow=False),

                                   Rule(link1, callback='parse_detail'),

                          )

                          #解析出标题和网友名称

                          def parse_item(self, response):

                                   tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')

                                   for tr in tr_list:

                                            title = tr.xpath('./td[2]/a[2]/text()').extract_first()

                                            netFriend = tr.xpath('./td[4]/text()').extract_first()

                                            item = SunlineproItem()

                                            item['title'] = title

                                            item['netFriend'] = netFriend

 

                                            yield item

                          #解析出新闻的内容

                          def parse_detail(self,response):

                                   content = response.xpath('/html/body/div[9]/table[2]//tr[1]/td/div[2]//text()').extract()

                                   content = ''.join(content)

 

                                   item = ContentItem()

                                   item['content'] = content

 

                                   yield item
View Code

 

scrapy分布式爬虫

1.概念:

         可以将一组程序执行在多台机器上(分布式机群),使其进行数据的分布爬取。

2.原生的scrapy框架是否可以实现分布式?不可以.

         - 调度器不可以被分布式机群共享

         - 管道不可以被分布式机群共享

3.借助scrapy-redis这个模块帮助scrapy实现分布式

         - scrapy-redis作用:

         - 可以提供可以被共享的管道和调度器

        

4.使用scrapy-redis:

         1.安装scrapy-redis:

       pip install scrapy-redis

         2.爬虫文件中:

                  - spider对应的分布式类

           from scrapy_redis.spiders import RedisSpider 

                  - Crawlspider对应的分布式类

          from scrapy_redis.spiders import RedisCrawlSpider          

         3.修改爬虫文件的代码:

                  - 将当前爬虫类的父类从CrawlSpider修改成RedisCrawlSpider

                  - 将start_urls删除

                  - 添加一个新的类属性redis_key = 'ts' (可以被共享的调度器中的队列名称)

         4.设置管道piplines.py

    ITEM_PIPELINES = {
           'scrapy_redis_ep.pipelines.RedisPipeline': 400
           # 'scrapyRedisPro.pipelines.ScrapyredisproPipeline': 300,
       }

         5.settings.py中设置调度器

      # 使用scrapy-redis组件的去重队列,增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 是否暂停,配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True

         6.settings.py中设置redis服务器  

      REDIS_HOST = '192.168.12.154'
      REDIS_PORT = 6379
      # REDIS_ENCODING = 'utf-8'
      # REDIS_PARAMS = {'password':'123456'}

         7.配置redis:

                  修改Redis的配置文件:redis.windows.conf

        - #bind 127.0.0.1
        - protected-mode no        

         8.启动redis

                  携带配置文件启动redis服务                  

          redis-server ./redis.windows.conf       

                  启动redis客户端                     

        redis-cli         

                  执行工程

                           scrapy runspider xxx.py 

                  手动将起始url扔入调度器的队列中,在redis客户端中输入命令(redis-cli), xxx.com为起始url

                           lpush ts www.xxx.com 

                  查看爬取的所有items

                          lrange 爬虫文件名:items 0 -1 

增量式爬虫

增量式爬虫

         - 概念:监测网上数据更新的情况

                  比如爬取电影网时,过一段时间又会有新的电影产生,这是就不能再将所有数据存储到数据库中了因为会重复

         - 数据指纹

                  将爬取的数据进行hash等方式转换为一个不重复的值,存到数据库中.

                  比如爬取糗百时,将爬到的标题和段子内容原始数据第一步先持久化存储,第二步再将标题和内容拼接成一个字符串再进行hash生成一个data_id存入数据库, 在下一次爬取时就可以将标题和内容拼接生成hash值(data_id),在数据库中查询是否存在这个id值,进而判断数据是否存在                       

import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from redis import Redis
    from moviePro.items import MovieproItem
    class MovieSpider(CrawlSpider):
        name = 'movie'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.4567tv.tv/frim/index1.html']
        link = LinkExtractor(allow=r'/frim/index1-\d+.html')
        rules = (
            Rule(link, callback='parse_item', follow=False),
        )
        conn = Redis(host='127.0.0.1',port=6379)
        #解析电影的名称和详情页的url
        def parse_item(self, response):
            li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
            for li in li_list:
                title = li.xpath('./div/a/@title').extract_first()
                detail_url = 'https://www.4567tv.tv'+li.xpath('./div/a/@href').extract_first()
                item = MovieproItem()
                item['title'] = title

                #判断该详情页的url是否进行请求发送
                ex = self.conn.sadd('movie_detail_urls',detail_url)

                if ex == 1:#说明detail_url不存在于redis的set中
                    print('已有最新数据更新,请爬......')
                    yield scrapy.Request(url=detail_url,callback=self.parse_detail,meta={'item':item})
                else:
                    print('暂无新数据的更新!!!')

        def parse_detail(self,response):
            item = response.meta['item']
            desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
            item['desc'] = desc

            yield item
View Code

         管道文件:

class MovieproPipeline(object):
            def process_item(self, item, spider):
                dic = {
                    'title':item['title'],
                    'desc':item['desc']
                }
                conn = spider.conn

                conn.lpush('movie_data',dic)
                return item
View Code

 

        

 

posted @ 2019-06-05 21:45  robertx  阅读(1232)  评论(0编辑  收藏  举报