當我們取得了網頁的response之后,最關鍵的就是如何從繁雜的網頁中把我們需要的數據提取出來,python從網頁中提取數據的包很多,常用的有下面的幾個:
- BeautifulSoup
它基於HTML代碼的結構來構造一個Python對象, 對不良標記的處理也非常合理,但是速度上有所欠缺。 - lxml
是一個基於 ElementTree (不是Python標准庫的一部分)的python化的XML解析庫(也可以解析HTML)。
你可以在scrapy中使用任意你熟悉的網頁數據提取工具,但是,scrapy本身也為我們提供了一套提取數據的機制,我們稱之為選擇器(seletors),他們通過特定的 XPath 或者 CSS 表達式來“選擇” HTML文件中的某個部分。XPath 是一門用來在XML文件中選擇節點的語言,也可以用在HTML上。 CSS 是一門將HTML文檔樣式化的語言。選擇器由它定義,並與特定的HTML元素的樣式相關連。
Scrapy選擇器構建於 lxml 庫之上,這意味着它們在速度和解析准確性上非常相似。下面我們來了解scrapy選擇器。
使用選擇器
scrapy中調用選擇器的方法非常的簡單,下面我們從實例中進行學習。
我們還是以博客園首頁的信息作為例子,演示使用選擇器抓取數據,下圖是首頁的html信息,我們下面就是抓取標題,鏈接,閱讀數,評論數。
import scrapy
from scrapy.selector import Selector
class Cnblog_Spider(scrapy.Spider):
name = "cnblog"
allowed_domains = ["cnblogs.com"]
start_urls = [
'https://www.cnblogs.com/',
]
def parse(self, response):
selector = Selector(response=response)
title = selector.xpath('//a[@class="titlelnk"]/text()').extract()
link = selector.xpath('//a[@class="titlelnk"]/@href').extract()
read = selector.xpath('//span[@class="article_comment"]/a/text()').extract()
comment = selector.xpath('//span[@class="article_view"]/a/text()').extract()
print('這是title:',title)
print('這是鏈接:', link)
print('這是閱讀數', read)
print('這是評論數', comment)
選擇器的使用可以分為下面的三步:
- 導入選擇器
from scrapy.selector import Selector
- 創建選擇器實例
selector = Selector(response=response)
- 使用選擇器
selector.xpath()
或者selector.css()
當然你可以使用xpath
或者css
中的任意一種或者組合使用,怎么方便怎么來,至於xpath和css語法,你可以去額外學習,仔細觀察,你會發現每個選擇器最后都有一個extract()
,你可以嘗試去掉這個看一下,區別在於,當你沒有使用extract()
的時候,提取出來的內容依然具有選擇器屬性,簡而言之,你可以繼續使用里面的內容進行提取下級內容,而當你使用了extract()
之后,提取出來的內容就會變成字符串格式了。我們進行多級提取的時候,這會很有用。值得注意的是,選擇器提取出來的內容是放在列表里面的,即使沒有內容,那也是一個空列表,下面我們運行這個爬蟲,你會發現內容已經被提取出來了。
事實上,我們可以完全不用那么麻煩,因為scrapy為我們提供了選擇器的簡易用法,當我們需要選擇器的時候,只要一步就可以了,如下:
import scrapy
class Cnblog_Spider(scrapy.Spider):
name = "cnblog"
allowed_domains = ["cnblogs.com"]
start_urls = [
'https://www.cnblogs.com/',
]
def parse(self, response):
title = response.xpath('//a[@class="titlelnk"]/text()').extract()
link = response.xpath('//a[@class="titlelnk"]/@href').extract()
read = response.xpath('//span[@class="article_comment"]/a/text()').extract()
comment = response.xpath('//span[@class="article_view"]/a/text()').extract()
print('這是title:', title)
print('這是鏈接:', link)
print('這是閱讀數', read)
print('這是評論數', comment)
可以看到,我們直接使用response.xpath()
就可以了,並沒有導入什么,實例化什么,可以說非常方便了,當然直接response.css()
一樣可以。
拓展
scrapy為我們提供的選擇器還有一些其他的特點,這里我們簡單的列舉
- extract()
前面已經提到了,.xpath() 及 .css() 方法返回一個類 SelectorList 的實例, 它是一個新選擇器的列表,就是說,你依然可以使用里面的元素進行向下提取,因為它還是一個選擇器,為了提取真實的原文數據,我們需要調用 .extract()>>> response.xpath('//title/text()') [<Selector (text) xpath=//title/text()>] >>> response.css('title::text') [<Selector (text) xpath=//title/text()>]
- extract_first()
如果想要提取到第一個匹配到的元素, 可以調用response.xpath('//span[@class="article_view"]/a/text()').extract_first()
這樣我們就拿到了第一個匹配的數據,當然,我們之前提到了選擇器返回的數據是一個列表,那么你當然可以使用response.xpath('//span[@class="article_view"]/a/text()').extract()[0]
拿到第一個匹配的數據,這和response.xpath('//span[@class="article_view"]/a/text()')[0].extract()
效果是一樣的,值得注意的是,如果是空列表,這兩種方法的區別就出現了,extract_first()會返回None,而后面的那種方法,就會因列表為空而報錯。
除此之外,我們還可以為extract_first()設置默認值,當空列表時,就會返回一個我們設置的值,比如:extract_first(default='not-found')
。
結合正則表達式
你會發現,之前我們匹配的閱讀數,評論數都會有漢字在里面,如果我們只想提取里面的數字呢,這個時候就可以使用正則表達式和選擇器配合來實現,比如下面:
import scrapy
class Cnblog_Spider(scrapy.Spider):
name = "cnblog"
allowed_domains = ["cnblogs.com"]
start_urls = [
'https://www.cnblogs.com/',
]
def parse(self, response):
read = response.xpath(
'//span[@class="article_comment"]/a/text()').re('\d+')
comment = response.xpath(
'//span[@class="article_view"]/a/text()').re('\d+')
print('這是閱讀數', read)
print('這是評論數', comment)
運行一下,可以看到,效果就出來了。