XPath 的安裝以及使用
1 . XPath 的介紹
剛學過正則表達式,用的正順手,現在就把正則表達式替換掉,使用 XPath,有人表示這太坑爹了,早知道剛上來就學習 XPath 多省事 啊。其實我個人認為學習一下正則表達式是大有益處的,之所以換成 XPath ,我個人認為是因為它定位更準確,使用更加便捷。可能有的人對 XPath 和正則表達式的區別不太清楚,舉個例子來說吧,用正則表達式提取我們的內容,就好比說一個人想去天安門,地址的描述是左邊有一個圓形建筑,右邊是一個方形建筑,你去找吧,而使用 XPath 的話,地址的描述就變成了天安門的具體地址。怎么樣?相比之下,哪種方式效率更高,找的更準確呢?
2 . XPath 的安裝
XPath 包含在 lxml 庫中,那么我們到哪里去下載呢? 點擊此處 ,進入網頁后按住 ctrl+f 搜索 lxml ,然后進行下載,下載完畢之后將文件拓展名改為 .zip ,然后進行解壓,將名為 lxml 的文件夾復制粘貼到 Python 的 Lib 目錄下,這樣就安裝完畢了。
3 . XPath 的使用
為了方便演示,我利用 Html 寫了個簡單的網頁,代碼如下所示(為了節省時間,方便小伙伴們直接進行測試,可直接復制粘貼我的代碼)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < title >Test Html</ title > </ head > < body > < div id = "content" > < ul id = "like" > < li >like one</ li > < li >like two</ li > < li >like three</ li > </ ul > < ul id = "hate" > < li >hate one</ li > < li >hate two</ li > < li >hate three</ li > </ ul > < div id = "url" > < a href = "http://www.baidu.com" >百度一下</ a > < a href = "http://www.hao123.com" >好123</ a > </ div > </ div > </ body ></ html > |
用谷歌瀏覽器打開這個網頁,然后右擊,選擇檢查,會出現如下所示界面
這個時候你鼠標右擊任何一行 html 代碼,都可以看到一個 Copy,將鼠標放上去,就可以看到 Copy XPath ,先復制下來,怎么用呢?
1
2
3
4
5
6
7
8
9
10
11
|
# coding=utf-8 from lxml import etree f = open ( 'myHtml.html' , 'r' ) html = f.read() f.close() selector = etree.HTML(html) content = selector.xpath( '//*[@id="like"]/li/text()' ) for each in content: print each |
看看打印結果
1
2
3
|
like one like two like three |
很顯然,將我們想要的內容打印下來了,注意我們在 xpath() 中使用了 text() 函數,這個函數就是獲取其中的內容,但是如果我們想獲取一個屬性,該怎么辦?比如說我們想得到 html 中的兩個鏈接地址,也就是 href 屬性,我們可以這么操作
1
2
3
|
content = selector.xpath( '//*[@id="url"]/a/@href' ) for each in content: print each |
這個時候的打印結果就是
1
2
|
http://www.baidu.com http://www.hao123.com |
看到現在大家大概也就對 xpath() 中的符號有了一定的了解,比如一開始的 // 指的就是根目錄,而 / 就是父節點下的子節點,其他的 id 屬性也是一步一步從上往下尋找的,由于這是一種樹結構,所以也難怪方法的名字為 etree()。
4 . XPath 的特殊用法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < title >Title</ title > </ head > < body > < div id = "likeone" >like one</ div > < div id = "liketwo" >like two</ div > < div id = "likethree" >like three</ div > </ body > </ html > |
面對上面的一個網頁,我們應該如何獲取到三行的內容的 ? 嗯哼,很簡單,我寫三個 XPath 語句不就好了,so easy 。 如果真是這樣,那么我們的效率好像是太低了一點,仔細看看這三行 div 的 id 屬性,好像前四個字母都是 like, 那就好辦了,我們可以使用 starts-with 對這三行進行同時提取,如下所示
content = selector.xpath('//div[starts-with(@id,"like")]/text()')
不過這樣有一點麻煩的地方,我們就需要手動的去寫 XPath 路徑了,當然也可以復制粘貼下來在進行修改,這就是提升復雜度來換取效率的問題了。再來看看標簽嵌套標簽的提取情況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < title >Title</ title > </ head > < body > < div id = "content" > < div id = "text" > < p >hello < b > world < font color = "#ffe4c4" > Python </ font > </ b > </ p > </ div > </ div > </ body > </ html > |
像上面這樣的一個網頁,如果我們想獲取到 hello world Python 語句,該怎么獲取呢?很明顯這是一種標簽嵌套標簽的情況,我們按照正常情況進行提取,看看結果如何
1
2
3
|
content = selector.xpath( '//*[@id="text"]/p/text()' ) for each in content: print each |
運行之后,很遺憾的,只打印出了 hello 字樣,其他字符丟失了,該怎么辦呢?這種情況可以借助于 string(.)如下所示
1
2
3
4
|
content = selector.xpath( '//*[@id="text"]/p' )[ 0 ] info = content.xpath( 'string(.)' ) data = info.replace( '\n' ,' ').replace(' ',' ') print data |
這樣就可以打印出正確內容了,至于第三行為什么存在,你可以將其去掉看看結果,到時候你自然就明白了。
Python 并行化的簡單介紹
有人說 Python 中的并行化并不是真正的并行化,但是多線程還是能夠顯著提高我們代碼的執行效率,為我們節省下來一大筆時間,下面我們就針對單線程和多線程進行時間上的比較。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
# coding=utf-8 import requests from multiprocessing.dummy import Pool as ThreadPool import time def getsource(url): html = requests.get(url) if __name__ = = '__main__' : urls = [] for i in range ( 50 , 500 , 50 ): newpage = 'http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=' + str (i) urls.append(newpage) # 單線程計時 time1 = time.time() for i in urls: print i getsource(i) time2 = time.time() print '單線程耗時 : ' + str (time2 - time1) + ' s' # 多線程計時 pool = ThreadPool( 4 ) time3 = time.time() results = pool. map (getsource, urls) pool.close() pool.join() time4 = time.time() print '多線程耗時 : ' + str (time4 - time3) + ' s' |
打印結果為
1
2
3
4
5
6
7
8
9
10
11
|
http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=50 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=100 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=150 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=200 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=250 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=300 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=350 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=400 http://tieba.baidu.com/f?kw=python&ie=utf-8&pn=450 單線程耗時 : 7.26399993896 s 多線程耗時 : 2.49799990654 s |
至于以上鏈接為什么設置間隔為 50,是因為我發現在百度貼吧上沒翻一頁,pn 的值就會增加 50。 通過以上結果我們發現,多線程相比于單線程效率提升了太多太多。至于以上代碼中多線程的使用,我就不再過多講解,我相信只要接觸過 Java 的人對多線程的使用不會陌生,其實都是大差不差。沒有接觸過 Java ?那就對不起了,以上代碼請自行消化吧。
實戰 -- 爬取當當網書籍信息
一直以來都在當當網購買書籍,既然學會了如何利用 Python 爬取信息,那么首先就來爬取一下當當網中的書籍信息吧。本實戰完成之后的內容如下所示
在當當網中搜索 Java ,出現了89頁內容,我選擇爬取了前 80 頁,而且為了比較多線程和單線程的效率,我特意在這里對二者進行了比較,其中單線程爬取所用時間為 67s,而多線程僅為 15s 。
如何爬取網頁,在上面 XPath 的使用中我們也已經做了介紹,無非就是進入網頁,右擊選擇檢查,查看網頁 html 代碼,然后尋找規律,進行信息的提取,在這里就不在多介紹,由于代碼比較短,所以在這里直接上源代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# coding=utf8 import requests import re import time from lxml import etree from multiprocessing.dummy import Pool as ThreadPool import sys reload (sys) sys.setdefaultencoding( 'utf-8' ) def changepage(url, total): urls = [] nowpage = int (re.search( '(\d+)' , url, re.S).group( 1 )) for i in range (nowpage, total + 1 ): link = re.sub( 'page_index=(\d+)' , 'page_index=%s' % i, url, re.S) urls.append(link) return urls def spider(url): html = requests.get(url) content = html.text selector = etree.HTML(content) title = [] title = selector.xpath( '//*[@id="component_0__0__6612"]/li/a/@title' ) detail = [] detail = selector.xpath( '//*[@id="component_0__0__6612"]/li/p[3]/span[1]/text()' ) saveinfo(title,detail) def saveinfo(title, detail): length1 = len (title) for i in range ( 0 , length1 - 1 ): f.writelines(title[i] + '\n' ) f.writelines(detail[i] + '\n\n' ) if __name__ = = '__main__' : pool = ThreadPool( 4 ) f = open ( 'info.txt' , 'a' ) url = 'http://search.dangdang.com/?key=Java&act=input&page_index=1' urls = changepage(url, 80 ) time1 = time.time() pool. map (spider, urls) pool.close() pool.join() f.close() print '爬取成功!' time2 = time.time() print '多線程耗時 : ' + str (time2 - time1) + 's' # time1 = time.time() # for each in urls: # spider(each) # time2 = time.time() # f.close() # print '單線程耗時 : ' + str(time2 - time1) + 's' |
可見,以上代碼中的知識,我們都在介紹 XPath 和 并行化 中做了詳細的介紹,所以閱讀起來十分輕松。
好了,到今天為止,Python 爬蟲相關系列的文章到此結束,謝謝你的觀看。