爬蟲的抓取方式有好幾種,正則表達式,Lxml(xpath)與Beautiful,我在網上查了一下資料,瞭解到三者之間的使用難度與性能html
三種爬蟲方式的對比。python
抓取方式 | 性能 | 使用難度 |
---|---|---|
正則表達式 | 快 | 困難 |
Lxml | 快 | 簡單 |
BeautifulSoup | 慢 | 簡單 |
這樣一比較我我選擇了Lxml(xpath)的方式了,雖然有三種方式,但確定是要選擇最好的方式來爬蟲,這個道理你們都懂,另外有興趣的朋友也能夠去了解另外兩種爬蟲方式!正則表達式
好了如今來說講xpath瀏覽器
因爲Xpath屬於lxml模塊,因此首先須要安裝lxml庫,老辦法直接在file-->setting---project interpreter 一鍵添加lxml庫。scrapy
from lxml import etreeide
s=etree.HTML(源碼) #將源碼轉化爲能被XPath匹配的格式性能
s.xpath(xpath表達式) #返回爲一列表,url
- // 雙斜槓 定位根節點,會對全文進行掃描,在文檔中選取全部符合條件的內容,以列表的形式返回。
- / 單斜槓 尋找當前標籤路徑的下一層路徑標籤或者對當前路標籤內容進行操做
- /text() 獲取當前路徑下的文本內容
- /@xxxx 提取當前路徑下標籤的屬性值
- | 可選符 使用|可選取若干個路徑 如//p | //div 即在當前路徑下選取全部符合條件的p標籤和div標籤。
- . 點 用來選取當前節點
- .. 雙點 選取當前節點的父節點
學以至用,方能讓咱們能快速掌握xpath語法功能。spa
咱們此次須要爬取豆瓣音樂前250條3d
打開豆瓣音樂:https://music.douban.com/top250
右鍵彈出菜單欄 Copy==> Copy Xpath
這裏咱們想獲取音樂標題,音樂標題的xpath是:xpath://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a') print title
運行代碼:
竟然是空的。!!!
這裏須要注意一下,瀏覽器複製的xpath只能做參考,由於瀏覽器常常會在本身裏面增長多餘的tbody標籤,咱們須要手動把這個標籤刪除
刪除中間的/tbody後,是這樣的,title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a')
而後咱們再運行代碼。
獲得:<Element a at 0x53d26c8>
說明標題被獲取到了。
由於要獲取標題文本,因此xpath表達式要追加/text()title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text()
又由於這個s.xpath返回的是一個集合,且集合中只有一個元素因此我再追加一個[0]
新的表達式:title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text(),再追加[0]
從新運行獲得結果:
We Sing. We Dance. We Steal Things.
正是咱們想要的標題。
老辦法,先用右鍵copy評分的xpath ://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/div/span[2]
複製評價人數的xpath://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/div/span[3]/text()
一樣的咱們要把tbody去掉,而後從新運行代碼:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() print title,score,numbers
獲得:
We Sing. We Dance. We Steal Things. 9.1 ( 100395人評價 )
copy標題的xpath,://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a
想獲取音樂鏈接href這裏須要,獲取這個標籤屬於,/@xxx能夠提取當前路徑標籤下的屬性值//*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[2]/div/a/@href
代碼:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) href = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/@href')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() print href,title,score,numbers
運行代碼獲得:
https://music.douban.com/subject/2995812/ We Sing. We Dance. We Steal Things. 9.1 ( 100395人評價 )
找到圖片,複製他的xpath地址://*[@id="content"]/div/div[1]/div/table[1]/tbody/tr/td[1]/a/img
運行代碼:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) href = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/@href')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() score = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[2]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/div/span[3]/text()')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() imgpath = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[1]/a/img/@src')[0]#由於要獲取文本,因此我須要這個當前路徑下的文本,因此使用/text() print href,title,score,numbers,imgpath
老套路:
獲得結果:
https://music.douban.com/subject/2995812/ We Sing. We Dance. We Steal Things. 9.1 ( 100395人評價 ) https://img3.doubanio.com/spic/s2967252.jpg
可是這只是獲取了一條數據,若是獲取多條數據呢?
咱們再看第二條數據,第三條數據,第四條數據
獲得他們的xpath:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) title = s.xpath('//*[@id="content"]/div/div[1]/div/table[1]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() title2 = s.xpath('//*[@id="content"]/div/div[1]/div/table[2]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() title3 = s.xpath('//*[@id="content"]/div/div[1]/div/table[3]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() title4 = s.xpath('//*[@id="content"]/div/div[1]/div/table[4]/tr/td[2]/div/a/text()')[0]#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() print title,title2,title3,title4
獲得:
We Sing. We Dance. We Steal Things. Viva La Vida 華麗的冒險 范特西
對比他們的xpath,發現只有table序號不同,咱們能夠就去掉序號,獲得通用的xpath信息:
運行代碼:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()')#由於要獲取標題,因此我須要這個當前路徑下的文本,因此使用/text() for title in titles: print title.strip()
獲得:
We Sing. We Dance. We Steal Things. Viva La Vida 華麗的冒險 范特西 後。青春期的詩 是時候 Lenka Start from Here 旅行的意義 太陽 Once (Soundtrack) Not Going Anywhere American Idiot OK 無與倫比的美麗 親愛的...我還不知道 城市 O Wake Me Up When September Ends 葉惠美 七里香 21 My Life Will... 寓言 你在煩惱什麼
其它的信息如:連接地址,評分,評價人數均可以用一樣的辦法來獲取,如今我同時獲取多條數據,由於每頁數據是25條,因此:
完整代碼以下:
# coding:utf-8 from lxml import etree import requests url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) hrefs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/@href') titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()') scores = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[2]/text()') numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[3]/text()') imgs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[1]/a/img/@src') for i in range(25): print hrefs[i],titles[i],scores[i],numbers[i],imgs[i]
獲得:
一大批數據了,我就不展現了。有興趣能夠直接copy代碼運行.,注意你得裝上lxml與requests庫.
咱們也發現了問題每個xpath路徑特別長,能不能精簡一下呢?
#####5. 精簡一下xpath路徑
hrefs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/@href') titles = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/a/text()') scores = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[2]/text()') numbers = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[2]/div/div/span[3]/text()') imgs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr/td[1]/a/img/@src')
觀察發現獲取幾個關鍵字段的xpath前綴都是 //*[@id="content"]/div/div[1]/div/table/tr
那我能不能把這些東西提出來呢,讓後面的不一樣的本身去追加,另外這樣寫也不用管每一個頁面到底有多少條數據,只管查就好了。因此代碼作了一下精簡。
url = 'https://music.douban.com/top250' html = requests.get(url).text s = etree.HTML(html) trs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr') #先提取tr以前的節點集合 for tr in trs: #遍歷tr href = tr.xpath('./td[2]/div/a/@href')[0] #注意新節點是tr下的節點 title = tr.xpath('./td[2]/div/a/text()')[0] score = tr.xpath('./td[2]/div/div/span[2]/text()')[0] number = tr.xpath('./td[2]/div/div/span[3]/text()')[0] img = tr.xpath('./td[1]/a/img/@src')[0] print href,title,score,number,img
獲得的結果和以前是同樣的。
可是,可是,這只是一個頁面的數據,我如今想爬取多個頁面的數據,怎麼辦呢?
觀察一下翻頁路徑:
https://music.douban.com/top250?start=0
https://music.douban.com/top250?start=25
https://music.douban.com/top250?start=50
有沒有發現頁面只是後面start參數發生了改變,且增加爲每次25,而且250條數據正好是10頁。
因此我能夠遍歷這個頁面。
代碼:
for i in range(10): url = 'https://music.douban.com/top250?start={}'.format(i*25) print url
獲得:
https://music.douban.com/top250?start=0 https://music.douban.com/top250?start=25 https://music.douban.com/top250?start=50 https://music.douban.com/top250?start=75 https://music.douban.com/top250?start=100 https://music.douban.com/top250?start=125 https://music.douban.com/top250?start=150 https://music.douban.com/top250?start=175 https://music.douban.com/top250?start=200 https://music.douban.com/top250?start=225
正是本身要的結果。
好了最後咱們把代碼拼裝在一塊兒,並注意每一個方法的用途。
# coding:utf-8 from lxml import etree import requests #獲取頁面地址 def getUrl(): for i in range(10): url = 'https://music.douban.com/top250?start={}'.format(i*25) scrapyPage(url) #爬取每頁數據 def scrapyPage(url): html = requests.get(url).text s = etree.HTML(html) trs = s.xpath('//*[@id="content"]/div/div[1]/div/table/tr') for tr in trs: href = tr.xpath('./td[2]/div/a/@href')[0] title = tr.xpath('./td[2]/div/a/text()')[0] score = tr.xpath('./td[2]/div/div/span[2]/text()')[0] number = tr.xpath('./td[2]/div/div/span[3]/text()')[0] img = tr.xpath('./td[1]/a/img/@src')[0] print href, title, score, number, img if '__main__': getUrl()