Scrapy模块和Asyncpy模块

Scrapy笔记

  • scrapy的环境安装

    • mac or linux: pip install scrapy

    • windows:

      pip install wheel

      scrapy框架异步请求基于Twisted,所以先要下载whl包安装 下载twisted url: https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 注意python版本

      twisted当前目录运行 pip install xxxxxxx.whl 这一步不能报错

      pip install pywin32

      pip install Scrapy

  • scrapy调试
    scrapy 如果懒得控制台运行,可以在根目录创建py文件

    from scrapy import cmdline
    cmdline.execute(["scrapy","crawl","crawla"])
    #分别填写终端的几个参数
    
    
  • scrapy基本使用

    • 创建一个工程 scrapy startproject projectname(比如命名工程文件名为myspider),此时工程文件会创建如下文件
    • 创建爬虫文件,进入到工程文件根目录后 用scrapy genspider <爬虫文件名> <起始爬取的url>

      我们来创建一个爬取百度网站的爬虫文件,取名叫handsome scrapy genspider handsome www.baidu.com

    • 运行爬虫工程文件, scrapy crawl <爬虫文件名> --nolog nolog主要为了不要输出日志记录,或者在配置文件中设置LOG_LEVEL = "ERROR",就只输出错误信息

    • 数据解析

      1. scrapy内置了xpath,对于数据解析直接respons.xpath("xpath语法"),返回值是一个Selector类型的对象包裹在列表中,如果要提取其中的文本数据要用extract()方法 respons.xpath("xpath语法")[0].extract()
      2. 上面是提取列表某个元素的data值,respons.xpath("xpath语法").extract()是提取列表中每个元素的data值
      3. 但是推荐使用extract_first()可以提取第一个元素的data文本,如果提取对象为空返回None
    • response对象的常用属性

      1. response.url
      2. response.request.url
      3. response.headers
      4. response.request.headers
      5. response.body
      6. response.status
  • scrapy的持久化存储

    • 基于终端指令存储:只可以将parse方法的返回值存储到本地文件中去,返回值必须是列表类型,有数据类型局限性

      scrapy crawl -o path 例如scrapy crawl handsome -o ./handome.csv

      基于终端指令存储只能限定于'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'等格式. csv如果中文乱码,

      csv的返回值,必须是列表包裹字典([{key:value}]),因为excel需要dataframe格式

      setting中配置FEED_EXPORT_ENCODING ="utf-8-sig"

          def parse(self, response):
              alldata = []
              index = 0
              empty_dict = {}
              article_lists = response.xpath(".//div[@class='col1 old-style-col1']//div[contains(@class,'article block untagged mb15')]")
              for article_list in article_lists:
                  index+=1
                  content = article_list.xpath(".//span/text()").extract()
                  content_clean = ["%s"%i.strip("\n, ") for i in content]
                  ret_data = "".join(content_clean)
                  empty_dict[index] = ret_data
                  alldata.append(empty_dict)
              return alldata
      
    • 基于管道存储

      • 数据解析(parse函数)

      • 在item类(items.py文件)中定义相关的属性

      • 将parse函数的解析数据封装到item类型的对象

      • 管道(piplines.py)中自定义父类的spider open和close方法

      • 管道类的process_item中将接收到的item对象进行持久化存储

      • 在配置文件中开启管道

        ITEM_PIPELINES = {
           'scrapy_test.pipelines.ScrapyTestPipeline': 300,
            #第一个参数是管道类的实例,第二个是权重,数字越小,优先越高,可以放多个管道实例
        }
        

        代码举例:

        """scrapy spider文件.py"""
        # 1 数据解析(parse函数)
            def parse(self, response):
                alldata = []
                index = 0
                empty_dict = {}
                article_lists = response.xpath(".//div[@class='col1 old-style-col1']//div[contains(@class,'article block untagged mb15')]")
                for article_list in article_lists:
                    index+=1
                    content = article_list.xpath(".//span/text()").extract()
                    content_clean = ["%s"%i.strip("\n, ") for i in content]
                    ret_data = "".join(content_clean)
                    # 3 将parse函数的解析数据封装到item类型的对象之创建实例 每次赋值都创建一个实例
                    item = ScrapyTestItem()  
                    # 3 将parse函数的解析数据写到item对象中去
                    item["item_ret"]=ret_data
                    yield item #将item提交给管道
         -------------------------------------------------------------------
           """item.py"""         
          # 2 在item类(items.py文件)中定义相关的属性
        class ScrapyTestItem(scrapy.Item):
            item_ret = scrapy.Field() #这个item_ret就是parse函数里item的key值
        
        ------------------------------------------------------------
        """piplines.py"""
        
      class ScrapyTestPipeline:
          fp = None
          # 4 管道(piplines.py)中自定义父类的spider open和close方法 这个方法在开始爬虫时候只会调用一次
          def open_spider(self, spider):
              print("开始爬虫")
              self.fp = open("./aaa.txt", "w", encoding="utf-8")
      
          # 这个方法在parse那每接收一个item就会被调用一次
          # 5 管道类的process_item中将接收到的item对象进行持久化存储
          def process_item(self, item, spider):
              fetch_data = item["item_ret"]
              self.fp.write(fetch_data + "\n" + "/" * 50 + "\n")
              return item
      
          # 4 管道(piplines.py)中自定义父类的spider open和close方法 这个方法在开始爬虫时候只会调用一次
          def close_spider(self, spider):
              print("爬虫结束")
              self.fp.close()
       # ---------------数据库的存储方法---------------------------       
         """piplines.py"""
          import pymysql
          class ScrapyMysqlPipeline:
          conn = None
          cursor = None
      
          # 4 管道(piplines.py)中自定义父类的spider open和close方法 这个方法在开始爬虫时候只会调用一次
          def open_spider(self, spider):
              print("开始爬取写入数据库")
              self.conn = pymysql.connect(user="root",password="192406",host="127.0.0.1",port=3306,db="test",charset="utf8")
      
          # 这个方法在parse那每接收一个item就会被调用一次
          # 5 管道类的process_item中将接收到的item对象进行持久化存储
          def process_item(self, item, spider):
              fetch_data = item["item_ret"]
              sql = 'insert into t1 (name) values ("%s");'%fetch_data
              self.cursor = self.conn.cursor()
              try:
                  self.cursor.execute(sql)
                  self.conn.commit()
              except Exception as e:
                  print(e)
                  self.conn.rollback()
      
              return item
      
          # 4 管道(piplines.py)中自定义父类的spider open和close方法 这个方法在开始爬虫时候只会调用一次
          def close_spider(self, spider):
              print("爬虫结束")
              self.cursor.close()
              self.conn.close()
      ----------------------------------------------------------------------
      """ 6 在配置文件中开启管道"""
      
      ITEM_PIPELINES = {
         'scrapy_test.pipelines.ScrapyTestPipeline': 300,
         'scrapy_test.pipelines.ScrapyMysqlPipeline': 301,
      }
      """
      这里定义了2个管道,一个是存储到文本的,一个是存储到Mysql的,因为ScrapyTestPipeline 权重比较小,所以会优先执行,先执行的管道需要在 def process_item(self, item, spider) 函数中进行return item,才能把这个数值传到下一个管道中去.所以平时就建议用return item进行闭包,同时会把这个item对象传给下一个管道进行存储
      """
      
  • 全站数据的爬取

    适用场景,我们爬取多个url,并且每个url有特定的组成规律,比如第一页url是https://www.qiushibaike.com/text/

    第二页是https://www.qiushibaike.com/text/page/2/

    第三页是https://www.qiushibaike.com/text/page/3/

    我们通常除了使用start_urls作为起始url外,会自己再构造一个url 看下面代码的5,17行
    这种情况需要用到scrapy.Request的callback参数.先看下scrapy中常见的有哪些参数

    scrapy.Request(url,[callback,method="GET",headers,body,cookies,meta,dont_filter=False])
    callback: 回调函数,即当前url响应交给哪个函数处理
    meta:实现数据在不同的解析函数中传递,默认有部分数据,比如下载延迟,请求深度(传递参数)等
    dont_filter: 过滤请求的url地址,请求过的地址不会被继续请求,如果需要重复请求,可以设置true
    ```
    
    class HandsomeSpider(scrapy.Spider):
        name = 'handsome'
        # allowed_domains = ['https://www.qiushibaike.com/text/']
        start_urls = ['https://www.qiushibaike.com/text/']
        url = "https://www.qiushibaike.com/text/page/%s/"
        num = 1
        def parse(self, response):
            titles = response.xpath(".//div[@class='col1 old-style-col1']//div[contains(@class,'article block untagged mb15')]")
            for i in titles:
                ret = i.xpath("./div[1]/a[2]/h2/text()").extract_first().strip()
                item = SpiderTestItem()
                item["fetch_ret"]=ret #把解析数据赋值给item对象
                yield item  #这个yield 等待把结果提交给管道
            print("第%s页打印完毕"%self.num)
            if self.num<=5:
                self.num+=1
                #new_url = response.urljoin(string)  response的urljoin方法可以实现url字符串拼接.
                new_url = self.url%self.num
                yield scrapy.Request(url=new_url,callback=self.parse)  #这个yield 是把新的net_url回调给parse函数再次进行数据爬取
    
  • 请求传参,第18行,就是利用meta参数进行回传参数给

    class HandsomeSpider(scrapy.Spider):
     name = 'handsome'
     # allowed_domains = ['https://www.qiushibaike.com/text/']
     start_urls = ['https://www.qiushibaike.com/text/'] # 1.起始url
     url = "https://www.qiushibaike.com/text/page/%s/" # 2.1 手动构造url
     num = 2 #2.2 构造url的页数
     def parse(self, response):
         # 3. 对起始url进行解析
         titles = response.xpath(
             ".//div[@class='col1 old-style-col1']//div[contains(@class,'article block untagged mb15')]")
         for i in titles:
             ret = i.xpath("./a[1]/@href").extract_first().strip()
             item = SpiderTestItem()
             url = "https://www.qiushibaike.com" + ret
             item["url"] = url # 3.1 将起始url中解析出来的25个a标签链接写入item对象,进行管道传输
          print("获取的url为%s" % url)
             # 3.2 利用meta把item对象传给parse_detail的函数
             yield scrapy.Request(url=url, callback=self.parse_detail, meta={"item": item})
    	# 4 全站爬取,解析完第一页数据后,继续用callback对自身进行回调,把构造的url和页数作为一个新的url进行请求
      if self.num<4:
             new_url = self.url%self.num
             self.num+=1
             yield scrapy.Request(url=new_url,callback=self.parse)
    
    
    def parse_detail(self,response):
         # 3.3 用response接收item对象
         item = response.meta["item"]
         ret = response.xpath(".//div[@class='side-user-info clearfix']/div[1]/div[1]/text()").extract_first()
         # 3.4 把解析到的每个详情页中作者的粉丝数写入item的fan_num值中
         item["fan_num"] = ret
         # 3.5 粉丝数提交到item
         yield item
         print("粉丝数为%s"%ret)
    
  • 图片数据爬取之ImagesPipeline

    • parse函数中提取图片的src,提交到管道对象item
    ```python
    class HandsomeSpider(scrapy.Spider):
        name = 'handsome'
        # allowed_domains = ['https://www.qiushibaike.com/text/']
        start_urls = ['https://www.qiushibaike.com/imgrank/']
        def parse(self, response):
            titles = response.xpath(".//div[contains(@class,'article block untagged mb15')]")
            for i in titles:
                ret = i.xpath("./div[@class='thumb']/a/img/@src").extract_first()
                item = SpiderTestItem()
                detail_url = "https:"+ret
                # 提取src,提交到管道item对象
                item["src"] = detail_url
                yield item
    ```
    
    • item对象设定src字段

      class SpiderTestItem(scrapy.Item):
          src = scrapy.Field()
      
    • piplines中自定义一个管道类,需要继承父类 ImagesPipeline,重写父类的三个方法,

      file_path:设置文件名和保存路径,存储路径的父路径在setting里设置 IMAGES_STORE ="./imgs"字段

      item_completed:自定义的Pipline 运行后继续把item传给下一个管道类,所以如果没有其他管道存储,也可以不写这个方法

      get_media_requests :将图片的src进行请求下载

      from scrapy.pipelines.images import ImagesPipeline
      import scrapy
      class Pipline(ImagesPipeline):
          #将图片的src进行请求下载,
          def get_media_requests(self, item, info):
              # print("item的src是",item["src"])
              #这里不需要回调函数,直接进行请求,meta可以把前面的参数给传下去,比如传自定义的文件名
              yield scrapy.Request(item["src"],meta=item)
          # 执行下一个即将执行的管道类,所以如果没有其他管道存储,也可以不写这个方法
          def item_completed(self, results, item, info):
              return item
      
          # 设定存储文件名,存储路径的父路径在setting里设置 IMAGES_STORE 字段
          def file_path(self, request, response=None, info=None, *, item=None):
              # return item["fname"]   直接把item里的文件名返回
      
              #这里是把url后面的xxx.jpg截取作为文件名返回保存
              f_name = request.url.split("/")[-1]
              return f_name
      
  • 中间件

    下载中间件:位于引擎和下载器之间,用来拦截工程中所有的请求和响应

    • 拦截请求
      1. UA伪装
      2. 代理IP
    • 拦截响应
      1. 篡改响应数据,响应对象
  • 五大核心组件的关系和流程

  • 下载中间件之请求中间件更改代码示例

    代码效果主要是爬取云代理的免费代理池前10页的ip,并提取其中的https类型ip,在进行百度查询ip地址前进行替换,然后看保存下来的html文件,显示被篡改后的请求ip,期间获取的ip池没有落地保存文件,都是放在实例的属性中

    爬虫代码示例

    from spider_test.items import SpiderTestItem
    
    class HandsomeSpider(scrapy.Spider):
     name = 'handsome'
     # allowed_domains = ['www.xxx.com']
     start_urls = ['http://www.ip3366.net/']
     url = "http://www.ip3366.net/?page=%s"
     ip_proxy = {"http": [], "https": []}
     num = 2
    
     def parse(self, response):
         ip_proxy_lists = response.xpath(".//table[@class='table table-bordered table-striped']/tbody/tr")
         for ip_infos in ip_proxy_lists:
             ip = ip_infos.xpath("./td[1]/text()").extract_first()
             port = ip_infos.xpath("./td[2]/text()").extract_first()
             type = ip_infos.xpath("./td[4]/text()").extract_first().lower()
             ip_port = "%s:%s" % (ip, port)
             self.ip_proxy[type].append(ip_port)
         # 1. 重复爬取前10页的ip地址,存放在ip_proxy,并且分成http和https2个字段
         if self.num < 10:
             follow_url = self.url % self.num
             self.num+=1
             yield scrapy.Request(url=follow_url, callback=self.parse)
         else:
             # 2. 爬取完毕后进行百度网址的访问
             new_url = "https://www.baidu.com/s?wd=ip"
             yield scrapy.Request(url=new_url, callback=self.baidu)
    
     def baidu(self, response):
         with open("./test.html", "w", encoding="utf-8")as f:
             f.write(response.text)
    

    中间件代码示例

    class SpiderTestDownloaderMiddleware:
    
     def process_request(self, request, spider):
         #对替换所有request的请求headers
         request.headers["User-Agent"]="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
         # 2.1 通过对request.url对请求进行过滤,但是单独对这个请求更换代理ip
         if request.url.startswith("https://www.baidu.com"):
             print("开始访问百度")
             call_type = request.url.split(":")[0]
             #直接调用spider的ip_proxy属性把前面抓获的ip池数据进行调用,每次取ip池里的第一个ip
             proxy_ip = spider.ip_proxy[call_type].pop(0)
             replaceip = "%s://%s" % (call_type, proxy_ip)
             print("替换的ip是", replaceip)
             # 2.2 替换请求前的数据
             request.meta["proxy"] = replaceip
         return None
    
     def process_exception(self, request, exception, spider):
         call_type = request.url.split(":")[0]
         proxy_ip = spider.ip_proxy[call_type].pop(0)
         replaceip = "%s://%s" % (call_type, proxy_ip)
         print("异常了,再次替换的ip是",replaceip)
         request.meta["proxy"] = replaceip
         return request
    
  • 下载中间件之响应中间件更改代码示例

    百度新闻里面的新闻也是ajax渲染的,我们通过url http://news.baidu.com/ 到新闻首页访问里面的国内,国际军事三个板块新闻.

    通过中间件一旦识别到url是这三个板块的url,就用selenium进行访问并在加载数据后把数据进行回传

    # 爬虫文件
    import scrapy
    from spider_test.items import SpiderTestItem
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    
    class HandsomeSpider(scrapy.Spider):
     name = 'handsome'
     # allowed_domains = ['www.xxx.com']
     start_urls = ['http://news.baidu.com']
     fetch_link = []
     ch_options = Options()
    
     def __init__(self):
         #设置selenium 无头
         self.ch_options.add_argument('--headless')
         self.ch_options.add_argument('blink-settings=imagesEnabled=false') #设置禁止加载图片,加快运行速度
         # 只会运行一次,创建实例
         self.bro = webdriver.Chrome(executable_path=r"D:\coding\chromedriver\chromedriver.exe",options=self.ch_options)
    
     def parse(self, response):
         # 1.提取新闻主页所有的栏目链接
         title_lists = response.xpath(".//div[@id='menu']/div[1]/div[1]/ul/li")
         # 2. 选择序号为2 3 4的新闻栏目,对应的分别是国内新闻  国际新闻 军事新闻
         page_list = [2,3,4]
         for i in page_list:
             # 3. 提取新闻栏目的url,并进行完整的url拼接
             url = title_lists[i].xpath("./a/@href").extract_first()
             complete_url = self.start_urls[0]+url
             self.fetch_link.append(complete_url)
         for i in self.fetch_link:
             # 4. 新闻板块url分别提交
             yield scrapy.Request(url=i,callback=self.baidu)
    
     def baidu(self, response):
         # 5. 获取返回内容,此时的返回内容已经是中间件拦截后,用selenuim访问url加载后的内容
         print("response.request.url>>>",response.request.url)
         ret = response.xpath(".//div[contains(@class,'column clearfix')][3]//div[contains(@class,'b-left')]/ul/li")
         for idx,i in enumerate(ret):
             content = i.xpath("./a/text()").extract_first()
             print(content)
    
     def close(self,spider, reason):
         # 只会运行一次,关闭实例
         self.bro.quit()
    
    # --------------中间件-----------------
    from scrapy.http import HtmlResponse
    
    class SpiderTestDownloaderMiddleware:
    
     def process_request(self, request, spider):
         request.headers["User-Agent"]="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
         return None
    
     def process_response(self, request, response, spider):
         if request.url in spider.fetch_link:
             bro = spider.bro
             bro.get(request.url)
             page = bro.page_source
             #4.1 需要创建一个HtmlResponse实例,把这个实例作为返回的对象
             # 4.2 url就是请求的url,body就是响应内容,这里设置的参数其实都是自定义的,在spider.py文件可以按属性调取
             new_response = HtmlResponse(url=request.url,body=page,encoding="utf-8",request=request)
             return new_response
         return response
    
    
  • CrawlSpider 是Spider的一个子类

    全站爬取方式可以用spider的手动请求yield,可以用CrawlSpider的自动模式

    • CrawlSpider使用:

      创建工程

      cd 目录

      创建爬虫文件(CrawlSpider):命令和之前不同,

      • scrapy genspider -t crawl xxxx www.xxx.com

      • 链接提取器 根据指定规则(allow)进行指定链接提取,并自动进行请求发送,不需要yield手动请求

        链接解析器中的follow参数如果为true,会在当前解析出来的链接继续访问其他的链接,进一步解析,然后丢到调度器中进行去重

      • 规则解析器 将连接提取器提取到的链接进行指定规则(callback)的解析操作

    • CrawlSpider代码示例

      # ---------------spider.py--------------------
      from scrapy.linkextractors import LinkExtractor
      from scrapy.spiders import CrawlSpider, Rule
      from crawl_test.items import CrawlTestItem,detail_item
      
      class CrawlaSpider(CrawlSpider):
          name = 'crawla'
          # allowed_domains = ['www.xxx.com']
          #1 从起始url进行访问,然后通过rules第一个链接提取器提取出符合规则的url进行异步访问,
          start_urls = ['https://nb8185.cnnb.com.cn/plus/list.php?tid=1401&TotalResult=8266&PageNo=1']
      
          rules = (
              # Rule 就是根据链接解析器实例创建一个规则解析器而已
              #2 因为设置的follow参数是false,所以不会对解析出来的链接继续访问,只访问起始url页面中解析出来的所有链接
              #规则解析器做2件事,提取符合规则的url,然后把url发给指定函数提取里面的信息
              Rule(LinkExtractor(allow=r'tid=\d+&TotalResult=\d+&PageNo=\d+'), callback='parse_item', follow=False),
              Rule(LinkExtractor(allow=r'html/292/1401/\d+/\d+/\d+.html'), callback='parse_detail', follow=False),
          )
      
          def parse_item(self, response):
              # 2.1根据页面分离器提取的页面url,对第1,2,3...页提取里面的url和发布日期
              ret_lists = response.xpath(".//div[@class='modItem1 clearfix']/dl")
              for i in ret_lists:
                  title = i.xpath("./dt/a/text()").extract_first().strip()
                  url =  "https://nb8185.cnnb.com.cn"+i.xpath("./dt/a/@href").extract_first().strip()
                  date =  i.xpath("./em/text()").extract_first().strip()
                  item = CrawlTestItem()
                  item["url"]=url
                  item["date"]=date
                  item["title"]=title
                  yield item
      
          def parse_detail(self,response):
              # 2.2 根据第二个规则解析器提取详情页里面的 相关部门和回复内容,标题
              title =response.xpath(".//div[@class='conMod bmhf clearfix' or @class = 'article_content']/h1/text()").extract_first().strip()
              department = response.xpath(".//dl[@id='replydiv1']/dt/text()").extract_first().strip()
              reply = response.xpath(".//dl[@id='replydiv1']/dd/div/text()").extract_first().strip()
              Detail_item = detail_item()
              Detail_item["title"] = title
              Detail_item["department"] = department
              Detail_item["reply"] = reply
              Detail_item["url"] = response.url
              yield Detail_item
      
          def close(self,spider, reason):
              print("spider 结束了")
      
      # ---------------item.py--------------------
      import scrapy
      
      class CrawlTestItem(scrapy.Item):
          # define the fields for your item here like:
          url = scrapy.Field()
          title = scrapy.Field()
          date = scrapy.Field()
      
      class detail_item(scrapy.Item):
          # define the fields for your item here like:
          url = scrapy.Field()
          title = scrapy.Field()
          department = scrapy.Field()
          reply = scrapy.Field()
      # ---------------pipelines.py--------------------
      from crawl_test.items import CrawlTestItem,detail_item
      class CrawlTestPipeline:
          aaa = {}
          bbb = {}
          def open_spider(self,spider):
              pass
      
          def process_item(self, item, spider):
              if item.__class__.__name__ =="CrawlTestItem":
                  self.aaa.update({item["url"]:[item["date"],item["title"]]})
              else:
                  self.bbb.update({item["url"]:[item["department"],item["title"],item["reply"]]})
              return item
      
          def close_spider(self,spider):
              print("关闭爬虫管道CrawlTestPipeline")
              #把aaa的数据整合到bbb的列表中去
              for k,v in self.aaa.items():
                  if self.bbb.get(k):
                      self.bbb[k].extend(v)
              print(self.bbb)
      
  • 分布式爬虫

  • 概念: 搭建一个分布式集群,对一组资源进行多台设备分布联合爬取
  • 分布式实现步骤
  • 原理:原生scrapy不能实现分布式爬虫,因为调度器和管道无法被分布式集群共享,因此需要要借助其他组件实现,这里以scrapy-redis组件为案例

    scrapy-redis 可以给原生scrapy框架提供可以被共享的管道和调度器

  • 实现步骤 基于crawlspider类

    1. 创建工程scrapy startporject fbs_pro && scrapy genspider -t crawl fbs www.xxx.com

    2. 修改爬虫文件

      2.1 导包 from scrapy_redis.spiders import RedisCrawlSpider,把start_urls和allowed_domains注释

      2.2 自定义一个redis_key(被共享的调度器队列名称,后期用来存放起始url) redis_key = "sun"

      2.3 将当前爬虫类的父类改为RedisCrawlSpider

    3. 修改配置文件setting.py

      3.1 修改指定可以被共享的管道'scrapy_redis.pipelines.RedisPipeline': 300,

      3.2 修改指定的调度器

      # 设置默认使用scrapy_redis的过滤器
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
      # 设置默认使用scrapy_redis的调度器
      SCHEDULER = "scrapy_redis.scheduler.Scheduler"
      
      # 可选的调度器队列   默认为第一个
      #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
      #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
      #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
      
      # 是否保持调度器队列,断点续爬
      SCHEDULER_PERSIST = True
      
      #连接redis数据库
      REDIS_HOST = '192.168.13.20' #主机名
      REDIS_PORT = 6379 #端口号
      REDIS_PARAMS = {
        # ‘password’: ‘123’,
        # }
      
      # redis 编码类型默认:'utf-8'
      REDIS_ENCODING = "utf-8"
      
    4. redis配置文件修改

      4.1 修改bind 127.0.0.1参数,禁用本地访问

      4.2 关闭保护模式 protected-mode 改为no

      ​ 保护模式作用是在有一个客户端访问的前提下,禁止其他客户端进行访问.

      4.3 启动redis服务端 ./redis-server ../redis.conf redis-server 配置文件路径

      4.4 启动redis客户端 ./redis-cli

    5. 执行工程文件 要进入到spider.py的文件夹 爬虫文件会开始监听

      scrapy runspider xxx.py

    6. 向redis客户端的调度器的队列中放入一个起始的url

      新开终端后完整的命令为 redis-cli -h redis主机id lpush redis_key名字 起始的url

      这里因为4.4步骤已经打开了客户端所以直接填后面的-h redis主机id lpush redis_key名字 起始的url即可

    7. 如果要重新运行爬虫记得把master上的redis清空,因为master里的数据库“dmoz:dupefilter”是用来过滤重复的请求

  • 增量式爬虫 检测网站更新情况,只会爬取更新出来的资源

    实现流程:

    • 指定一个起始url,基于crawlspider获取其他页码链接
    • 基于rule对其他页码链接进行请求
    • 从每一个页码对应的页面源码中解析出一个电影详情url
    • 对详情url发请求,解析电影的名称和简介
    • 进行持久化存储

Asyncpy模块

参照scrapy的流程做的异步scrapy

视频介绍:https://www.bilibili.com/video/BV1dK411J7Ja/?vd_source=52e346b31d1a88b94ab95ca6e15eddd9
github:https://github.com/lixi5338619/asyncpy

posted @ 2021-12-09 16:42  零哭谷  阅读(138)  评论(0编辑  收藏  举报