• 第十三篇 Scrapy框架


    Scrapy

    - 什么是框架?
    - 就是一个集成了很多功能并且具有很强通用性的一个项目模板。
    - 如何学习框架?
    - 专门学习框架封装的各种功能的详细用法。
    - 什么是scrapy?
    - 爬虫中封装好的一个明星框架。功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式


    -qiubaiPro.demo
    from scrapy import cmdline
    
    cmdline.execute("scrapy crawl cnblog_spider --nolog".split())       #--nolog是以不显示日志的形式运行,如果需要看详细信息,可以去掉
    main.py启动文件
    # -*- coding: utf-8 -*-
    import scrapy
    from qiubaiPro.items import QiubaiproItem
    
    class QiubaiSpider(scrapy.Spider):
        name = 'qiubai'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.qiushibaike.com/text/']
    
        # def parse(self, response):
        #     #解析:作者的名称+段子内容
        #     div_list = response.xpath('//div[@id="content-left"]/div')
        #     all_data = [] #存储所有解析到的数据
        #     for div in div_list:
        #         #xpath返回的是列表,但是列表元素一定是Selector类型的对象
        #         #extract可以将Selector对象中data参数存储的字符串提取出来
        #         # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
        #         author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
        #         #列表调用了extract之后,则表示将列表中每一个Selector对象中data对应的字符串提取了出来
        #         content = div.xpath('./a[1]/div/span//text()').extract()
        #         content = ''.join(content)
        #
        #         dic = {
        #             'author':author,
        #             'content':content
        #         }
        #
        #         all_data.append(dic)
        #
        #
        #     return all_data (基于终端指令保存文件)
    
        def parse(self, response):
            #解析:作者的名称+段子内容
            div_list = response.xpath('//div[@class="col1 old-style-col1"]/div')
            for div in div_list:
                #xpath返回的是列表,但是列表元素一定是Selector类型的对象
                #extract可以将Selector对象中data参数存储的字符串提取出来
                # author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()
                author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span/h2/text()').extract_first()
                #列表调用了extract之后,则表示将列表中每一个Selector对象中data对应的字符串提取了出来
                content = div.xpath('./a[1]/div/span//text()').extract()
                content = ''.join(content)
    
                item = QiubaiproItem()
                item['author'] = author
                item['content'] = content
    
                yield item#将item提交给了管道
    spiders/qiubai.py
    # -*- coding: utf-8 -*-
    
    # Define here the models for your scraped items
    #
    # See documentation in:
    # https://doc.scrapy.org/en/latest/topics/items.html
    
    import scrapy
    
    
    class QiubaiproItem(scrapy.Item):
        # define the fields for your item here like:
        author = scrapy.Field()
        content = scrapy.Field()
        # pass
    items.py
    # -*- coding: utf-8 -*-
    
    # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
    import pymysql
    
    class QiubaiproPipeline(object):
        fp = None
        #重写父类的一个方法:该方法只在开始爬虫的时候被调用一次
        def open_spider(self,spider):
            print('开始爬虫......')
            self.fp = open('./qiubai.txt','w',encoding='utf-8')
    
    
        #专门用来处理item类型对象
        #该方法可以接收爬虫文件提交过来的item对象
        #该方法没接收到一个item就会被调用一次
        def process_item(self, item, spider):
            author = item['author']
            content= item['content']
    
            self.fp.write(author+':'+content+'
    ')
    
            return item #就会传递给下一个即将被执行的管道类
    
        def close_spider(self,spider):
            print('结束爬虫!')
            self.fp.close()
    
    #管道文件中一个管道类对应将一组数据存储到一个平台或者载体中
    class mysqlPileLine(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='qiubai',charset='utf8')
        def process_item(self,item,spider):
            self.cursor = self.conn.cursor()
    
            try:
                self.cursor.execute('insert into qiubai values("%s","%s")'%(item["author"],item["content"]))
                self.conn.commit()
            except Exception as e:
                print(e)
                self.conn.rollback()
    
            return item
        def close_spider(self,spider):
            self.cursor.close()
            self.conn.close()
    
    
    #爬虫文件提交的item类型的对象最终会提交给哪一个管道类?
        #先执行的管道类
    pipelines
    # 设置UA
    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    
    LOG_LEVEL = 'ERROR'
    
    # 开启管道
    ITEM_PIPELINES = {
       'qiubaiPro.pipelines.QiubaiproPipeline': 300,
       # 'qiubaiPro.pipelines.mysqlPileLine': 301,
        #300表示的是优先级,数值越小优先级越高
    }
    settings.py
    - scrapy框架相关命令
    - 环境的安装:
            - mac or linux:pip install scrapy
            - windows:
                - pip install wheel
                - 下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
                - 安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
                - pip install pywin32
    
                - pip install scrapy  (一般可以直接安装,不行的话在执行以上操作)
                测试:在终端里录入scrapy指令,没有报错即表示安装成功!
        - 创建一个工程:scrapy startproject xxxPro
        - cd xxxPro
        - 在spiders子目录中创建一个爬虫文件
            - scrapy genspider spiderName www.xxx.com
        - 执行工程:
            - scrapy crawl spiderName

     - 解析-存储-管道-回调

    - scrapy数据解析
    
    - scrapy持久化存储(示例:qiubaiPro)
        - 基于终端指令:
            - 要求:只可以将parse方法的返回值存储到本地的文本文件中
            - 注意:持久化存储对应的文本文件的类型只可以为:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle
            - 指令:scrapy crawl xxx -o filePath
            - 好处:简介高效便捷
            - 缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
    
        - 基于管道:
            - 编码流程:
                - 数据解析
                - 在item类中定义相关的属性
                - 将解析的数据封装存储到item类型的对象
                - 将item类型的对象提交给管道进行持久化存储的操作
                - 在管道类的process_item中要将其接受到的item对象中存储的数据进行持久化存储操作
                - 在配置文件中开启管道
            - 好处:
                - 通用性强。
    
        - 问题:将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
            - 管道文件中一个管道类对应的是将数据存储到一种平台
            - 爬虫文件提交的item只会给管道文件中第一个被执行的管道类接受
            - process_item中的return item表示将item传递给下一个即将被执行的管道类
    
    - 回调函数的使用:
    - 基于Spider的全站数据爬取(示例:xiaohuaPro)
        - 就是将网站中某板块下的全部页码对应的页面数据进行爬取
        - 需求:爬取校花网中的照片的名称
        - 实现方式:
            - 将所有页面的url添加到start_urls列表(不推荐)
            - 自行手动进行请求发送(推荐)
                - 手动请求发送:
                    - yield scrapy.Request(url,callback):callback专门用做于数据解析

    - 五大核心组件
        引擎(Scrapy)
    用来处理整个系统的数据流处理, 触发事务(框架核心)
    调度器(Scheduler)
    用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
    下载器(Downloader)
    用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
    爬虫(Spiders)
    爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
    项目管道(Pipeline)
    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。

     - 自定制管道类(图片存储)

    --使用scrapy爬取图片
    - 需求:爬取站长素材中的高清图片 http://sc.chinaz.com/tupian/

    - 使用流程:
    - 数据解析(图片的地址)
    - 将存储图片地址的item提交到制定的管道类
    - 在管道文件中自定制一个基于ImagesPipeLine的一个管道类
    - get_media_request 发送请求
    - file_path 获取文件名
    - item_completed 将item传给下一个指定执行的管道类
    - 在配置文件中:
    - 指定图片存储的目录:IMAGES_STORE = './imgs_bobo'
    - 指定开启的管道:自定制的管道类
    # -*- coding: utf-8 -*-
    import scrapy
    from imgsPro.items import ImgsproItem
    
    class ImgSpider(scrapy.Spider):
        name = 'img'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['http://sc.chinaz.com/tupian/']
    
        def parse(self, response):
            div_list = response.xpath('//div[@id="container"]/div')
            for div in div_list:
                #注意:使用伪属性
                src = div.xpath('./div/a/img/@src2').extract_first()
                # print(src)
                item = ImgsproItem()
                item['src'] ='https:'+ src
    
                yield item
    spiders/img.py
    import scrapy
    
    class ImgsproItem(scrapy.Item):
        # define the fields for your item here like:
        src = scrapy.Field()
        # pass
    items.py
    from scrapy.pipelines.images import ImagesPipeline
    import scrapy
    class imgsPileLine(ImagesPipeline):
    
        #就是可以根据图片地址进行图片数据的请求
        def get_media_requests(self, item, info):
    
            yield scrapy.Request(item['src'])
    
        #指定图片存储的路径
        def file_path(self, request, response=None, info=None):
            imgName = request.url.split('/')[-1]
            return imgName
    
        def item_completed(self, results, item, info):
            return item #返回给下一个即将被执行的管道类
    pipelines.py
    LOG_LEVEL = 'ERROR'
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' #伪装请求载体身份
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    # 开启管道
    TEM_PIPELINES = {
       'imgsPro.pipelines.imgsPileLine': 300,
    }
    #指定图片存储的目录
    IMAGES_STORE = './imgs_hua'
    settings.py

    - meta传参,cookie设置

    - 请求传参
       - 使用场景:如果爬取解析的数据不在同一张页面中。(深度爬取)
        - 需求:爬取boss的岗位名称,岗位描述
    
         detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/div[1]/div[1]//div[1]/span[1]/a/@href').extract_first()
         #对详情页发请求获取详情页的页面源码数据
         #手动请求的发送
         #请求传参:meta={},可以将meta字典传递给请求对应的回调函数
         yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
    - 设置cookie def start_requests(self): # 该cookie是登陆后获取的cookie字符串。 cookies
    ="lastCity=101280600; __zp_seo_uuid__=6e0b7a8a-bddd-4649-8665-3f0499d721d3; __g=-; __l=r=https%3A%2F%2Fwww.sogou.com%2Flink%3Furl%3DhedJjaC291Oa2YscKbYXZDtGMBTLg3lG&l=%2Fwww.zhipin.com%2Fshenzhen%2F&s=1&g=&s=3&friend_source=0; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1610982279; __c=1610982279; __a=47655612.1610982279..1610982279.2.1.2.2; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1610982335; __zp_stoken__=ce26bZzVnTEARJFNSDGwQIXM0Lh9YBHxkBlhjMWIKSHN4VGR5ZVEgD1BSQjtlSn1lHh8PIE4CIHFPaz0abRR2UX4SQFxbagYKaDYZfDNjOFskSn9HCVkWYXYyOxpNZTQTGE4FGX99IHdsQHV5YQ%3D%3D" # 转换成字典 cookies = {i.split("=")[0]: i.split("=")[1] for i in cookies.split("; ")} # headers = {"Cookie":cookies} # cookie放到headers中无效 yield scrapy.Request( self.start_urls[0], callback=self.parse, cookies=cookies # 携带cookie进行请求 # headers = headers # cookie放到headers中无效 ) -- seetting COOKIES_ENABLED = True
    import scrapy
    from bossPro.items import BossproItem
    
    class BossSpider(scrapy.Spider):
        name = 'boss'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.zhipin.com/job_detail/?query=python&city=101010100&industry=&position=']
    
        url = 'https://www.zhipin.com/c101010100/?query=python&page=%d'
        page_num = 2
    
        def start_requests(self):
            # 该cookie是登陆后获取的cookie字符串。
            cookies ="lastCity=101280600; __zp_seo_uuid__=6e0b7a8a-bddd-4649-8665-3f0499d721d3; __g=-; __l=r=https%3A%2F%2Fwww.sogou.com%2Flink%3Furl%3DhedJjaC291Oa2YscKbYXZDtGMBTLg3lG&l=%2Fwww.zhipin.com%2Fshenzhen%2F&s=1&g=&s=3&friend_source=0; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1610982279; __c=1610982279; __a=47655612.1610982279..1610982279.2.1.2.2; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1610982335; __zp_stoken__=ce26bZzVnTEARJFNSDGwQIXM0Lh9YBHxkBlhjMWIKSHN4VGR5ZVEgD1BSQjtlSn1lHh8PIE4CIHFPaz0abRR2UX4SQFxbagYKaDYZfDNjOFskSn9HCVkWYXYyOxpNZTQTGE4FGX99IHdsQHV5YQ%3D%3D"
            # 转换成字典
            cookies = {i.split("=")[0]: i.split("=")[1] for i in cookies.split("; ")}
            # headers = {"Cookie":cookies}  # cookie放到headers中无效
            yield scrapy.Request(
                self.start_urls[0],
                callback=self.parse,
                cookies=cookies  # 携带cookie进行请求
                # headers = headers  # cookie放到headers中无效
            )
    
        #回调函数接受item
        def parse_detail(self,response):
            item = response.meta['item']
    
            job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div/text()').extract()
            job_desc = ''.join(job_desc)
            print(job_desc)
            item['job_desc'] = job_desc
    
            yield item
    
        #解析首页中的岗位名称
        def parse(self, response):
            li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
            for li in li_list:
                item = BossproItem()
    
                job_name = li.xpath('.//div[@class="info-primary"]/div[1]/div[1]//div[1]/span[1]/a/text()').extract_first()
                item['job_name'] = job_name
                print(job_name)
                detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/div[1]/div[1]//div[1]/span[1]/a/@href').extract_first()
                #对详情页发请求获取详情页的页面源码数据
                #手动请求的发送
                #请求传参:meta={},可以将meta字典传递给请求对应的回调函数
                yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
    
            #分页操作
            if self.page_num <= 3:
                new_url = format(self.url%self.page_num)
                self.page_num += 1
    
                yield scrapy.Request(new_url,callback=self.parse)
    spiders/boss.py
    import scrapy
    
    
    class BossproItem(scrapy.Item):
        # define the fields for your item here like:
        job_name = scrapy.Field()
        job_desc = scrapy.Field()
        # pass
    items.py
    class BossproPipeline(object):
        fp = None
        #重写父类的一个方法:该方法只在开始爬虫的时候被调用一次
        def open_spider(self,spider):
            print('开始爬虫......')
            self.fp = open('./boss.txt','w',encoding='utf-8')
    
        def process_item(self, item, spider):
            job_name = item['job_name']
            job_desc= item['job_desc']
            self.fp.write(job_name+':'+job_desc+'
    ')
            return item #就会传递给下一个即将被执行的管道类
        def close_spider(self,spider):
            print('结束爬虫!')
            self.fp.close()
    pipelines.py
    BOT_NAME = 'bossPro'
    LOG_LEVEL = 'ERROR'
    SPIDER_MODULES = ['bossPro.spiders']
    NEWSPIDER_MODULE = 'bossPro.spiders'
    
    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
    
    ROBOTSTXT_OBEY = False
    
    COOKIES_ENABLED = True
    
    ITEM_PIPELINES = {
       'bossPro.pipelines.BossproPipeline': 300,
    }
    settings.py

    - 中间件的使用

    - 需求:爬取网易新闻中的新闻数据(标题和内容)
        - 1.通过网易新闻的首页解析出五大板块对应的详情页的url
        - 2.每一个板块对应的新闻标题都是动态加载出来的(动态加载)
        - 3.通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻内容
    
    知识点:  1 中间件的使用 ,拦截请求,拦截响应数据
            2 在scrapy中使用selenium,获取动态加载的数据
    
    
    - 中间件(示例:middlePro)
        - 下载中间件
            - 位置:引擎和下载器之间
            - 作用:批量拦截到整个工程中所有的请求和响应
            - 拦截请求:
                - UA伪装:process_request
                - 代理IP:process_exception:return request
    
            - 拦截响应:
                - 篡改响应数据,响应对象

      -开启中间件(settings.py)
       DOWNLOADER_MIDDLEWARES = {
         'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
          }
    import scrapy
    from selenium import webdriver
    from wangyiPro.items import WangyiproItem
    class WangyiSpider(scrapy.Spider):
        name = 'wangyi'
        # allowed_domains = ['www.cccom']
        start_urls = ['https://news.163.com/']
        models_urls = []  #存储五个板块对应详情页的url
    
        def __init__(self):
            """ #实例化一个浏览器对象"""
            self.bro = webdriver.Chrome(executable_path=r'F:python爬虫7 scrapy框架chromedriver.exe')
    
        def parse(self, response):
            li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
            alist = [3,4]
            for index in alist:
                model_url = li_list[index].xpath('./a/@href').extract_first()
                self.models_urls.append(model_url)
    
            for url in self.models_urls:
                """#对每一个板块的url进行请求发送"""
                yield scrapy.Request(url,callback=self.parse_model)
    
    
        def parse_model(self,response):
            """
             #每一个板块对应的新闻标题相关的内容都是动态加载
             #解析每一个板块页面中对应新闻的标题和新闻详情页的url
             """
            div_list = response.xpath('/html/body/div/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
            for div in div_list:
                title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
                new_detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
                item = WangyiproItem()
                item['title'] = title
                #对新闻详情页的url发起请求
                yield scrapy.Request(url=new_detail_url,callback=self.parse_detail,meta={'item':item})
        def parse_detail(self,response):#解析新闻内容
            content = response.xpath('//*[@id="content"]/div[2]//text()').extract()
            # print(content)
            content = ''.join(content)
            item = response.meta['item']
            item['content'] = content
    
    
            yield item
    
        def closed(self,spider):
            self.bro.quit()
    spiders/wangyi.py
    import scrapy
    
    
    class WangyiproItem(scrapy.Item):
        # define the fields for your item here like:
        title = scrapy.Field()
        content = scrapy.Field()
    items.py
    # -*- coding: utf-8 -*-
    
    # Define here the models for your spider middleware
    #
    # See documentation in:
    # https://doc.scrapy.org/en/latest/topics/spider-middleware.html
    
    from scrapy import signals
    
    import random
    from scrapy.http import HtmlResponse
    from time import sleep
    
    class WangyiproDownloaderMiddleware(object):
        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',
        ]
    
        # def process_request(self, request, spider):
        #     # UA伪装
        #     request.headers['User-Agent'] = random.choice(self.user_agent_list)
        #     return None
    
        def process_response(self, request, response, spider):#spider爬虫对象
            bro = spider.bro
            if request.url in spider.models_urls:
                bro.get(request.url)
                sleep(3)
                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 #response #其他请求对应的响应对象
    
        # def process_exception(self, request, exception, spider):
        #     if request.url.split(':')[0] == 'http':
        #         # 代理
        #         request.meta['proxy'] = 'http://' + random.choice(self.PROXY_http)
        #     else:
        #         request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)
        #
        #     return request  # 将修正之后的请求对象进行重新的请求发送
    middlewares.py
    class WangyiproPipeline(object):
        fp = None
        def open_spider(self, spider):
            print('开始爬虫......')
            self.fp = open('./wangyi.txt', 'w', encoding='utf-8')
    
        def process_item(self, item, spider):
            title = item['title']
            content = item['content']
            self.fp.write(title + '
    ' + content + '
    ')
            return item  # 就会传递给下一个即将被执行的管道类
    
        def close_spider(self, spider):
            print('结束爬虫!')
            self.fp.close()
    pipelines.py
    BOT_NAME = 'wangyiPro'
    
    SPIDER_MODULES = ['wangyiPro.spiders']
    NEWSPIDER_MODULE = 'wangyiPro.spiders'
    
    
    # Crawl responsibly by identifying yourself (and your website) on the user-agent
    #USER_AGENT = 'wangyiPro (+http://www.yourdomain.com)'
    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    # 开启中间件
    DOWNLOADER_MIDDLEWARES = {
       'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
    }
    
    # Enable or disable extensions
    # See https://doc.scrapy.org/en/latest/topics/extensions.html
    #EXTENSIONS = {
    #    'scrapy.extensions.telnet.TelnetConsole': None,
    #}
    
    # Configure item pipelines
    # See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
    ITEM_PIPELINES = {
       'wangyiPro.pipelines.WangyiproPipeline': 300,
    }
    LOG_LEVEL = 'ERROR'
    settings.py
    - CrawlSpider:类,Spider的一个子类
    #需求:爬取sun网站中的编号,新闻标题,新闻内容,标号
    - 分析:爬取的数据没有在同一张页面中。
    - 1.可以使用链接提取器提取所有的页码链接
    - 2.让链接提取器提取所有的新闻详情页的链接
    - CrawlSpider:类,Spider的一个子类(用于全站数据爬取)
    - 全站数据爬取的方式(对应板块所有页码数据的爬取)
    - 基于Spider:手动请求
    - 基于CrawlSpider
    - CrawlSpider的使用:
    - 创建一个工程
    - cd XXX
    - 创建爬虫文件(CrawlSpider):
    - scrapy genspider -t crawl xxx www.xxxx.com
    - 链接提取器:
    - 作用:根据指定的规则(allow)进行指定链接的提取
    - 规则解析器:
    - 作用:将链接提取器提取到的链接进行指定规则(callback)的解析

    知识点:
    -xpath表达式中不可以出现tbody标签
    tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
    -pipelines中如何区分不同的item
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from sunPro.items import SunproItem,DetailItem
    
    #需求:爬取sun网站中的编号,新闻标题,新闻内容,标号
    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+')
        link_detail = LinkExtractor(allow=r'question/d+/d+.shtml')
        rules = (
            Rule(link, callback='parse_item', follow=True),
            Rule(link_detail,callback='parse_detail')
        )
    
    
        def parse_item(self, response):
            """#解析新闻编号和新闻的标题
                #如下两个解析方法中是不可以实现请求传参!
                #如法将两个解析方法解析的数据存储到同一个item中,可以以此存储到两个item
            """
            #注意:xpath表达式中不可以出现tbody标签
            tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
            for tr in tr_list:
                new_num = tr.xpath('./td[1]/text()').extract_first()
                new_title = tr.xpath('./td[2]/a[2]/@title').extract_first()
                item = SunproItem()
                item['title'] = new_title
                item['new_num'] = new_num
    
                yield item
    
        #解析新闻内容和新闻编号
        def parse_detail(self,response):
            new_id = response.xpath('/html/body/div[9]/table[1]//tr/td[2]/span[2]/text()').extract_first()
            new_content = response.xpath('/html/body/div[9]/table[2]//tr[1]//text()').extract()
            new_content = ''.join(new_content)
    
            # print(new_id,new_content)
            item = DetailItem()
            item['content'] = new_content
            item['new_id'] = new_id
    
            yield item
    spiders/sun.py
    #需求:爬取sun网站中的编号,新闻标题,新闻内容,标号
            - 分析:爬取的数据没有在同一张页面中。
            - 1.可以使用链接提取器提取所有的页码链接
            - 2.让链接提取器提取所有的新闻详情页的链接
    
    
    - CrawlSpider:类,Spider的一个子类(用于全站数据爬取)
        - 全站数据爬取的方式(对应板块所有页码数据的爬取)
            - 基于Spider:手动请求
            - 基于CrawlSpider
        - CrawlSpider的使用:
            - 创建一个工程
            - cd XXX
            - 创建爬虫文件(CrawlSpider):
                - scrapy genspider -t crawl xxx www.xxxx.com
                - 链接提取器:
                    - 作用:根据指定的规则(allow)进行指定链接的提取
                - 规则解析器:
                    - 作用:将链接提取器提取到的链接进行指定规则(callback)的解析
    
    知识点:
     #注意:xpath表达式中不可以出现tbody标签
            tr_list = response.xpath('//*[@id="morelist"]/div/table[2]//tr/td/table//tr')
    items.py
    lass SunproPipeline(object):
        def process_item(self, item, spider):
            """ #如何判定item的类型
                #将数据写入数据库时,如何保证数据的一致性
            """
            if item.__class__.__name__ == 'DetailItem':
                print(item['new_id'],item['content'])
            else:
                print(item['new_num'],item['title'])
            return item
    pipelines.py

    - 分布式爬虫
    - 分布式爬虫
        - 概念:我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取。
        - 作用:提升爬取数据的效率
    
        - 如何实现分布式?
            - 安装一个scrapy-redis的组件
            - 原生的scarapy是不可以实现分布式爬虫,必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫。
            - 为什么原生的scrapy不可以实现分布式?
                - 调度器不可以被分布式机群共享
                - 管道不可以被分布式机群共享
            - scrapy-redis组件作用:
                - 可以给原生的scrapy框架提供可以被共享的管道和调度器
            - 实现流程 (示例:fbsPro)
                - 创建一个工程
                - 创建一个基于CrawlSpider的爬虫文件
                - 修改当前的爬虫文件:
                    - 导包:from scrapy_redis.spiders import RedisCrawlSpider
                    - 将start_urls和allowed_domains进行注释
                    - 添加一个新属性:redis_key = 'sun' 可以被共享的调度器队列的名称
                    - 编写数据解析相关的操作
                    - 将当前爬虫类的父类修改成RedisCrawlSpider
                - 修改配置文件settings
                    - 指定使用可以被共享的管道:
                        ITEM_PIPELINES = {
                            'scrapy_redis.pipelines.RedisPipeline': 400
                        }
                    - 指定调度器:
                        # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
                        DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
                        # 使用scrapy-redis组件自己的调度器
                        SCHEDULER = "scrapy_redis.scheduler.Scheduler"
                        # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
                        SCHEDULER_PERSIST = True
                    - 指定redis服务器:
                        setting:
                        #指定redis
                        REDIS_HOST = '127.0.0.1' #redis远程服务器的ip(修改)
                        REDIS_PORT = 6379
    
                - redis相关操作配置:
                    - 配置redis的配置文件:
                        - linux或者mac:redis.conf
                        - windows:redis.windows.conf
                        - 代开配置文件修改:
                            - 将bind 127.0.0.1进行删除
                            - 关闭保护模式:protected-mode yes改为no
                    - 结合着配置文件开启redis服务
                        - redis-server 配置文件
                    - 启动客户端:
                        - redis-cli
                - 执行工程:
                    - scrapy runspider xxx.py
                - 向调度器的队列中放入一个起始的url:
                    - 调度器的队列在redis的客户端中
                        - lpush xxx www.xxx.com
                - 爬取到的数据存储在了redis的proName:items这个数据结构中

    -增量式爬虫

    增量式爬虫
        - 概念:监测网站数据更新的情况,只会爬取网站最新更新出来的数据。
        - 分析:
            - 指定一个起始url
            - 基于CrawlSpider获取其他页码链接
            - 基于Rule将其他页码链接进行请求
            - 从每一个页码对应的页面源码中解析出每一个电影详情页的URL
    
            - 核心:检测电影详情页的url之前有没有请求过
                - 将爬取过的电影详情页的url存储
                    - 存储到redis的set数据结构
    
            - 对详情页的url发起请求,然后解析出电影的名称和简介
            - 进行持久化存储
    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.ccc.com']
        start_urls = ['https://www.4567tv.tv/frim/index1.html']
    
        rules = (
            Rule(LinkExtractor(allow=r'/frim/index1-d+.html'), callback='parse_item', follow=True),
        )
        # 创建redis链接对象
        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:
                # 获取详情页的url
                detail_url = 'https://www.4567tv.tv' + li.xpath('./div/a/@href').extract_first()
    
                # 将详情页的url存入redis的set中
                ex = self.conn.sadd('urls', detail_url)
                if ex == 1:
                    print('该url没有被爬取过,可以进行数据的爬取')
                    yield scrapy.Request(url=detail_url, callback=self.parst_detail)
                else:
                    print('数据还没有更新,暂无新数据可爬取!')
    
        # 解析详情页中的电影名称和类型,进行持久化存储
        def parst_detail(self, response):
            item = MovieproItem()
            item['name'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/h1/text()').extract_first()
            item['desc'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]//text()').extract()
            item['desc'] = ''.join(item['desc'])
            yield item
    spiders/movie.py
    import scrapy
    
    
    class MovieproItem(scrapy.Item):
        # define the fields for your item here like:
        name = scrapy.Field()
        desc = scrapy.Field()
        # pass
    items.py
    from redis import Redis
    class MovieproPipeline(object):
        conn = None
        def open_spider(self,spider):
            self.conn = spider.conn
        def process_item(self, item, spider):
            dic = {
                'name':item['name'],
                'desc':item['desc']
            }
            # print(dic)
            self.conn.lpush('movieData',dic)
            return item
    pipelines.py

    网盘下载源码

    1点击下载(Scrapy示例)

    2增量式爬虫示例-点击下载

    
    
    作者:华王 博客:https://www.cnblogs.com/huahuawang/
  • 相关阅读:
    java 的 线程池应用和实践
    拦截信息短信息并转发到指定手机
    分享 UC优视 的android程序员面试题
    解释通讯协议中的xml
    设计模式工厂模式
    MongoDB基础教程系列第一篇 进入MongoDB世界
    Docx组件读写Word文档介绍
    [转]Oracle数据库逻辑增量备份之exp/imp
    JSON文件读取
    JAVA综合--如何掌握JDK1.5枚举类型[转]
  • 原文地址:https://www.cnblogs.com/huahuawang/p/14812016.html
Copyright © 2020-2023  润新知