整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          深入理解CSS選擇器優(yōu)先級

          深入理解CSS選擇器優(yōu)先級

          外話

          今天把 《CSS REFACTORING》(中文名叫《CSS重構(gòu):樣式表性能調(diào)優(yōu)》)電子書粗略的瀏覽了一遍,這本書很薄,150頁左右,首先是介紹了什么是重構(gòu)并舉了兩個簡單的重構(gòu)例子,然后介紹了CSS的選擇器優(yōu)先級,再然后介紹了CSS的最佳實踐, 再然后就介紹如何重置瀏覽器的默認樣式,最后比較虛的、純理論的介紹了CSS重構(gòu)的策略,然后就沒有然后了。這書整體內(nèi)容很簡單,但是,其中對于 CSS選擇器優(yōu)先級計算 作了比較深入的講解。

          什么是選擇器優(yōu)先級(Specificity)

          直接復制了MDN對優(yōu)先級的定義 上的解釋:

          瀏覽器通過優(yōu)先級來判斷哪一些屬性值與一個元素最為相關(guān),從而在該元素上應(yīng)用這些屬性值。優(yōu)先級是基于不同種類選擇器組成的匹配規(guī)則。

          這句話也是很抽象,暫且先不管它了。但是我們可以先看一個例子:

          • HTML:
          <div id="content" class="content">
          我是什么顏色
          </div>
          
          • CSS:
          #content {
           color: #f00;
          }
          .content {
           color: #0f0;
          }
          

          那最后文字是什么顏色呢?答案很簡單:紅色。這就涉及到了優(yōu)先級問題,同一塊內(nèi)容,我們同時用了 ID選擇器 和 類選擇器,因為 ID選擇器 優(yōu)先級大于 類選擇器 , 所以最終顯示為紅色。

          優(yōu)先級的計算規(guī)則

          相信每位寫過CSS的朋友都知道,CSS選擇器的優(yōu)先級關(guān)系是:

          內(nèi)聯(lián) > ID選擇器 > 類選擇器 > 標簽選擇器。

          但是,瀏覽器具體的優(yōu)先級算法是怎樣的?可能還有些人不知道 。《CSS REFACTORING》 中提到了算法的過程 。

          A specificity is determined by plugging numbers into (a, b, c, d):

          1. If the styles are applied via the style attribute, a=1; otherwise, a=0.
          2. b is equal to the number of ID selectors present.
          3. c is equal to the number of class selectors, attribute selectors, and pseudoclasses present.
          4. d is equal to the number of type selectors and pseudoelements present.

          翻譯過來就是

          優(yōu)先級是由 A 、B、C、D 的值來決定的,其中它們的值計算規(guī)則如下:

          1. 如果存在內(nèi)聯(lián)樣式,那么 A=1, 否則 A=0;
          2. B 的值等于 ID選擇器 出現(xiàn)的次數(shù);
          3. C 的值等于 類選擇器 和 屬性選擇器 和 偽類 出現(xiàn)的總次數(shù);
          4. D 的值等于 標簽選擇器 和 偽元素 出現(xiàn)的總次數(shù) 。

          這樣子直接看好像也還是很明白 ,那先上個例子:

          #nav-global > ul > li > a.nav-link
          

          套用上面的算法,依次求出 A B C D 的值:

          1. 因為沒有內(nèi)聯(lián)樣式 ,所以 A=0;
          2. ID選擇器總共出現(xiàn)了1次, B=1;
          3. 類選擇器出現(xiàn)了1次, 屬性選擇器出現(xiàn)了0次,偽類選擇器出現(xiàn)0次,所以 C=(1 + 0 + 0)=1;
          4. 標簽選擇器出現(xiàn)了3次, 偽元素出現(xiàn)了0次,所以 D=(3 + 0)=3;

          上面算出的A 、 B、C、D 可以簡記作:(0, 1, 1, 3)。

          為了熟悉掌握優(yōu)先級算法 ,我們再來做一些練習:

          li /* (0, 0, 0, 1) */
          ul li /* (0, 0, 0, 2) */
          ul ol+li /* (0, 0, 0, 3) */
          ul ol+li /* (0, 0, 0, 3) */
          h1 + *[REL=up] /* (0, 0, 1, 1) */
          ul ol li.red /* (0, 0, 1, 3) */
          li.red.level /* (0, 0, 2, 1) */
          a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11 /* (0, 0, 11,0) */
          #x34y /* (0, 1, 0, 0) */
          li:first-child h2 .title /* (0, 0, 2, 2) */
          #nav .selected > a:hover /* (0, 1, 2, 1) */
          html body #nav .selected > a:hover /* (0, 1, 2, 3) */
          

          OK, 現(xiàn)在已經(jīng)弄清楚了優(yōu)先級是怎么算的了。但是,還有一個問題,怎么比較兩個優(yōu)先級的高低呢?

          比較規(guī)則是: 從左往右依次進行比較 ,較大者勝出,如果相等,則繼續(xù)往右移動一位進行比較 。如果4位全部相等,則后面的會覆蓋前面的

          再來看一下例子:

          • html:
          <div class="nav-list" id="nav-list">
           <div class="item">nav1</div>
           <div class="item">nav2</div>
          </div>
          
          • CSS:
          #nav-list .item {
           color: #f00;
          }
          .nav-list .item {
           color: #0f0;
          }
          

          算出 #nav-list .item 的優(yōu)先級是 (0, 1, 1, 0), .nav-list .item 的優(yōu)先級是 (0, 0, 2, 0)。 左邊第一位都是0, 再看看左邊第二位,前者是1,后者是0, 所以(0, 1, 1, 0) 的大于 (0, 0, 2, 0) ,即 #nva-list .item 大于 .nav-list .item,所以字體會是紅色。

          優(yōu)先級的特殊情況

          經(jīng)過上面的優(yōu)先級計算規(guī)則,我們可以知道內(nèi)聯(lián)樣式的優(yōu)先級是最高的,但是外部樣式有沒有什么辦法覆蓋內(nèi)聯(lián)樣式呢?有的,那就要 !important 出馬了。因為一般情況下,很少會使用內(nèi)聯(lián)樣式 ,所以 !important 也很少會用到!如果不是為了要覆蓋內(nèi)聯(lián)樣式,建議盡量不要使用 !important 。、

          那可能有人會想,那如果我內(nèi)聯(lián)樣式用了 !important,是不是外部樣式就沒有辦法了呢?比如下面的代碼:

          • HTML:
          <div class="app" style="color:#f00!important">666</div>
          
          • CSS:
          .app {
           color: 0f0!important;
          }
          

          是的,你贏了,這時候內(nèi)聯(lián)樣式已經(jīng)強大到不管你外部樣式怎么寫都無法覆蓋它了。這種情況在實際代碼中是要杜絕的!記住,千萬不要在內(nèi)聯(lián)樣式中使用 !important

          最后 , !important 真的是的無法超越的王者嗎?其實不是的,一些情況,我們可以超越 !important, 請看下面的例子:

          • html:
          <div class="box" style="background: #f00; width: 300px!important;"><div>
          
          • css:
          .box {
           max-width: 100px;
          }
          

          這時候 .box 的寬度只有 100px , 而不是 300px, 可見,max-width 可以超越 width!important!但是,這實際上不是優(yōu)先級的問題,因為優(yōu)先級是比較相同屬性的,而 max-width 和 width 是兩個不同的問題。之所以舉這個例子,是要告訴大家,有時候不管怎么設(shè)置容器的 width 都不生效,檢查一下是不是有人寫了 max-width 坑了你哈。

          OK,優(yōu)先級先寫到這里啦,朋友們有問題歡迎留言討論~ 作者:漁歌。

          家都知道display可以轉(zhuǎn)換元素類型,但是大多人其實對于display的屬性值,比較熟悉的只是blockinline以及inline-blocknone,對于其他屬性值,了解都比較一般,在平時開發(fā)過程中也不太用得到其他的屬性值,但是每次用這個屬性的時候,腦海里都會冒出來,其他的屬性值,設(shè)置了會是怎么樣、有什么樣的特點,這個奇奇怪怪的想法,所以找了個時間,寫下這篇文章, 跟我有相同可愛想法的伙伴,如果感興趣的,可以駐步瞄一眼喲;

          一、css規(guī)定,元素類型有兩大類,一類是塊狀元素,一類是行內(nèi)元素,首先了解一下兩種元素類型的特征:

          塊狀元素特征:

          • 塊狀元素可以正確解釋盒模型的屬性width,height,padding,margin,border
          • 塊狀元素前后帶有換行符,所以自占一行,在垂直方向一個接一個的放置;
          • 塊狀元素一般作為容器、盒子使用;
          • 塊狀元素默認情況下,寬度與父元素同寬,高度根據(jù)內(nèi)容而定,沒有內(nèi)容,高度為0;

          行內(nèi)(內(nèi)聯(lián))元素特征:

          • 行內(nèi)元素不能正確解釋盒模型的屬性,widthheight執(zhí)行無效,paddingbordermargin解釋時,左右解釋沒有問題,上下解釋不正確;
          • 行內(nèi)元素在一行內(nèi)從左往右依次排列;
          • 行內(nèi)元素默認情況下,寬度和高度都根據(jù)內(nèi)容而定;

          二、元素類型的轉(zhuǎn)換

          display屬性:規(guī)定元素應(yīng)該生成的框的類型(改變元素的類型,使用display屬性)。

          ps:以下就是每個屬性的詳解了,啦啦啦啦啦啦啦;

          屬性值詳解:

          1、none:此元素不會被顯示;

          (1) none此單詞的意思是沒有一個、毫無的意思;所以當display的屬性值設(shè)置為none的時候,表示的是沒有框類型,沒有框類型的元素,是無法在瀏覽器中顯示的,就實現(xiàn)隱藏元素的作用了;

          示例如下:

          html結(jié)構(gòu):

              <div></div>
              <p>我是p,測試div消失后,會不會占據(jù)瀏覽器空間</p>


          css樣式:

               <style>
                  div{
                      width:100px;
                      height:100px;
                      background:violet;
                      /*設(shè)置div的屬性值為none,觀察div是否會隱藏不可見*/
                      display: none;
                  }
                  p{
                      background:yellowgreen
                  }
              </style>

          以上代碼效果可以看出,div設(shè)置none之后,實現(xiàn)了完全消失并且不占據(jù)瀏覽器的空間效果;

          (2)有很多標簽,display的屬性值默認是none,比如 head meta style link等等;

          (3)項目應(yīng)用中,做二級導航效果或者鼠標懸停效果動態(tài)時,會經(jīng)常用到這個屬性值,下次我們寫一個好玩的二級效果再來展示這個屬性值的作用;

          2、block:此元素將顯示為塊級元素,此元素前后會帶有換行符。

          示例如下:

           html結(jié)構(gòu):
               <em>我原本是行內(nèi)元素</em>
           css樣式:
                 <style>
                      em{
                          width:100px;
                          height:100px;
                          background:tomato;
                         /*em本來是行內(nèi)元素元素,現(xiàn)在使用display屬性轉(zhuǎn)換為塊狀元素 */
                          display: block;
                      }
                  </style>

          3、inline 默認。此元素會被顯示為內(nèi)聯(lián)元素,元素前后沒有換行符。

          示例如下:

            html結(jié)構(gòu):   
              <div>我原本是塊狀元素</div>
              <span>用來測試的行內(nèi)元素span</span>
              
             css樣式: 
               <style>
                  div{
                      width:80px;
                      height:80px;
                      background:coral;
                      /*div標簽本來是塊狀元素,現(xiàn)在使用display屬性轉(zhuǎn)換為行內(nèi)元素;*/
                      display: inline;
                  }
                  span{
                      background:cornflowerblue
                  }
              </style>

          4、inline-block 行內(nèi)塊元素(CSS2.1 新增的值)

          說明:行內(nèi)塊元素既具備行內(nèi)元素的特性也具備塊狀元素的特性,具備行內(nèi)元素前后沒有換行符可以在一行內(nèi)并列顯示的特性,具備塊狀元素可以正確解釋盒模型屬性的特性。

          示例如下:

          div塊狀元素,span行內(nèi)元素,使用此屬性值轉(zhuǎn)換為行內(nèi)塊元素;

           html結(jié)構(gòu):   
                <div>我原本是塊狀元素</div>
                <span>我原本是行內(nèi)元素</span>
              
             css樣式: 
                  div,span{
                     /*div塊狀元素,span行內(nèi)元素,使用此屬性值轉(zhuǎn)換為行內(nèi)塊元素;*/  
                      display: inline-block;
                  }
                  div{
                      width:100px;
                      height:100px;
                      background:tomato;
                  }
                  span{
                      width:100px;
                      height:100px;
                      background:turquoise;
                  }

          5、list-item 此元素會作為列表顯示。

          (1) 此屬性值表示將元素顯示為列表項標簽,li標簽?zāi)J的display的屬性值是list-item,display的屬性值為list-item的標簽也屬于塊狀元素;

          示例如下:

          (2) li標簽作為列表項標簽,前面會有列表項標記,下面給div標簽設(shè)置為list-item,div也會有列表項標記

           html結(jié)構(gòu):
                  <ul>
                      <div>div0</div>
                      <li>li1</li>
                      <li>li2</li>
                      <li>li3</li>
                  </ul>
              
           css樣式:     
          
                  ul{
                      background:tomato
                  }
                  ul li{
                      border:1px solid turquoise
                  }
                  div{
                      /*給div標簽設(shè)置為list-item*/
                      display: list-item;
                  }

          6、run-in 此元素會根據(jù)上下文作為塊級元素或內(nèi)聯(lián)元素顯示。

          (1) 目前很少有瀏覽器支持run-in這個屬性值,并且在開發(fā)過程中用不到這個屬性值,不予過多的研究;

          7、table 此元素會作為塊級表格來顯示(類似 <table>),表格前后帶有換行符。

          (1)table標簽?zāi)J的元素類型是table,顯示為塊級表格,可以設(shè)置大小并且單獨占據(jù)一行;(2)當table標簽的元素類型是table時,并且設(shè)置寬度和高度之后,后代td標簽的寬度和高度,默認是由table根據(jù)內(nèi)容的多少去分配的;

          (3) table屬于塊狀元素,但是對比別的塊狀元素,有自己的特點, table會單獨占據(jù)一行,但是在沒有設(shè)置width的情況下,不會與父元素同寬,而是根據(jù)內(nèi)容而定;

          html結(jié)構(gòu):
                <table>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                  </table>
                  <span>我是行內(nèi)元素span</span>
                  
           css樣式: 
          
                  table{
                      border:2px solid red;
                  }
                  table td{
                      border:1px solid chocolate;
                      background:darkcyan
                  }
                  span{
                      background:cornflowerblue
                  }

          (3)其他標簽設(shè)置display的屬性值為table,也不會具有表格的特性;

          html結(jié)構(gòu):
                 <div>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                 </div>
          
           css樣式:    
           
                  div{
                      height:300px;
                      width:300px;
                      border:2px solid red;
                      /*將div設(shè)置為表格類型*/
                      display: table;
                  }
                  div td{
                      border:1px solid chocolate;
                      background:darkcyan
                  }
                  span{
                      background:cornflowerblue
                  }

          8、inline-table 此元素會作為內(nèi)聯(lián)表格來顯示(類似 <table>),表格前后沒有換行符。

          (1)將table顯示為行內(nèi)表格,具有行內(nèi)塊元素的特征,可以和別的行內(nèi)元素從左往右顯示也可以設(shè)置寬度和高度;

          html結(jié)構(gòu):

                 <table>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                 </table>
                 <span>我是行內(nèi)元素span</span>


          css樣式:

                  table{
                      border:2px solid red;
                      /*將table設(shè)置為inline-table*/
                      display: inline-block;
                  }
                  table td{
                      background:darkcyan;
                      
                  }
                span{
                       background:cornflowerblue
                }

          (2)將table標簽設(shè)置為inline-table之后,td標簽的寬度就不能是table根據(jù)內(nèi)容去分配了,需要單獨給td設(shè)置widthheight屬性實現(xiàn),不然td的大小就是內(nèi)容的大小

          HTML結(jié)構(gòu):

                 <table>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                 </table>
                 <span>我是行內(nèi)元素span</span>

          css樣式:

           table{
                      border:2px solid red;
                      /*將table設(shè)置為inline-table*/
                      display: inline-block;
                      width:300px;
                      height:300px;
                  }
                 table td{
                      background:darkcyan;
                      
                  }
                span{
                       background:cornflowerblue
                }

          9、table-caption 此元素會作為一個表格標題顯示(類似 <caption>)

          (1)caption標簽display的屬性值是table-caption,能設(shè)置寬度高度盒模型屬性等,屬于塊狀元素;

          html結(jié)構(gòu):

               <table>
                  <caption>我是表格的標題標簽</caption>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr> 
                </table>

          css樣式:

           table{
                      border:2px solid red;
                     width:300px;
                     height:300px;
                  }
                  table td{
                      background:darkcyan;
                  }
                  table caption{
                     width:400px;
                     height:50px;
                     background:cyan;
                     margin:10px 0;
                     padding:10px;
                     line-height:50px;
                  }

          (2)其他標簽也可以設(shè)置此屬性值,但是不具備表格標題的作用

          10、table-header-group 此元素會作為一個或多個行的分組來顯示(類似 <thead>)。

          • thead標簽display的屬性值是table-header-group;
          • thead標簽的大小根據(jù)table自動分配,或者根據(jù)td而定,本身不能設(shè)置大小或者邊距
          • 其他標簽可以設(shè)置此屬性值,但是不具備表頭標簽的作用

          html結(jié)構(gòu):

                 <table>
                      <thead>
                          <tr>
                              <td>1</td>
                              <td>2</td>
                              <td>3</td>
                          </tr>
                      </thead>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                 </table>

          css樣式:

           table{
                      border:2px solid red;
                  }
                  table td{
                      background:darkcyan;
                      width:100px;
                      height:100px;
                  }
                table thead{
                    /*以下屬性設(shè)置無效*/
                    height:60px;
                    width:400px;
                    margin:100px;
                }

          11、table-row-group 此元素會作為一個或多個行的分組來顯示(類似 <tbody>)。

          • tbody標簽display的屬性值是table-row -group;
          • tbody標簽的大小根據(jù)table自動分配,或者根據(jù)td而定,本身不能設(shè)置大小或者邊距
          • 其他標簽可以設(shè)置此屬性值,但是不具表格主體標簽的作用
                 <table>
                     <thead>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                     </thead>
                    <tbody>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                      <tr>
                          <td>1</td>
                          <td>2</td>
                          <td>3</td>
                      </tr>
                    </tbody>
                 </table>

          css樣式:

                  table{
                      border:2px solid red;
                      width:300px;
                      height:300px; 
                  }
                  table td{
                      background:darkcyan;
                  }
                table tbody{
                    /*以下屬性設(shè)置無效*/
                    height:60px;
                    width:400px;
                    margin:100px;
                }

          12、table-footer-group 此元素會作為一個或多個行的分組來顯示(類似 <tfoot>)。

          (1)tfoot標簽display的屬性值是table-footer-group;

          (2)tfoot標簽的大小根據(jù)table自動分配,或者根據(jù)td而定,本身不能設(shè)置大小或者其他邊距

          (3)其他標簽可以設(shè)置此屬性值,但是不具表格表尾標簽的作用

          HTML結(jié)構(gòu):

              <table>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tfoot>
                     <tr>
                         <td>1</td>
                         <td>2</td>
                         <td>3</td>
                      </tr>
                  </tfoot>
                </table>

          css樣式:

                  table{
                      border:2px solid red;
                      width:300px;
                      height:300px;
                  }
                  table td{
                      background:darkcyan;
                  }
                /*以下屬性設(shè)置無效*/
                table tfoot{
                    height:60px;
                    width:400px;
                    margin:100px;
                }

          13、table-row 此元素會作為一個表格行顯示(類似 <tr>)。

          (1) tr標簽display的屬性值是table-row

          (2) tr標簽設(shè)置height有效,width 邊距設(shè)置無效,具體的大小根據(jù)table和td而定;

          (3) 其他標簽可以設(shè)置此屬性值,但是不具表格行標簽的作用

          HTML結(jié)構(gòu):

              <table>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                </table>

          css樣式:

                  table{
                      border:2px solid red;
                  }
                  table td{
                      background:darkcyan;
                  }
                 table tr{
                   border:1px solid chartreuse;
                   margin:20px;
                   height:100px;
                   width:300px;
                   margin:10px;
                 }

          14、table-cell 此元素會作為一個表格單元格顯示(類似 <td> 和 <th>)

          (1)td , th標簽display的屬性值是table-cell

          (2)設(shè)置寬度、高度、邊框、內(nèi)邊距有效,外邊距無效,單元格之間的間距,使用border-spacing實現(xiàn)

          HTML結(jié)構(gòu):

               <table>
                 <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
               </table>

          css樣式:

              <style>
                  table{
                      border:2px solid red;
                      border-spacing: 10px;
                  }
                  table td{
                      background:darkcyan;
                      width:100px;
                      height:100px;
                  }
              </style>

          (3)其他標簽可以設(shè)置此屬性值,但是不具單元格標簽的作用

          html結(jié)構(gòu):

          <table>
                  <tr>
                      <span>1</span>
                      <span>2</span>
                      <span>3</span>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
             </table>

          css樣式:

                  table{
                      border:2px solid red;
                      border-spacing: 10px;
                  }
                  table td{
                      background:darkcyan;
                      width:100px;
                      height:100px;
                  }
                 span{
                     display: table-cell;
                     width:100px;
                     height:100px;
                     background:darkcyan;
                 }

          15、table-column-group 此元素會作為一個或多個列的分組來顯示(類似 <colgroup>)。

          (1)colgroup 標簽 display的屬性值是table-column-group

          (2)此標簽的特點是對列進行分組的,給分組的列設(shè)置樣式;

          HTML結(jié)構(gòu):

          <table>
                <colgroup span="1"></colgroup>
                <colgroup span="2"></colgroup>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
             </table>

          css樣式:

              table{
                      border:2px solid red;
                      border-spacing: 10px;
                  }
                  table td{
                      background:darkcyan;
                  }
                 table colgroup:nth-child(1){
                      width:100px;
                      height:100px;
                      background:pink
                  }
                  table colgroup:nth-child(2){
                      width:50px;
                      height:50px;
                      background:green
                  }

          16、table-column 此元素會作為一個單元格列顯示(類似 <col>)

          (1)col 標簽 display的屬性值是table-column

          (2)此標簽的特點是對單元格列設(shè)置效果;

          HTML結(jié)構(gòu):

          <table>
                 <col span="1">
                 <col span="2">
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
                  <tr>
                      <td>1</td>
                      <td>2</td>
                      <td>3</td>
                  </tr>
             </table>

          css樣式:

           <style>
                  table{
                      border:2px solid red;
                      border-spacing: 10px;
                  }
                  table td{
                      width:100px;
                      height:100px;
                  }
                  table col:nth-child(1){
                      background:pink
                  }
                  table col:nth-child(2){
                      background:green
                  }
              </style>

          17、inherit 規(guī)定應(yīng)該從父元素繼承 display 屬性的值。

          說明:此屬性值是所有css屬性都有的值,能被繼承的屬性,自然可以繼承,不能被繼承的,設(shè)置了此屬性值也不能實現(xiàn)繼承;

          18、compact CSS 中有值 compact,不過由于缺乏廣泛支持,已經(jīng)從 CSS2.1 中刪除。

          19、marker CSS 中有值 marker,不過由于缺乏廣泛支持,已經(jīng)從 CSS2.1 中刪除。

          總結(jié):

          通過以上的測試,可以總結(jié)出:

          display的屬性值為blocktable的標簽都為塊狀元素;

          display的屬性值為inlineinline-table,inline-block的標簽為行內(nèi)級元素;

          表格中的標簽對應(yīng)的那些display的屬性值,其他的標簽也可以設(shè)置,但是都不具備表格標簽的功能和特征,所以表格中的標簽對應(yīng)的display的屬性值,不能泛用,相當于一種特殊的存在;

          們可以嘗試分析Ajax來抓取了相關(guān)數(shù)據(jù),但是并不是所有的頁面都是可以分析Ajax來就可以完成抓取的,比如淘寶。它的整個頁面數(shù)據(jù)確實也是通過Ajax獲取的,但是這些Ajax接口參數(shù)比較復雜,可能會包含加密密鑰等參數(shù),所以我們?nèi)绻胱约簶?gòu)造Ajax參數(shù)是比較困難的,對于這種頁面我們最方便快捷的抓取方法就是通過Selenium,本節(jié)我們就來用Selenium來模擬瀏覽器操作,抓取淘寶的商品信息,并將結(jié)果保存到MongoDB。

          接口分析

          首先我們來看下淘寶的接口,看看它的接口相比一般Ajax多了怎樣的內(nèi)容。

          打開淘寶頁面,搜索一個商品,比如iPad,此時打開開發(fā)者工具,截獲Ajax請求,我們可以發(fā)現(xiàn)會獲取商品列表的接口。

          它的鏈接包含了幾個GET參數(shù),如果我們要想構(gòu)造Ajax鏈接直接請求再好不過了,它的返回內(nèi)容是Json格式。

          但是這個Ajax接口包含了幾個參數(shù),其中_ksTS、rn參數(shù)不能直接發(fā)現(xiàn)其規(guī)律,如果我們要去探尋它的生成規(guī)律也不是做不到,但這樣相對會比較繁瑣,所以如果我們直接用Selenium來模擬瀏覽器的話就不需要再關(guān)注這些接口參數(shù)了,只要在瀏覽器里面可以看到的我們都可以爬取。這也是為什么我們選用Selenium爬取淘寶的原因。

          頁面分析

          我們本節(jié)的目標是爬取商品信息,例如:

          這樣的一個結(jié)果就包含了一個商品的基本信息,包括商品圖片、名稱、價格、購買人數(shù)、店鋪名稱、店鋪所在地,我們要做的就是將這些信息都抓取下來。

          抓取入口就是淘寶的搜索頁面,這個鏈接是可以直接構(gòu)造參數(shù)訪問的,例如如果搜索iPad,就可以直接訪問https://s.taobao.com/search?q=iPad,呈現(xiàn)的就是第一頁的搜索結(jié)果,如圖所示:

          如果想要分頁的話,我們注意到在頁面下方有一個分頁導航,包括前5頁的鏈接,也包括下一頁的鏈接,同時還有一個輸入任意頁碼跳轉(zhuǎn)的鏈接,如圖所示:

          在這里商品搜索結(jié)果一般最大都為100頁,我們要獲取的每一頁的內(nèi)容,只需要將頁碼從1到100順次遍歷即可,頁碼數(shù)是確定的。所以在這里我們可以直接在頁面跳轉(zhuǎn)文本框中輸入要跳轉(zhuǎn)的頁碼,然后點擊確定按鈕跳轉(zhuǎn)即可到達頁碼頁碼對應(yīng)的頁面。

          在這里我們不直接點擊下一頁的原因是,一旦爬取過程中出現(xiàn)異常退出,比如到了50頁退出了,我們?nèi)绻c擊下一頁就無法快速切換到對應(yīng)的后續(xù)頁面,而且爬取過程中我們也需要記錄當前的頁碼數(shù),而且一旦點擊下一頁之后頁面加載失敗,我們還需要做異常檢測檢測當前頁面是加載到了第幾頁,因此整個流程相對復雜,所以在這里我們直接選用跳頁的方式來爬取頁面。

          當我們成功加載出某一頁商品列表時,利用Selenium即可獲取頁面源代碼,然后我們再用相應(yīng)的解析庫解析即可,在這里我們選用PyQuery進行解析。

          代碼實戰(zhàn)

          下面我們用代碼來實現(xiàn)一下整個抓取過程。

          獲取商品列表

          首先我們需要構(gòu)造一個抓取的URL,https://s.taobao.com/search?q=iPad,URL非常簡潔,參數(shù)q就是要搜索的關(guān)鍵字,我們只需要改變鏈接的參數(shù)q即可獲取不同商品的列表,在這里我們將商品的關(guān)鍵字定義成一個變量,然后構(gòu)造出這樣的一個URL。

          構(gòu)造出URL之后我們就需要用Selenium進行抓取了,我們實現(xiàn)如下抓取列表頁的方法:

          from selenium import webdriver

          from selenium.common.exceptions import TimeoutException

          from selenium.webdriver.common.by import By

          from selenium.webdriver.support import expected_conditions as EC

          from selenium.webdriver.support.wait import WebDriverWait

          from urllib.parse import quote

          browser=webdriver.Chrome()

          wait=WebDriverWait(browser, 10)

          KEYWORD='iPad'

          def index_page(page):

          """

          抓取索引頁

          :param page: 頁碼

          """

          print('正在爬取第', page, '頁')

          try:

          url='https://s.taobao.com/search?q=' + quote(KEYWORD)

          browser.get(url)

          if page > 1:

          input=wait.until(

          EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input')))

          submit=wait.until(

          EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit')))

          input.clear()

          input.send_keys(page)

          submit.click()

          wait.until(

          EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)))

          wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item')))

          get_products()

          except TimeoutException:

          index_page(page)

          在這里我們首先構(gòu)造了一個WebDriver對象,使用的瀏覽器是Chrome,然后指定一個關(guān)鍵詞,如iPad,然后我們定義了一個get_index()方法,用于抓取商品列表頁。

          在該方法里我們首先訪問了這個鏈接,然后判斷了當前的頁碼,如果大于1,那就進行跳頁操作,否則等待頁面加載完成。

          等待加載我們使用了WebDriverWait對象,它可以指定等待條件,同時指定一個最長等待時間,在這里指定為最長10秒。如果在這個時間內(nèi)成功匹配了等待條件,也就是說頁面元素成功加載出來了,那就立即返回相應(yīng)結(jié)果并繼續(xù)向下執(zhí)行,否則到了最大等待時間還沒有加載出來就直接拋出超時異常。

          比如我們最終要等待商品信息加載出來,在這里就指定了presence_of_element_located這個條件,然后傳入了 .m-itemlist .items .item 這個選擇器,而這個選擇器對應(yīng)的頁面內(nèi)容就是每個商品的信息塊,可以到網(wǎng)頁里面查看一下。如果加載成功,就會執(zhí)行后續(xù)的get_products()方法,提取商品信息。

          關(guān)于翻頁的操作,我們在這里是首先獲取了頁碼輸入框,賦值為input,然后獲取了提交按鈕,賦值為submit,分別是下圖中的兩個元素:

          首先我們清空了輸入框,調(diào)用clear()方法即可,隨后調(diào)用send_keys()方法將頁碼填充到輸入框中,然后點擊確定按鈕即可。

          那么怎樣知道有沒有跳轉(zhuǎn)到對應(yīng)的頁碼呢?我們可以注意到成功跳轉(zhuǎn)某一頁后頁碼都會高亮顯示:

          我們只需要判斷當前高亮的頁碼數(shù)是當前的頁碼數(shù)即可,所以在這里使用了另一個等待條件 text_to_be_present_in_element,它會等待某一文本出現(xiàn)在某一個節(jié)點里面即返回成功,在這里我們將高亮的頁碼節(jié)點對應(yīng)的CSS選擇器和當前要跳轉(zhuǎn)的頁碼通過參數(shù)傳遞給這個等待條件,這樣它就會檢測當前高亮的頁碼節(jié)點里是不是我們傳過來的頁碼數(shù),如果是,那就證明頁面成功跳轉(zhuǎn)到了這一頁,頁面跳轉(zhuǎn)成功。

          那么這樣,剛才我們所實現(xiàn)的get_index()方法就可以做到傳入對應(yīng)的頁碼,然后加載出對應(yīng)頁碼的商品列表后,再去調(diào)用get_products()方法進行頁面解析。

          解析商品列表

          接下來我們就可以實現(xiàn)get_products()方法來解析商品列表了,在這里我們直接獲取頁面源代碼,然后用PyQuery進行解析,實現(xiàn)如下:

          from pyquery import PyQuery as pq

          def get_products():

          """

          提取商品數(shù)據(jù)

          """

          html=browser.page_source

          doc=pq(html)

          items=doc('#mainsrp-itemlist .items .item').items()

          for item in items:

          product={

          'image': item.find('.pic .img').attr('data-src'),

          'price': item.find('.price').text(),

          'deal': item.find('.deal-cnt').text(),

          'title': item.find('.title').text(),

          'shop': item.find('.shop').text(),

          'location': item.find('.location').text()

          }

          print(product)

          save_to_mongo(product)

          首先我們調(diào)用了page_source屬性獲取了頁碼的源代碼,然后構(gòu)造了PyQuery解析對象,首先我們提取了商品列表,使用的CSS選擇器是 #mainsrp-itemlist .items .item,它會匹配到整個頁面的每個商品,因此它的匹配結(jié)果是多個,所以在這里我們又對它進行了一次遍歷,用for循環(huán)將每個結(jié)果分別進行解析,在這里每個結(jié)果我們用for循環(huán)把它賦值為item變量,每個item變量都是一個PyQuery對象,然后我們再調(diào)用它的find()方法,傳入CSS選擇器,就可以獲取單個商品的特定內(nèi)容了。

          比如在這里我們查看一下商品信息源碼,如圖所示:

          在這里我們觀察一下商品圖片的源碼,它是一個 img 節(jié)點,包含了id、class、data-src、alt、src等屬性,在這里我們之所以可以看到這張圖片是因為它的src屬性被賦值為圖片的URL,在這里我們就把它的src屬性提取出來就可以獲取商品的圖片了,不過這里我們還注意到有一個data-src屬性,它的內(nèi)容也是圖片的URL,觀察后發(fā)現(xiàn)此URL是圖片的完整大圖,而src是壓縮后的小圖,所以這里我們抓取data-src屬性來作為商品的圖片。

          所以我們需要先利用find()方法先找到圖片的這個節(jié)點,然后再調(diào)用attr()方法獲取商品的data-src屬性即可,這樣就成功提取了商品圖片鏈接。然后我們用同樣的方法提取商品的價格、成交量、名稱、店鋪、店鋪所在地等信息,然后將所有提取結(jié)果賦值為一個字典,叫做product,隨后調(diào)用save_to_mongo()將其保存到MongoDB即可。

          保存到MongoDB

          接下來我們再將商品信息保存到MongoDB,實現(xiàn)如下:

          MONGO_URL='localhost'

          MONGO_DB='taobao'

          MONGO_COLLECTION='products'

          client=pymongo.MongoClient(MONGO_URL)

          db=client[MONGO_DB]

          def save_to_mongo(result):

          """

          保存至MongoDB

          :param result: 結(jié)果

          """

          try:

          if db[MONGO_COLLECTION].insert(result):

          print('存儲到MongoDB成功')

          except Exception:

          print('存儲到MongoDB失敗')

          我們首先創(chuàng)建了一個MongoDB的連接對象,然后指定了數(shù)據(jù)庫,在方法里隨后指定了Collection的名稱,然后直接調(diào)用insert()方法即可將數(shù)據(jù)插入到MongoDB,此處的result變量就是在get_products()方法里傳來的product,包含了單個商品的信息,這樣我們就成功實現(xiàn)了數(shù)據(jù)的插入。

          遍歷每頁

          剛才我們所定義的get_index()方法需要接收一個參數(shù)page,page即代表頁碼數(shù),所以在這里我們再實現(xiàn)頁碼遍歷即可,代碼如下:

          MAX_PAGE=100

          def main():

          """

          遍歷每一頁

          """

          for i in range(1, MAX_PAGE + 1):

          index_page(i)

          實現(xiàn)非常簡單,只需要調(diào)用一個for循環(huán)即可,在這里定義最大的頁碼數(shù)100,range()方法的返回結(jié)果就是1到100的列表,順次遍歷調(diào)用index_page()方法即可。

          這樣我們的淘寶商品爬蟲就完成了,最后調(diào)用main()方法即可運行。

          運行

          我們將代碼運行起來,可以發(fā)現(xiàn)首先會彈出一個Chrome瀏覽器,然后順次訪問淘寶頁面,然后控制臺便會輸出相應(yīng)的提取結(jié)果,這些商品信息結(jié)果都是一個字典形式,然后被存儲到了MongoDB里面。

          對接PhantomJS

          但是此次爬取有個不太友好的地方就是Chrome瀏覽器,爬取過程必須要開啟一個Chrome瀏覽器確實不太方便,所以在這里我們還可以對接PhantomJS,只需要將WebDriver的聲明修改一下即可,但是注意這里必須要安裝好PhantomJS,如果沒有安裝可以參考第一章里的安裝方法說明。

          將WebDriver聲明修改如下:

          browser=webdriver.PhantomJS()

          這樣在抓取過程中就不會有瀏覽器彈出了。

          另外我們還可以設(shè)置緩存和禁用圖片加載的功能,進一步提高爬取效率,修改如下:

          SERVICE_ARGS=['--load-images=false', '--disk-cache=true']

          browser=webdriver.PhantomJS(service_args=SERVICE_ARGS)

          這樣我們就可以禁用PhantomJS的圖片加載同時開啟緩存,可以發(fā)現(xiàn)頁面爬取速度進一步提升。

          源碼

          本節(jié)代碼地址為:https://github.com/Python3WebSpider/TaobaoProduct

          End.

          來源:公眾號“Python愛好者社區(qū)”

          運行人員:中國統(tǒng)計網(wǎng)小編(微信號:itongjilove)

          微博ID:中國統(tǒng)計網(wǎng)

          中國統(tǒng)計網(wǎng),是國內(nèi)最早的大數(shù)據(jù)學習網(wǎng)站,公眾號:中國統(tǒng)計網(wǎng)

          http://www.itongji.cn


          主站蜘蛛池模板: 亚洲性日韩精品一区二区三区 | 农村乱人伦一区二区| 熟妇人妻系列av无码一区二区| 精品亚洲AV无码一区二区三区| 国产精品免费大片一区二区| 国产精品99精品一区二区三区| 少妇无码一区二区三区免费| 国产品无码一区二区三区在线蜜桃 | 久久久精品日本一区二区三区| 成人H动漫精品一区二区| 国产AV午夜精品一区二区三区| 久久国产高清一区二区三区| 精品一区二区三区中文| 在线观看日韩一区| 国产精品无码亚洲一区二区三区 | 精品亚洲一区二区三区在线观看| 暖暖免费高清日本一区二区三区| 亚洲精品精华液一区二区| 亚洲av乱码一区二区三区| 男女久久久国产一区二区三区| 亚洲bt加勒比一区二区| 另类免费视频一区二区在线观看| 真实国产乱子伦精品一区二区三区 | 日韩精品无码免费一区二区三区| 精品国产日产一区二区三区| 精品久久一区二区三区| 无码人妻一区二区三区免费看| 日韩精品一区二区亚洲AV观看| 亚洲一区中文字幕久久| 91精品一区二区三区在线观看| 亚洲一区中文字幕在线观看| 亚洲欧美国产国产综合一区| 精品一区二区ww| 国产精品亚洲专区一区| 无码AⅤ精品一区二区三区| 成人区精品一区二区不卡| 鲁丝片一区二区三区免费| 人妻av无码一区二区三区| 亚洲国产成人久久一区二区三区 | 亚洲中文字幕在线无码一区二区| 91视频国产一区|