Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
外話
今天把 《CSS REFACTORING》(中文名叫《CSS重構(gòu):樣式表性能調(diào)優(yōu)》)電子書(shū)粗略的瀏覽了一遍,這本書(shū)很薄,150頁(yè)左右,首先是介紹了什么是重構(gòu)并舉了兩個(gè)簡(jiǎn)單的重構(gòu)例子,然后介紹了CSS的選擇器優(yōu)先級(jí),再然后介紹了CSS的最佳實(shí)踐, 再然后就介紹如何重置瀏覽器的默認(rèn)樣式,最后比較虛的、純理論的介紹了CSS重構(gòu)的策略,然后就沒(méi)有然后了。這書(shū)整體內(nèi)容很簡(jiǎn)單,但是,其中對(duì)于 CSS選擇器優(yōu)先級(jí)計(jì)算 作了比較深入的講解。
什么是選擇器優(yōu)先級(jí)(Specificity)
直接復(fù)制了MDN對(duì)優(yōu)先級(jí)的定義 上的解釋?zhuān)?/p>
瀏覽器通過(guò)優(yōu)先級(jí)來(lái)判斷哪一些屬性值與一個(gè)元素最為相關(guān),從而在該元素上應(yīng)用這些屬性值。優(yōu)先級(jí)是基于不同種類(lèi)選擇器組成的匹配規(guī)則。
這句話也是很抽象,暫且先不管它了。但是我們可以先看一個(gè)例子:
<div id="content" class="content"> 我是什么顏色 </div>
#content { color: #f00; } .content { color: #0f0; }
那最后文字是什么顏色呢?答案很簡(jiǎn)單:紅色。這就涉及到了優(yōu)先級(jí)問(wèn)題,同一塊內(nèi)容,我們同時(shí)用了 ID選擇器 和 類(lèi)選擇器,因?yàn)?ID選擇器 優(yōu)先級(jí)大于 類(lèi)選擇器 , 所以最終顯示為紅色。
優(yōu)先級(jí)的計(jì)算規(guī)則
相信每位寫(xiě)過(guò)CSS的朋友都知道,CSS選擇器的優(yōu)先級(jí)關(guān)系是:
內(nèi)聯(lián) > ID選擇器 > 類(lèi)選擇器 > 標(biāo)簽選擇器。
但是,瀏覽器具體的優(yōu)先級(jí)算法是怎樣的?可能還有些人不知道 。《CSS REFACTORING》 中提到了算法的過(guò)程 。
A specificity is determined by plugging numbers into (a, b, c, d):
翻譯過(guò)來(lái)就是
優(yōu)先級(jí)是由 A 、B、C、D 的值來(lái)決定的,其中它們的值計(jì)算規(guī)則如下:
這樣子直接看好像也還是很明白 ,那先上個(gè)例子:
#nav-global > ul > li > a.nav-link
套用上面的算法,依次求出 A B C D 的值:
上面算出的A 、 B、C、D 可以簡(jiǎn)記作:(0, 1, 1, 3)。
為了熟悉掌握優(yōu)先級(jí)算法 ,我們?cè)賮?lái)做一些練習(xí):
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)先級(jí)是怎么算的了。但是,還有一個(gè)問(wèn)題,怎么比較兩個(gè)優(yōu)先級(jí)的高低呢?
比較規(guī)則是: 從左往右依次進(jìn)行比較 ,較大者勝出,如果相等,則繼續(xù)往右移動(dòng)一位進(jìn)行比較 。如果4位全部相等,則后面的會(huì)覆蓋前面的
再來(lái)看一下例子:
<div class="nav-list" id="nav-list"> <div class="item">nav1</div> <div class="item">nav2</div> </div>
#nav-list .item { color: #f00; } .nav-list .item { color: #0f0; }
算出 #nav-list .item 的優(yōu)先級(jí)是 (0, 1, 1, 0), .nav-list .item 的優(yōu)先級(jí)是 (0, 0, 2, 0)。 左邊第一位都是0, 再看看左邊第二位,前者是1,后者是0, 所以(0, 1, 1, 0) 的大于 (0, 0, 2, 0) ,即 #nva-list .item 大于 .nav-list .item,所以字體會(huì)是紅色。
優(yōu)先級(jí)的特殊情況
經(jīng)過(guò)上面的優(yōu)先級(jí)計(jì)算規(guī)則,我們可以知道內(nèi)聯(lián)樣式的優(yōu)先級(jí)是最高的,但是外部樣式有沒(méi)有什么辦法覆蓋內(nèi)聯(lián)樣式呢?有的,那就要 !important 出馬了。因?yàn)橐话闱闆r下,很少會(huì)使用內(nèi)聯(lián)樣式 ,所以 !important 也很少會(huì)用到!如果不是為了要覆蓋內(nèi)聯(lián)樣式,建議盡量不要使用 !important 。、
那可能有人會(huì)想,那如果我內(nèi)聯(lián)樣式用了 !important,是不是外部樣式就沒(méi)有辦法了呢?比如下面的代碼:
<div class="app" style="color:#f00!important">666</div>
.app { color: 0f0!important; }
是的,你贏了,這時(shí)候內(nèi)聯(lián)樣式已經(jīng)強(qiáng)大到不管你外部樣式怎么寫(xiě)都無(wú)法覆蓋它了。這種情況在實(shí)際代碼中是要杜絕的!記住,千萬(wàn)不要在內(nèi)聯(lián)樣式中使用 !important
最后 , !important 真的是的無(wú)法超越的王者嗎?其實(shí)不是的,一些情況,我們可以超越 !important, 請(qǐng)看下面的例子:
<div class="box" style="background: #f00; width: 300px!important;"><div>
.box { max-width: 100px; }
這時(shí)候 .box 的寬度只有 100px , 而不是 300px, 可見(jiàn),max-width 可以超越 width!important!但是,這實(shí)際上不是優(yōu)先級(jí)的問(wèn)題,因?yàn)閮?yōu)先級(jí)是比較相同屬性的,而 max-width 和 width 是兩個(gè)不同的問(wèn)題。之所以舉這個(gè)例子,是要告訴大家,有時(shí)候不管怎么設(shè)置容器的 width 都不生效,檢查一下是不是有人寫(xiě)了 max-width 坑了你哈。
OK,優(yōu)先級(jí)先寫(xiě)到這里啦,朋友們有問(wèn)題歡迎留言討論~ 作者:漁歌。
家都知道display可以轉(zhuǎn)換元素類(lèi)型,但是大多人其實(shí)對(duì)于display的屬性值,比較熟悉的只是block和inline以及inline-block和none,對(duì)于其他屬性值,了解都比較一般,在平時(shí)開(kāi)發(fā)過(guò)程中也不太用得到其他的屬性值,但是每次用這個(gè)屬性的時(shí)候,腦海里都會(huì)冒出來(lái),其他的屬性值,設(shè)置了會(huì)是怎么樣、有什么樣的特點(diǎn),這個(gè)奇奇怪怪的想法,所以找了個(gè)時(shí)間,寫(xiě)下這篇文章, 跟我有相同可愛(ài)想法的伙伴,如果感興趣的,可以駐步瞄一眼喲;
display屬性:規(guī)定元素應(yīng)該生成的框的類(lèi)型(改變?cè)氐念?lèi)型,使用display屬性)。
ps:以下就是每個(gè)屬性的詳解了,啦啦啦啦啦啦啦;
屬性值詳解:
1、none:此元素不會(huì)被顯示;
(1) none此單詞的意思是沒(méi)有一個(gè)、毫無(wú)的意思;所以當(dāng)display的屬性值設(shè)置為none的時(shí)候,表示的是沒(méi)有框類(lèi)型,沒(méi)有框類(lèi)型的元素,是無(wú)法在瀏覽器中顯示的,就實(shí)現(xiàn)隱藏元素的作用了;
示例如下:
html結(jié)構(gòu):
<div></div>
<p>我是p,測(cè)試div消失后,會(huì)不會(huì)占據(jù)瀏覽器空間</p>
css樣式:
<style>
div{
width:100px;
height:100px;
background:violet;
/*設(shè)置div的屬性值為none,觀察div是否會(huì)隱藏不可見(jiàn)*/
display: none;
}
p{
background:yellowgreen
}
</style>
以上代碼效果可以看出,div設(shè)置none之后,實(shí)現(xiàn)了完全消失并且不占據(jù)瀏覽器的空間效果;
(2)有很多標(biāo)簽,display的屬性值默認(rèn)是none,比如 head meta style link等等;
(3)項(xiàng)目應(yīng)用中,做二級(jí)導(dǎo)航效果或者鼠標(biāo)懸停效果動(dòng)態(tài)時(shí),會(huì)經(jīng)常用到這個(gè)屬性值,下次我們寫(xiě)一個(gè)好玩的二級(jí)效果再來(lái)展示這個(gè)屬性值的作用;
2、block:此元素將顯示為塊級(jí)元素,此元素前后會(huì)帶有換行符。
示例如下:
html結(jié)構(gòu):
<em>我原本是行內(nèi)元素</em>
css樣式:
<style>
em{
width:100px;
height:100px;
background:tomato;
/*em本來(lái)是行內(nèi)元素元素,現(xiàn)在使用display屬性轉(zhuǎn)換為塊狀元素 */
display: block;
}
</style>
3、inline 默認(rèn)。此元素會(huì)被顯示為內(nèi)聯(lián)元素,元素前后沒(méi)有換行符。
示例如下:
html結(jié)構(gòu):
<div>我原本是塊狀元素</div>
<span>用來(lái)測(cè)試的行內(nèi)元素span</span>
css樣式:
<style>
div{
width:80px;
height:80px;
background:coral;
/*div標(biāo)簽本來(lái)是塊狀元素,現(xiàn)在使用display屬性轉(zhuǎn)換為行內(nèi)元素;*/
display: inline;
}
span{
background:cornflowerblue
}
</style>
4、inline-block 行內(nèi)塊元素(CSS2.1 新增的值)
說(shuō)明:行內(nèi)塊元素既具備行內(nèi)元素的特性也具備塊狀元素的特性,具備行內(nèi)元素前后沒(mé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 此元素會(huì)作為列表顯示。
(1) 此屬性值表示將元素顯示為列表項(xiàng)標(biāo)簽,li標(biāo)簽?zāi)J(rèn)的display的屬性值是list-item,display的屬性值為list-item的標(biāo)簽也屬于塊狀元素;
示例如下:
(2) li標(biāo)簽作為列表項(xiàng)標(biāo)簽,前面會(huì)有列表項(xiàng)標(biāo)記,下面給div標(biāo)簽設(shè)置為list-item,div也會(huì)有列表項(xiàng)標(biāo)記
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標(biāo)簽設(shè)置為list-item*/
display: list-item;
}
6、run-in 此元素會(huì)根據(jù)上下文作為塊級(jí)元素或內(nèi)聯(lián)元素顯示。
(1) 目前很少有瀏覽器支持run-in這個(gè)屬性值,并且在開(kāi)發(fā)過(guò)程中用不到這個(gè)屬性值,不予過(guò)多的研究;
7、table 此元素會(huì)作為塊級(jí)表格來(lái)顯示(類(lèi)似 <table>),表格前后帶有換行符。
(1)table標(biāo)簽?zāi)J(rèn)的元素類(lèi)型是table,顯示為塊級(jí)表格,可以設(shè)置大小并且單獨(dú)占據(jù)一行;(2)當(dāng)table標(biāo)簽的元素類(lèi)型是table時(shí),并且設(shè)置寬度和高度之后,后代td標(biāo)簽的寬度和高度,默認(rèn)是由table根據(jù)內(nèi)容的多少去分配的;
(3) table屬于塊狀元素,但是對(duì)比別的塊狀元素,有自己的特點(diǎn), table會(huì)單獨(dú)占據(jù)一行,但是在沒(méi)有設(shè)置width的情況下,不會(huì)與父元素同寬,而是根據(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)其他標(biāo)簽設(shè)置display的屬性值為table,也不會(huì)具有表格的特性;
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è)置為表格類(lèi)型*/
display: table;
}
div td{
border:1px solid chocolate;
background:darkcyan
}
span{
background:cornflowerblue
}
8、inline-table 此元素會(huì)作為內(nèi)聯(lián)表格來(lái)顯示(類(lèi)似 <table>),表格前后沒(méi)有換行符。
(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標(biāo)簽設(shè)置為inline-table之后,td標(biāo)簽的寬度就不能是table根據(jù)內(nèi)容去分配了,需要單獨(dú)給td設(shè)置width和height屬性實(shí)現(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 此元素會(huì)作為一個(gè)表格標(biāo)題顯示(類(lèi)似 <caption>)
(1)caption標(biāo)簽display的屬性值是table-caption,能設(shè)置寬度高度盒模型屬性等,屬于塊狀元素;
html結(jié)構(gòu):
<table>
<caption>我是表格的標(biāo)題標(biāo)簽</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)其他標(biāo)簽也可以設(shè)置此屬性值,但是不具備表格標(biāo)題的作用
10、table-header-group 此元素會(huì)作為一個(gè)或多個(gè)行的分組來(lái)顯示(類(lèi)似 <thead>)。
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è)置無(wú)效*/
height:60px;
width:400px;
margin:100px;
}
11、table-row-group 此元素會(huì)作為一個(gè)或多個(gè)行的分組來(lái)顯示(類(lèi)似 <tbody>)。
<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è)置無(wú)效*/
height:60px;
width:400px;
margin:100px;
}
12、table-footer-group 此元素會(huì)作為一個(gè)或多個(gè)行的分組來(lái)顯示(類(lèi)似 <tfoot>)。
(1)tfoot標(biāo)簽display的屬性值是table-footer-group;
(2)tfoot標(biāo)簽的大小根據(jù)table自動(dòng)分配,或者根據(jù)td而定,本身不能設(shè)置大小或者其他邊距
(3)其他標(biāo)簽可以設(shè)置此屬性值,但是不具表格表尾標(biāo)簽的作用
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è)置無(wú)效*/
table tfoot{
height:60px;
width:400px;
margin:100px;
}
13、table-row 此元素會(huì)作為一個(gè)表格行顯示(類(lèi)似 <tr>)。
(1) tr標(biāo)簽display的屬性值是table-row
(2) tr標(biāo)簽設(shè)置height有效,width 邊距設(shè)置無(wú)效,具體的大小根據(jù)table和td而定;
(3) 其他標(biāo)簽可以設(shè)置此屬性值,但是不具表格行標(biāo)簽的作用
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 此元素會(huì)作為一個(gè)表格單元格顯示(類(lèi)似 <td> 和 <th>)
(1)td , th標(biāo)簽display的屬性值是table-cell
(2)設(shè)置寬度、高度、邊框、內(nèi)邊距有效,外邊距無(wú)效,單元格之間的間距,使用border-spacing實(shí)現(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)其他標(biāo)簽可以設(shè)置此屬性值,但是不具單元格標(biāo)簽的作用
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 此元素會(huì)作為一個(gè)或多個(gè)列的分組來(lái)顯示(類(lèi)似 <colgroup>)。
(1)colgroup 標(biāo)簽 display的屬性值是table-column-group
(2)此標(biāo)簽的特點(diǎn)是對(duì)列進(jìn)行分組的,給分組的列設(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 此元素會(huì)作為一個(gè)單元格列顯示(類(lèi)似 <col>)
(1)col 標(biāo)簽 display的屬性值是table-column
(2)此標(biāo)簽的特點(diǎn)是對(duì)單元格列設(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 屬性的值。
說(shuō)明:此屬性值是所有css屬性都有的值,能被繼承的屬性,自然可以繼承,不能被繼承的,設(shè)置了此屬性值也不能實(shí)現(xiàn)繼承;
18、compact CSS 中有值 compact,不過(guò)由于缺乏廣泛支持,已經(jīng)從 CSS2.1 中刪除。
19、marker CSS 中有值 marker,不過(guò)由于缺乏廣泛支持,已經(jīng)從 CSS2.1 中刪除。
通過(guò)以上的測(cè)試,可以總結(jié)出:
display的屬性值為block,table的標(biāo)簽都為塊狀元素;
display的屬性值為inline,inline-table,inline-block的標(biāo)簽為行內(nèi)級(jí)元素;
表格中的標(biāo)簽對(duì)應(yīng)的那些display的屬性值,其他的標(biāo)簽也可以設(shè)置,但是都不具備表格標(biāo)簽的功能和特征,所以表格中的標(biāo)簽對(duì)應(yīng)的display的屬性值,不能泛用,相當(dāng)于一種特殊的存在;
們可以嘗試分析Ajax來(lái)抓取了相關(guān)數(shù)據(jù),但是并不是所有的頁(yè)面都是可以分析Ajax來(lái)就可以完成抓取的,比如淘寶。它的整個(gè)頁(yè)面數(shù)據(jù)確實(shí)也是通過(guò)Ajax獲取的,但是這些Ajax接口參數(shù)比較復(fù)雜,可能會(huì)包含加密密鑰等參數(shù),所以我們?nèi)绻胱约簶?gòu)造Ajax參數(shù)是比較困難的,對(duì)于這種頁(yè)面我們最方便快捷的抓取方法就是通過(guò)Selenium,本節(jié)我們就來(lái)用Selenium來(lái)模擬瀏覽器操作,抓取淘寶的商品信息,并將結(jié)果保存到MongoDB。
首先我們來(lái)看下淘寶的接口,看看它的接口相比一般Ajax多了怎樣的內(nèi)容。
打開(kāi)淘寶頁(yè)面,搜索一個(gè)商品,比如iPad,此時(shí)打開(kāi)開(kāi)發(fā)者工具,截獲Ajax請(qǐng)求,我們可以發(fā)現(xiàn)會(huì)獲取商品列表的接口。
它的鏈接包含了幾個(gè)GET參數(shù),如果我們要想構(gòu)造Ajax鏈接直接請(qǐng)求再好不過(guò)了,它的返回內(nèi)容是Json格式。
但是這個(gè)Ajax接口包含了幾個(gè)參數(shù),其中_ksTS、rn參數(shù)不能直接發(fā)現(xiàn)其規(guī)律,如果我們要去探尋它的生成規(guī)律也不是做不到,但這樣相對(duì)會(huì)比較繁瑣,所以如果我們直接用Selenium來(lái)模擬瀏覽器的話就不需要再關(guān)注這些接口參數(shù)了,只要在瀏覽器里面可以看到的我們都可以爬取。這也是為什么我們選用Selenium爬取淘寶的原因。
我們本節(jié)的目標(biāo)是爬取商品信息,例如:
這樣的一個(gè)結(jié)果就包含了一個(gè)商品的基本信息,包括商品圖片、名稱(chēng)、價(jià)格、購(gòu)買(mǎi)人數(shù)、店鋪名稱(chēng)、店鋪所在地,我們要做的就是將這些信息都抓取下來(lái)。
抓取入口就是淘寶的搜索頁(yè)面,這個(gè)鏈接是可以直接構(gòu)造參數(shù)訪問(wèn)的,例如如果搜索iPad,就可以直接訪問(wèn)https://s.taobao.com/search?q=iPad,呈現(xiàn)的就是第一頁(yè)的搜索結(jié)果,如圖所示:
如果想要分頁(yè)的話,我們注意到在頁(yè)面下方有一個(gè)分頁(yè)導(dǎo)航,包括前5頁(yè)的鏈接,也包括下一頁(yè)的鏈接,同時(shí)還有一個(gè)輸入任意頁(yè)碼跳轉(zhuǎn)的鏈接,如圖所示:
在這里商品搜索結(jié)果一般最大都為100頁(yè),我們要獲取的每一頁(yè)的內(nèi)容,只需要將頁(yè)碼從1到100順次遍歷即可,頁(yè)碼數(shù)是確定的。所以在這里我們可以直接在頁(yè)面跳轉(zhuǎn)文本框中輸入要跳轉(zhuǎn)的頁(yè)碼,然后點(diǎn)擊確定按鈕跳轉(zhuǎn)即可到達(dá)頁(yè)碼頁(yè)碼對(duì)應(yīng)的頁(yè)面。
在這里我們不直接點(diǎn)擊下一頁(yè)的原因是,一旦爬取過(guò)程中出現(xiàn)異常退出,比如到了50頁(yè)退出了,我們?nèi)绻c(diǎn)擊下一頁(yè)就無(wú)法快速切換到對(duì)應(yīng)的后續(xù)頁(yè)面,而且爬取過(guò)程中我們也需要記錄當(dāng)前的頁(yè)碼數(shù),而且一旦點(diǎn)擊下一頁(yè)之后頁(yè)面加載失敗,我們還需要做異常檢測(cè)檢測(cè)當(dāng)前頁(yè)面是加載到了第幾頁(yè),因此整個(gè)流程相對(duì)復(fù)雜,所以在這里我們直接選用跳頁(yè)的方式來(lái)爬取頁(yè)面。
當(dāng)我們成功加載出某一頁(yè)商品列表時(shí),利用Selenium即可獲取頁(yè)面源代碼,然后我們?cè)儆孟鄳?yīng)的解析庫(kù)解析即可,在這里我們選用PyQuery進(jìn)行解析。
下面我們用代碼來(lái)實(shí)現(xiàn)一下整個(gè)抓取過(guò)程。
獲取商品列表
首先我們需要構(gòu)造一個(gè)抓取的URL,https://s.taobao.com/search?q=iPad,URL非常簡(jiǎn)潔,參數(shù)q就是要搜索的關(guān)鍵字,我們只需要改變鏈接的參數(shù)q即可獲取不同商品的列表,在這里我們將商品的關(guān)鍵字定義成一個(gè)變量,然后構(gòu)造出這樣的一個(gè)URL。
構(gòu)造出URL之后我們就需要用Selenium進(jìn)行抓取了,我們實(shí)現(xiàn)如下抓取列表頁(yè)的方法:
from selenium import webdriverfrom 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):
"""
抓取索引頁(yè)
:param page: 頁(yè)碼
"""
print('正在爬取第', page, '頁(yè)')
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)造了一個(gè)WebDriver對(duì)象,使用的瀏覽器是Chrome,然后指定一個(gè)關(guān)鍵詞,如iPad,然后我們定義了一個(gè)get_index()方法,用于抓取商品列表頁(yè)。
在該方法里我們首先訪問(wèn)了這個(gè)鏈接,然后判斷了當(dāng)前的頁(yè)碼,如果大于1,那就進(jìn)行跳頁(yè)操作,否則等待頁(yè)面加載完成。
等待加載我們使用了WebDriverWait對(duì)象,它可以指定等待條件,同時(shí)指定一個(gè)最長(zhǎng)等待時(shí)間,在這里指定為最長(zhǎng)10秒。如果在這個(gè)時(shí)間內(nèi)成功匹配了等待條件,也就是說(shuō)頁(yè)面元素成功加載出來(lái)了,那就立即返回相應(yīng)結(jié)果并繼續(xù)向下執(zhí)行,否則到了最大等待時(shí)間還沒(méi)有加載出來(lái)就直接拋出超時(shí)異常。
比如我們最終要等待商品信息加載出來(lái),在這里就指定了presence_of_element_located這個(gè)條件,然后傳入了 .m-itemlist .items .item 這個(gè)選擇器,而這個(gè)選擇器對(duì)應(yīng)的頁(yè)面內(nèi)容就是每個(gè)商品的信息塊,可以到網(wǎng)頁(yè)里面查看一下。如果加載成功,就會(huì)執(zhí)行后續(xù)的get_products()方法,提取商品信息。
關(guān)于翻頁(yè)的操作,我們?cè)谶@里是首先獲取了頁(yè)碼輸入框,賦值為input,然后獲取了提交按鈕,賦值為submit,分別是下圖中的兩個(gè)元素:
首先我們清空了輸入框,調(diào)用clear()方法即可,隨后調(diào)用send_keys()方法將頁(yè)碼填充到輸入框中,然后點(diǎn)擊確定按鈕即可。
那么怎樣知道有沒(méi)有跳轉(zhuǎn)到對(duì)應(yīng)的頁(yè)碼呢?我們可以注意到成功跳轉(zhuǎn)某一頁(yè)后頁(yè)碼都會(huì)高亮顯示:
我們只需要判斷當(dāng)前高亮的頁(yè)碼數(shù)是當(dāng)前的頁(yè)碼數(shù)即可,所以在這里使用了另一個(gè)等待條件 text_to_be_present_in_element,它會(huì)等待某一文本出現(xiàn)在某一個(gè)節(jié)點(diǎn)里面即返回成功,在這里我們將高亮的頁(yè)碼節(jié)點(diǎn)對(duì)應(yīng)的CSS選擇器和當(dāng)前要跳轉(zhuǎn)的頁(yè)碼通過(guò)參數(shù)傳遞給這個(gè)等待條件,這樣它就會(huì)檢測(cè)當(dāng)前高亮的頁(yè)碼節(jié)點(diǎn)里是不是我們傳過(guò)來(lái)的頁(yè)碼數(shù),如果是,那就證明頁(yè)面成功跳轉(zhuǎn)到了這一頁(yè),頁(yè)面跳轉(zhuǎn)成功。
那么這樣,剛才我們所實(shí)現(xiàn)的get_index()方法就可以做到傳入對(duì)應(yīng)的頁(yè)碼,然后加載出對(duì)應(yīng)頁(yè)碼的商品列表后,再去調(diào)用get_products()方法進(jìn)行頁(yè)面解析。
解析商品列表
接下來(lái)我們就可以實(shí)現(xiàn)get_products()方法來(lái)解析商品列表了,在這里我們直接獲取頁(yè)面源代碼,然后用PyQuery進(jìn)行解析,實(shí)現(xiàn)如下:
from pyquery import PyQuery as pqdef 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屬性獲取了頁(yè)碼的源代碼,然后構(gòu)造了PyQuery解析對(duì)象,首先我們提取了商品列表,使用的CSS選擇器是 #mainsrp-itemlist .items .item,它會(huì)匹配到整個(gè)頁(yè)面的每個(gè)商品,因此它的匹配結(jié)果是多個(gè),所以在這里我們又對(duì)它進(jìn)行了一次遍歷,用for循環(huán)將每個(gè)結(jié)果分別進(jìn)行解析,在這里每個(gè)結(jié)果我們用for循環(huán)把它賦值為item變量,每個(gè)item變量都是一個(gè)PyQuery對(duì)象,然后我們?cè)僬{(diào)用它的find()方法,傳入CSS選擇器,就可以獲取單個(gè)商品的特定內(nèi)容了。
比如在這里我們查看一下商品信息源碼,如圖所示:
在這里我們觀察一下商品圖片的源碼,它是一個(gè) img 節(jié)點(diǎn),包含了id、class、data-src、alt、src等屬性,在這里我們之所以可以看到這張圖片是因?yàn)樗膕rc屬性被賦值為圖片的URL,在這里我們就把它的src屬性提取出來(lái)就可以獲取商品的圖片了,不過(guò)這里我們還注意到有一個(gè)data-src屬性,它的內(nèi)容也是圖片的URL,觀察后發(fā)現(xiàn)此URL是圖片的完整大圖,而src是壓縮后的小圖,所以這里我們抓取data-src屬性來(lái)作為商品的圖片。
所以我們需要先利用find()方法先找到圖片的這個(gè)節(jié)點(diǎn),然后再調(diào)用attr()方法獲取商品的data-src屬性即可,這樣就成功提取了商品圖片鏈接。然后我們用同樣的方法提取商品的價(jià)格、成交量、名稱(chēng)、店鋪、店鋪所在地等信息,然后將所有提取結(jié)果賦值為一個(gè)字典,叫做product,隨后調(diào)用save_to_mongo()將其保存到MongoDB即可。
保存到MongoDB
接下來(lái)我們?cè)賹⑸唐沸畔⒈4娴組ongoDB,實(shí)現(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('存儲(chǔ)到MongoDB成功')
except Exception:
print('存儲(chǔ)到MongoDB失敗')
我們首先創(chuàng)建了一個(gè)MongoDB的連接對(duì)象,然后指定了數(shù)據(jù)庫(kù),在方法里隨后指定了Collection的名稱(chēng),然后直接調(diào)用insert()方法即可將數(shù)據(jù)插入到MongoDB,此處的result變量就是在get_products()方法里傳來(lái)的product,包含了單個(gè)商品的信息,這樣我們就成功實(shí)現(xiàn)了數(shù)據(jù)的插入。
遍歷每頁(yè)
剛才我們所定義的get_index()方法需要接收一個(gè)參數(shù)page,page即代表頁(yè)碼數(shù),所以在這里我們?cè)賹?shí)現(xiàn)頁(yè)碼遍歷即可,代碼如下:
MAX_PAGE=100def main():
"""
遍歷每一頁(yè)
"""
for i in range(1, MAX_PAGE + 1):
index_page(i)
實(shí)現(xiàn)非常簡(jiǎn)單,只需要調(diào)用一個(gè)for循環(huán)即可,在這里定義最大的頁(yè)碼數(shù)100,range()方法的返回結(jié)果就是1到100的列表,順次遍歷調(diào)用index_page()方法即可。
這樣我們的淘寶商品爬蟲(chóng)就完成了,最后調(diào)用main()方法即可運(yùn)行。
我們將代碼運(yùn)行起來(lái),可以發(fā)現(xiàn)首先會(huì)彈出一個(gè)Chrome瀏覽器,然后順次訪問(wèn)淘寶頁(yè)面,然后控制臺(tái)便會(huì)輸出相應(yīng)的提取結(jié)果,這些商品信息結(jié)果都是一個(gè)字典形式,然后被存儲(chǔ)到了MongoDB里面。
但是此次爬取有個(gè)不太友好的地方就是Chrome瀏覽器,爬取過(guò)程必須要開(kāi)啟一個(gè)Chrome瀏覽器確實(shí)不太方便,所以在這里我們還可以對(duì)接PhantomJS,只需要將WebDriver的聲明修改一下即可,但是注意這里必須要安裝好PhantomJS,如果沒(méi)有安裝可以參考第一章里的安裝方法說(shuō)明。
將WebDriver聲明修改如下:
browser=webdriver.PhantomJS()
這樣在抓取過(guò)程中就不會(huì)有瀏覽器彈出了。
另外我們還可以設(shè)置緩存和禁用圖片加載的功能,進(jìn)一步提高爬取效率,修改如下:
SERVICE_ARGS=['--load-images=false', '--disk-cache=true']browser=webdriver.PhantomJS(service_args=SERVICE_ARGS)
這樣我們就可以禁用PhantomJS的圖片加載同時(shí)開(kāi)啟緩存,可以發(fā)現(xiàn)頁(yè)面爬取速度進(jìn)一步提升。
本節(jié)代碼地址為:https://github.com/Python3WebSpider/TaobaoProduct
End.
來(lái)源:公眾號(hào)“Python愛(ài)好者社區(qū)”
運(yùn)行人員:中國(guó)統(tǒng)計(jì)網(wǎng)小編(微信號(hào):itongjilove)
微博ID:中國(guó)統(tǒng)計(jì)網(wǎng)
中國(guó)統(tǒng)計(jì)網(wǎng),是國(guó)內(nèi)最早的大數(shù)據(jù)學(xué)習(xí)網(wǎng)站,公眾號(hào):中國(guó)統(tǒng)計(jì)網(wǎng)
http://www.itongji.cn
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。