首先我們來說說css選擇器;其實在上面的概述:和scrapy相關的函數就這么三個而已:response.css("css表達式")、extract()、extract_first()。有變化的就是:css表達式的寫法,這里我們就列舉一些常見的表達式,雖然不能囊括100%的爬取任務,但可以很負責的說,至少可以囊括90%的爬取,這里小編會把常見的給諸位列舉哈,諸位見類似的便可直接依葫蘆畫瓢使用了。按照HTML標簽的結構可以分為:標簽屬性值提取、標簽內容提取,我們分別介紹對應的情況;
1、標簽屬性值的提取
href的值URL的提取:這是最常見的,我們要進入下一頁、或是打開內容頁……都少不了URL值,如下面這段HTML,我們來提取一下里面的URL
<ol class="page-navigator"> <li class="current"><a href="http://lab.scrapyd.cn/page/1/">1</a></li> <li><a href="http://lab.scrapyd.cn/page/2/">2</a></li> <li><a href="http://lab.scrapyd.cn/page/3/">3</a></li> <li><a href="http://lab.scrapyd.cn/page/4/">4</a></li> </ol>
這其實是我們爬蟲實驗室的分頁,如果我們要爬取下一頁,我們該如何提取URL呢?提取屬性我們是用:“標簽名::attr(屬性名)”,比如我們要提取url表達式就是:a::attr(href),要提取圖片地址的表達式就是:img::attr(src)……以此類推,好了知道scrapy給我們提供的提取變了的工具,那我們就可以提取上面的URL了,有多種方式,首先我們可以直接:
response.css("a::attr(href)")
然后,我們調試一下,看是不是我們想要的結果,cmd輸入:
scrapy shell lab.scrapy.cn
然后我們發現結果並非是我們想要的分頁URL,而是頁面中所有的URL:
Out[3]: ['http://lab.scrapyd.cn/', 'http://lab.scrapyd.cn/archives/57.html', 'http://lab.scrapyd.cn/tag/%E8%89%BA%E6%9C%AF/', 'http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/', …… 'http://lab.scrapyd.cn/page/1/', 'http://lab.scrapyd.cn/page/2/', …… 'http://lab.scrapyd.cn/page/6/', 'http://lab.scrapyd.cn/page/2/', 'http://lab.scrapyd.cn/tag/%E4%BA%BA%E7%94%9F/', …… 'http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/', 'http://lab.scrapyd.cn/tag/%E7%94%9F%E6%B4%BB/', …… 'http://lab.scrapyd.cn/']
這當然不是我們想要的,我們想要的只是分頁的URL,那這要怎么辦?那我們就需要限定一下我們URL的范圍,最好的方法就是找到我們要提取目標最近的class或是id,可以看到這段代碼中有個class="page-navigator",那我們就可以這樣來寫:
response.css(".page-navigator a::attr(href)").extract()
當這樣限定之后,我們發現這就成功提取了我們想要的URL,灰常的完美!隨便解釋哈上面的:.page-navigator,其中點代表class選擇器,如果代碼中是:id=“page-navigator”,那我們這里就要寫成:“#page-navigator”,大家如果不清楚可以去這里看看:https://www.runoob.com/css/css-id-class.html;
再來一個提取標簽屬性的栗子,最常見的就是我們的圖片地址,也就是要提取圖片的src值,如下面網頁:
<article class="post" itemscope="" itemtype="http://schema.org/BlogPosting"> <h1 class="post-title" itemprop="name headline"><a itemprop="url" href="http://lab.scrapyd.cn/archives/57.html">中國傳世名畫</a></h1> <div class="post-content" itemprop="articleBody"> <p>看官,此頁面只為爬蟲練習使用,都是殘卷,若喜歡可以去找點高清版!</p> <p><img src="http://lab.scrapyd.cn/usr/uploads/2018/02/3875934880.jpg" alt="1.jpg" title="1.jpg"></p> <p><img src="http://lab.scrapyd.cn/usr/uploads/2018/02/2269613152.jpg" alt="2.jpg" title="2.jpg"></p> <p><img src="http://lab.scrapyd.cn/usr/uploads/2018/02/2360992798.jpg" alt="3.jpg" title="3.jpg"></p> <p><img src="http://lab.scrapyd.cn/usr/uploads/2018/02/2239103416.jpg" alt="4.jpg" title="4.jpg"></p> <p><img src="http://lab.scrapyd.cn/usr/uploads/2018/02/4145232684.jpg" alt="5.jpg" title="5.jpg"></p> </div> <p itemprop="keywords" class="tags">標簽: <a href="http://lab.scrapyd.cn/tag/%E8%89%BA%E6%9C%AF/">藝術</a>, <a href="http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/">名畫</a></p> </article>
這個頁面是我們爬蟲實驗室:http://lab.scrapyd.cn/archives/57.html的文章,我們要提取里面圖片的地址供scrapy下載,有上面的基礎就很簡單了:首先找到隔img最近的class或id,可以看到有個:class="post-content",於是我們可以這樣寫表達式:
response.css(".post-content img::attr(src)").extract()
調試的話自己試哈,經過這個表達式:".post-content img::attr(src),可以看到已經成功提取出來:
Out[2]: ['http://lab.scrapyd.cn/usr/uploads/2018/02/3875934880.jpg', 'http://lab.scrapyd.cn/usr/uploads/2018/02/2269613152.jpg', 'http://lab.scrapyd.cn/usr/uploads/2018/02/2360992798.jpg', 'http://lab.scrapyd.cn/usr/uploads/2018/02/2239103416.jpg', 'http://lab.scrapyd.cn/usr/uploads/2018/02/4145232684.jpg']
好了上面便是提取標簽屬性的方法,利用的就是:標簽名::attr(屬性名),關鍵點就是如何縮小范圍!
2、標簽內容的提取
設么是標簽內容,比如:
<p>scrapy中文網</p> <div>scrapy中文網:www.scrapyd.cn</div> <h1>scrapy實驗室:lab.scrapyd.cn</h1> <title>scrapy中文網</title>
上面標簽:p、div、h1里面的文字便是標簽內容,那我們要如何提取呢?用到了scrapy給我提供的這么一個方法:“::text”,比如要提取上面p標簽里面的內容,我們可以這樣:
response.css("p::text").extract()
那接下來我們就演示哈如何提取scrapy中文網:http://www.scrapd.cn的title:
<html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Scrapy 中文網</title>
提取方式:
response.css("title::text").extract()
上面的這個栗子灰常簡單,應為tittle標簽是頁面唯一的標簽,所以在title前不用加什么限制,當你發現標簽不唯一的時候,我們就需要縮小范圍,原理和上面標簽屬性提取一樣,要么找最近的class、要么找最近的id,然后最終縮小提取訪問,這樣就能讓提取很准確,如下面這個例子:
<div class="left"> scrapy中文網左邊 </div> <div class="center"> scrapy中文網中部 </div> <div class="right"> scrapy中文網右側 </div>
如果我們要提取第二個div里面的內容,我們肯定不能這樣:
response.css("div::text").extract()
如果這樣提取的話,我們肯定得不到正確的結果,我們還需要限制哈范圍,找到最近的class=“center”,最終表達式如下:
response.css(".center::text").extract()
這樣的話就能正確提取我們想要的內容;下面我們再介紹一個最常用的提取方式:含有嵌套標簽文字的提取,HTML如下:
<div class="post-content" itemprop="articleBody"> <p>如果你因失去了太陽而流淚,那么你也將失去群星了。 <br> If you shed tears when you miss the sun, you also miss the stars. </p> <p> <a href="http://www.scrapyd.cn">scrapy中文網(</a><a href="http://www.scrapyd.cn">http://www.scrapyd.cn</a>)整理 </p> </div>
如果我們要提取class=“post-content”里面的所有文字,我們需要怎么辦呢?顯然上面的知識無法做到。這段文字是scrapy實驗室:http://lab.scrapyd.cn/archives/28.html 里的文章,諸位可以scrapy shell進行驗證結果,可以記住用下面的方式:
response.css(".post-content *::text").extract()
可以看到,“::tex“t前面有個“*”號,這是一個小技巧,一般人我都告訴他,學着點!好了,上面的一些常見的內容已經說了些;接下來我們來說一些比較復雜的表達式,大家根據情況使用:
3、CSS 高級用法:
CSS選擇器用於選擇你想要的元素的樣式的模式。"CSS"列表示在CSS版本的屬性定義(CSS1,CSS2,或對CSS3)。
| 選擇器 | 示例 | 示例說明 | CSS |
|---|---|---|---|
| .class | .intro | 選擇所有class="intro"的元素 | 1 |
| #id | #firstname | 選擇所有id="firstname"的元素 | 1 |
| * | * | 選擇所有元素 | 2 |
| element | p | 選擇所有<p>元素 | 1 |
| element,element | div,p | 選擇所有<div>元素和<p>元素 | 1 |
| element element | div p | 選擇<div>元素內的所有<p>元素 | 1 |
| element>element | div>p | 選擇所有父級是 <div> 元素的 <p> 元素 | 2 |
| element+element | div+p | 選擇所有緊接着<div>元素之后的<p>元素 | 2 |
| [attribute] | [target] | 選擇所有帶有target屬性元素 | 2 |
| [attribute=value] | [target=-blank] | 選擇所有使用target="-blank"的元素 | 2 |
| [attribute~=value] | [title~=flower] | 選擇標題屬性包含單詞"flower"的所有元素 | 2 |
| [attribute|=language] | [lang|=en] | 選擇一個lang屬性的起始值="EN"的所有元素 | 2 |
| :link | a:link | 選擇所有未訪問鏈接 | 1 |
| :visited | a:visited | 選擇所有訪問過的鏈接 | 1 |
| :active | a:active | 選擇活動鏈接 | 1 |
| :hover | a:hover | 選擇鼠標在鏈接上面時 | 1 |
| :focus | input:focus | 選擇具有焦點的輸入元素 | 2 |
| :first-letter | p:first-letter | 選擇每一個<P>元素的第一個字母 | 1 |
| :first-line | p:first-line | 選擇每一個<P>元素的第一行 | 1 |
| :first-child | p:first-child | 指定只有當<p>元素是其父級的第一個子級的樣式。 | 2 |
| :before | p:before | 在每個<p>元素之前插入內容 | 2 |
| :after | p:after | 在每個<p>元素之后插入內容 | 2 |
| :lang(language) | p:lang(it) | 選擇一個lang屬性的起始值="it"的所有<p>元素 | 2 |
| element1~element2 | p~ul | 選擇p元素之后的每一個ul元素 | 3 |
| [attribute^=value] | a[src^="https"] | 選擇每一個src屬性的值以"https"開頭的元素 | 3 |
| [attribute$=value] | a[src$=".pdf"] | 選擇每一個src屬性的值以".pdf"結尾的元素 | 3 |
| [attribute*=value] | a[src*="runoob"] | 選擇每一個src屬性的值包含子字符串"runoob"的元素 | 3 |
| :first-of-type | p:first-of-type | 選擇每個p元素是其父級的第一個p元素 | 3 |
| :last-of-type | p:last-of-type | 選擇每個p元素是其父級的最后一個p元素 | 3 |
| :only-of-type | p:only-of-type | 選擇每個p元素是其父級的唯一p元素 | 3 |
| :only-child | p:only-child | 選擇每個p元素是其父級的唯一子元素 | 3 |
| :nth-child(n) | p:nth-child(2) | 選擇每個p元素是其父級的第二個子元素 | 3 |
| :nth-last-child(n) | p:nth-last-child(2) | 選擇每個p元素的是其父級的第二個子元素,從最后一個子項計數 | 3 |
| :nth-of-type(n) | p:nth-of-type(2) | 選擇每個p元素是其父級的第二個p元素 | 3 |
| :nth-last-of-type(n) | p:nth-last-of-type(2) | 選擇每個p元素的是其父級的第二個p元素,從最后一個子項計數 | 3 |
| :last-child | p:last-child | 選擇每個p元素是其父級的最后一個子級。 | 3 |
| :root | :root | 選擇文檔的根元素 | 3 |
| :empty | p:empty | 選擇每個沒有任何子級的p元素(包括文本節點) | 3 |
| :target | #news:target | 選擇當前活動的#news元素(包含該錨名稱的點擊的URL) | 3 |
| :enabled | input:enabled | 選擇每一個已啟用的輸入元素 | 3 |
| :disabled | input:disabled | 選擇每一個禁用的輸入元素 | 3 |
| :checked | input:checked | 選擇每個選中的輸入元素 | 3 |
| :not(selector) | :not(p) | 選擇每個並非p元素的元素 | 3 |
| ::selection | ::selection | 匹配元素中被用戶選中或處於高亮狀態的部分 | 3 |
| :out-of-range | :out-of-range | 匹配值在指定區間之外的input元素 | 3 |
| :in-range | :in-range | 匹配值在指定區間之內的input元素 | 3 |
| :read-write | :read-write | 用於匹配可讀及可寫的元素 | 3 |
| :read-only | :read-only | 用於匹配設置 "readonly"(只讀) 屬性的元素 | 3 |
| :optional | :optional | 用於匹配可選的輸入元素 | 3 |
| :required | :required | 用於匹配設置了 "required" 屬性的元素 | 3 |
| :valid | :valid | 用於匹配輸入值為合法的元素 | 3 |
| :invalid | :invalid | 用於匹配輸入值為非法的元素 | 3 |
