Date: 2019-07-07
Author: Sun
1. 定義
Scrapy中的Selector類是基於lxml庫建立的,並且簡化了API接口。在使用的過程中先使用xpath或者css選擇器選中頁面中要提取的數據,然后進行提取。
從頁面中提取數據的核心技術是HTTP文本解析,在python中常用的模塊處理:
BeautifulSoup 非常流行的解析庫,API簡單,但解析的速度慢。
lxml 是一套使用c語言編寫的xml解析庫,解析速度快,API相對比較復雜。
2. 三者比較
-
BeautifulSoup缺點:慢
-
lxml:基於 ElementTree
-
Scrapy seletors: parsel library,構建於 lxml 庫之上,這意味着它們在速度和解析准確性上非常相似
3. 提取過程
通過scrapy的下載器可以下載對應的網頁內容,將內容封裝到response對象中。
從頁面中提取數據的核心技術是HTTP文本解析,在python中常用的模塊處理:
BeautifulSoup 非常流行的解析庫,API簡單,但解析的速度慢。
lxml 是一套使用c語言編寫的xml解析庫,解析速度快,API相對比較復雜。
Scrapy中的Selector類是基於lxml庫建立的,並且簡化了API接口。在使用的過程中先使用xpath或者css選擇器選中頁面中要提取的數據,然后進行提取。
提取數據
調用Selector或者SelectList對象的以下幾種方法可將選中的內容進行提取
(1)extract() 返回選中內容的Unicode字符串。
(2)extract_first() SelectorList專有,返回其中第一個Selector對象調用extract方法。通常SelectorList中只含有一個Selector對象的時候選擇調用該方法。
(3)re() 使用正則表達式來提取選中內容中的某部分。
舉個例子
selector.xpath('.//b/text()') .extract #[‘價格:99.00元’,‘價格:88.00元’,‘價格:88.00元’]
selector.xpath('.//b/text()').re('\d+.\d+') #[ '99.00','88.00','88.00']
(4) re_first() 返回SelectorList對象中的第一個Selector對象調用re方法。
selector.xpath('.//b/text()').re_first('\d+.\d+') # '99.00'
4 scrapy response自帶xpath和css
構造選擇器(selectors)
Scrapy selector是以 文字(text) 或 TextResponse
構造的 Selector
實例。 其根據輸入的類型自動選擇最優的分析方法(XML vs HTML):
實際的開發過程中,幾乎不需要手動創建Selector對象,,Response對象以自身參數自動創建Selector對象
response.xpath('.//h1/text').extract() # [ 'song','shi','chao']
response.css('li::text').extract() # [ 'song','shi','chao']
案例分析
>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
以文字構造:
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']
以response構造:
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']
為了方便起見,response對象以 .selector 屬性提供了一個selector, 您可以隨時使用該快捷方法:
>>> response.selector.xpath('//span/text()').extract()
[u'good']
4.1 Xpath選擇器
xpath即xml路徑語言,用來確定xml文檔中某個部分的語言,xml文檔(html屬於xml)是一系列節點構成的樹。
基礎語法
**/ ** 選中文檔的根 描述一個從根開始的絕對路徑 ./表示從當前節點選取 (比如提取了一部分,還需要從提取出來里面進行提取就會使用,如果不加 **. **會從整個文檔中開始提取)
. 選中當前節點 用來描述相對路徑
**.. ** 選中當前節點的父節點 用來描述相對路徑
ELEMENT 選中子節點中所有的ELEMENT元素節點
**//ELEMENT ** 選中子孫節點中所有的ELEMENT元素節點
*** ** 選中所有元素子節點
text() 選中所有文本子節點
@ATTR 選中名為ATTR的屬性節點
@* 選中所有的屬性節點
[謂語] 謂語用來查找某個特定的節點或者包含某個特定值的節點
舉例
response.xpath('/html/body/div') #選取body下的所有div
response.xpath('//a') #選中文檔所有a
response.xpath('/html/body//div') #選中body下的所有節點中的div,無論在什么位置
response.xpath('//a/text()') #選取所有a的文本
response.xpath('/html/div/*') #選取div的所有元素子節點
response.xpath('//div/*/img') #選取div孫節點的所有img
response.xpath('//img/@src') #選取所有img的src屬性
response.xpath('//a[1]/img/@*') #選取第一個a下img的所有屬性
response.xpath('//a[2]') #所有a中的第2個
response.xpath('//a[last()]') #所有a中的最后一個 ('/a[last()-1]')#倒第二個 ('//a[position()<=3]')#使用position函數選中前三個 ('//div[@id]')#選中所有含有id屬性的div ('//div[@id="song"]')#選中所有id屬性為song的div
response.xpath('//p[contains(@class,'song')]') #選擇class屬性中含有‘song’的p元素
response.xpath('//div/a | //div/p') 或者,頁面中可能是a可能是p
4.2. CSS選擇器
css即層疊樣式表,選擇器不如xpath強大,原理就是選擇的時候翻譯成xpath表達式在調用xpath方法。
*** ** 選擇所有節點
/#container 選擇id為container的節點
.container 選擇class包含container的節點
li a 選擇 所有 li 下的所有 a 節點ul + p ** 選擇所有ul后面的第一個p元素
#container > ul 選擇id為container的第一個ul節點
**a[class] ** 選取所有有class屬性的a元素
a[href="http://b.com"] 含有href="http://b.com"的a元素
**a[href*='job'] ** 包含job的a元素
**a[href^='https'] ** 開頭是https的a元素
**a[href$='cn'] **結尾是cn的a元素
response.css('div a::text').extract() 所有div下所有a的文本
response.css('div a::attr(href)').extract() href的值
response.css('div>a:nth-child(1)') 選中每個div的第一個a > 會設定只在子節點中找,不會到孫節點中
response.css('div:not(#container)') 選取所有id不是container的div
response.css('div:first-child>a:last-child') 第一個div中最后一個a
5. scrapy命令行工具分析網頁元素
Scrapy是通過
scrapy
命令行工具進行控制的。 這里我們稱之為 “Scrapy tool” 以用來和子命令進行區分。 對於子命令,我們稱為 “command” 或者 “Scrapy commands”。
Scrapy tool 針對不同的目的提供了多個命令,每個命令支持不同的參數和選項。
使用 scrapy工具
您可以以無參數的方式啟動Scrapy工具。該命令將會給出一些使用幫助以及可用的命令:
Scrapy X.Y - no active project
Usage:
scrapy <command> [options] [args]
Available commands:
crawl Run a spider
fetch Fetch a URL using the Scrapy downloader
[...]
您也可以查看所有可用的命令:
scrapy -h
Scrapy提供了兩種類型的命令。一種必須在Scrapy項目中運行(針對項目(Project-specific)的命令),另外一種則不需要(全局命令)。全局命令在項目中運行時的表現可能會與在非項目中運行有些許差別(因為可能會使用項目的設定)。
全局命令:
- startproject
- settings
- runspider
- shell
- fetch
- view
- version
項目(Project-only)命令:
- crawl
- check
- list
- edit
- parse
- genspider
- deploy
- bench
startproject
- 語法:
scrapy startproject <project_name>
- 是否需要項目: no
在 project_name
文件夾下創建一個名為 project_name
的Scrapy項目。
例子:
$ scrapy startproject myproject
genspider
- 語法:
scrapy genspider [-t template] <name> <domain>
- 是否需要項目: yes
在當前項目中創建spider。
這僅僅是創建spider的一種快捷方法。該方法可以使用提前定義好的模板來生成spider。您也可以自己創建spider的源碼文件。
例子:
$ scrapy genspider -l
Available templates:
basic
crawl
csvfeed
xmlfeed
#scrapy提供了兩種基本的爬蟲模板,basic和crawl,可以選擇一種作為爬蟲模板,會自動在spiders目錄下生成爬蟲模板文件
$ scrapy genspider -d basic
import scrapy
class $classname(scrapy.Spider):
name = "$name"
allowed_domains = ["$domain"]
start_urls = (
'http://www.$domain/',
)
def parse(self, response):
pass
#創建基於基礎模板basic的爬蟲
$ scrapy genspider -t basic example example.com
(或者直接寫成scrapy genspider example example.com)
Created spider 'example' using template 'basic' in module:
mybot.spiders.example
#創建基於crawl模板的爬蟲
$ scrapy genspider -t crawl example example.com
shell
運行在終端的工具,用來調試scrapy。
- 語法:
scrapy shell [url]
- 是否需要項目: no
以給定的URL(如果給出)或者空(沒有給出URL)啟動Scrapy shell。 查看 Scrapy終端(Scrapy shell)獲取更多信息。
例子:
$ scrapy shell http://www.example.com/some/page.html
[ ... scrapy shell starts ... ]
說明:
如果在使用scrapy shell的時候出現錯誤
ModuleNotFoundError: No module named 'win32api'
解決方案:
在虛擬化環境中安裝pypiwin32
pip install pypiwin32
fetch
- 語法:
scrapy fetch <url>
- 是否需要項目: no
使用Scrapy下載器(downloader)下載給定的URL,並將獲取到的內容送到標准輸出。
該命令以spider下載頁面的方式獲取頁面。例如,如果spider有 USER_AGENT
屬性修改了 User Agent,該命令將會使用該屬性。
因此,您可以使用該命令來查看spider如何獲取某個特定頁面。
該命令如果非項目中運行則會使用默認Scrapy downloader設定。
例子:
$ scrapy fetch --nolog http://www.example.com/some/page.html
[ ... html content here ... ]
$ scrapy fetch --nolog --headers http://www.example.com/
{'Accept-Ranges': ['bytes'],
'Age': ['1263 '],
'Connection': ['close '],
'Content-Length': ['596'],
'Content-Type': ['text/html; charset=UTF-8'],
'Date': ['Wed, 18 Aug 2010 23:59:46 GMT'],
'Etag': ['"573c1-254-48c9c87349680"'],
'Last-Modified': ['Fri, 30 Jul 2010 15:30:18 GMT'],
'Server': ['Apache/2.2.3 (CentOS)']}
parse
- 語法:
scrapy parse <url> [options]
- 是否需要項目: yes
獲取給定的URL並使用相應的spider分析處理。如果您提供 --callback
選項,則使用spider的該方法處理,否則使用 parse
。
支持的選項:
--spider=SPIDER
: 跳過自動檢測spider並強制使用特定的spider--a NAME=VALUE
: 設置spider的參數(可能被重復)--callback
or-c
: spider中用於解析返回(response)的回調函數--pipelines
: 在pipeline中處理item--rules
or-r
: 使用CrawlSpider
規則來發現用來解析返回(response)的回調函數--noitems
: 不顯示爬取到的item--nolinks
: 不顯示提取到的鏈接--nocolour
: 避免使用pygments對輸出着色--depth
or-d
: 指定跟進鏈接請求的層次數(默認: 1)--verbose
or-v
: 顯示每個請求的詳細信息
例子:
$ scrapy parse http://www.example.com/ -c parse_item
[ ... scrapy log lines crawling example.com spider ... ]
>>> STATUS DEPTH LEVEL 1 <<<
# Scraped Items ------------------------------------------------------------
[{'name': u'Example item',
'category': u'Furniture',
'length': u'12 cm'}]
# Requests -----------------------------------------------------------------
[]
crawl
- 語法:
scrapy crawl <spider>
- 是否需要項目: yes
運行。使用spider進行爬取。
例子:
$ scrapy crawl myspider
[ ... myspider starts crawling ... ]
check
- 語法:
scrapy check [-l] <spider>
- 是否需要項目: yes
運行contract檢查。
例子:
$ scrapy check -l
first_spider
* parse
* parse_item
second_spider
* parse
* parse_item
$ scrapy check
[FAILED] first_spider:parse_item
>>> 'RetailPricex' field is missing
[FAILED] first_spider:parse
>>> Returned 92 requests, expected 0..4
list
- 語法:
scrapy list
- 是否需要項目: yes
列出當前項目中所有可用的spider。每行輸出一個spider。
使用例子:
$ scrapy list
spider1
spider2
view
- 語法:
scrapy view <url>
- 是否需要項目: no
在瀏覽器中打開給定的URL,並以Scrapy spider獲取到的形式展現。 有些時候spider獲取到的頁面和普通用戶看到的並不相同。 因此該命令可以用來檢查spider所獲取到的頁面,並確認這是您所期望的。
例子:
$ scrapy view http://www.example.com/some/page.html
[ ... browser starts ... ]
settings
- 語法:
scrapy settings [options]
- 是否需要項目: no
獲取Scrapy的設定
在項目中運行時,該命令將會輸出項目的設定值,否則輸出Scrapy默認設定。
例子:
$ scrapy settings --get BOT_NAME
scrapybot
$ scrapy settings --get DOWNLOAD_DELAY
0
runspider
- 語法:
scrapy runspider <spider_file.py>
- 是否需要項目: no
在未創建項目的情況下,運行一個編寫在Python文件中的spider。
例子:
$ scrapy runspider myspider.py
[ ... spider starts crawling ... ]
version
- 語法:
scrapy version [-v]
- 是否需要項目: no
輸出Scrapy版本。配合 -v
運行時,該命令同時輸出Python, Twisted以及平台的信息,方便bug提交。
deploy
0.11 新版功能.
- 語法:
scrapy deploy [ <target:project> | -l <target> | -L ]
- 是否需要項目: yes
將項目部署到Scrapyd服務。(后續介紹)
案例分析
運行在終端的工具,用來調試scrapy
案例:爬取千峰官網上千峰教育網頁內容
scrapy shell "http://www.mobiletrain.org/?f=1kcom"
會進入到scrapy終端
[s] view(response) View response in a browser
此時會有一個response對象
response對象:
屬性:
text: 字符串格式的html
body:二進制格式的html
url: 當前請求的url
status: 狀態碼
方法:
xpath查找課程:
>>> response.xpath('//div[@class="index-video-box"]/ul/li/a/div/span/text()').extract()
['HTML基礎視頻教程', 'Linux雲主機實戰視頻教程', 'Java編程基礎講解視頻教程', '全棧測試全套技術視頻教程', 'PHP千萬級秒殺項視頻教程']
通過css查找課程:
>>> response.css(".index-video-box .fl::text").extract()
['HTML基礎視頻教程', 'Linux雲主機實戰視頻教程', 'Java編程基礎講解視頻教程', '全棧測試全套技術視頻教程', 'PHP千萬級秒殺項視頻教程']
那么如何在scrapy shell中給請求配置請求頭呢?如下:
$ scrapy shell ( -s USER_AGENT='Mozilla/5.0' )
...
>>> from scrapy import Request
>>> req = Request('yoururl.com', headers={"header1":"value1"})
>>> fetch(req)