Python 爬蟲(chóng)學(xué)習(xí)筆記之多線程爬蟲(chóng)
XPath 的安裝以及使用
1 . XPath 的介紹
剛學(xué)過(guò)正則表達(dá)式,用的正順手,現(xiàn)在就把正則表達(dá)式替換掉,使用 XPath,有人表示這太坑爹了,早知道剛上來(lái)就學(xué)習(xí) XPath 多省事 啊。其實(shí)我個(gè)人認(rèn)為學(xué)習(xí)一下正則表達(dá)式是大有益處的,之所以換成 XPath ,我個(gè)人認(rèn)為是因?yàn)樗ㄎ桓鼫?zhǔn)確,使用更加便捷。可能有的人對(duì) XPath 和正則表達(dá)式的區(qū)別不太清楚,舉個(gè)例子來(lái)說(shuō)吧,用正則表達(dá)式提取我們的內(nèi)容,就好比說(shuō)一個(gè)人想去天安門(mén),地址的描述是左邊有一個(gè)圓形建筑,右邊是一個(gè)方形建筑,你去找吧,而使用 XPath 的話,地址的描述就變成了天安門(mén)的具體地址。怎么樣?相比之下,哪種方式效率更高,找的更準(zhǔn)確呢?
2 . XPath 的安裝
XPath 包含在 lxml 庫(kù)中,那么我們到哪里去下載呢? 點(diǎn)擊此處 ,進(jìn)入網(wǎng)頁(yè)后按住 ctrl+f 搜索 lxml ,然后進(jìn)行下載,下載完畢之后將文件拓展名改為 .zip ,然后進(jìn)行解壓,將名為 lxml 的文件夾復(fù)制粘貼到 Python 的 Lib 目錄下,這樣就安裝完畢了。
3 . XPath 的使用
為了方便演示,我利用 Html 寫(xiě)了個(gè)簡(jiǎn)單的網(wǎng)頁(yè),代碼如下所示(為了節(jié)省時(shí)間,方便小伙伴們直接進(jìn)行測(cè)試,可直接復(fù)制粘貼我的代碼)
<!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 >百度一下</a>
<a >好123</a>
</div>
</div>
</body></html>
用谷歌瀏覽器打開(kāi)這個(gè)網(wǎng)頁(yè),然后右擊,選擇檢查,會(huì)出現(xiàn)如下所示界面

這個(gè)時(shí)候你鼠標(biāo)右擊任何一行 html 代碼,都可以看到一個(gè) Copy,將鼠標(biāo)放上去,就可以看到 Copy XPath ,先復(fù)制下來(lái),怎么用呢?
# 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
看看打印結(jié)果
like one like two like three
很顯然,將我們想要的內(nèi)容打印下來(lái)了,注意我們?cè)?xpath() 中使用了 text() 函數(shù),這個(gè)函數(shù)就是獲取其中的內(nèi)容,但是如果我們想獲取一個(gè)屬性,該怎么辦?比如說(shuō)我們想得到 html 中的兩個(gè)鏈接地址,也就是 href 屬性,我們可以這么操作
content = selector.xpath('//*[@id="url"]/a/@href')
for each in content:
print each
這個(gè)時(shí)候的打印結(jié)果就是
http://www.baidu.com http://www.hao123.com
看到現(xiàn)在大家大概也就對(duì) xpath() 中的符號(hào)有了一定的了解,比如一開(kāi)始的 // 指的就是根目錄,而 / 就是父節(jié)點(diǎn)下的子節(jié)點(diǎn),其他的 id 屬性也是一步一步從上往下尋找的,由于這是一種樹(shù)結(jié)構(gòu),所以也難怪方法的名字為 etree()。
4 . XPath 的特殊用法
<!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>
面對(duì)上面的一個(gè)網(wǎng)頁(yè),我們應(yīng)該如何獲取到三行的內(nèi)容的 ? 嗯哼,很簡(jiǎn)單,我寫(xiě)三個(gè) XPath 語(yǔ)句不就好了,so easy 。 如果真是這樣,那么我們的效率好像是太低了一點(diǎn),仔細(xì)看看這三行 div 的 id 屬性,好像前四個(gè)字母都是 like, 那就好辦了,我們可以使用 starts-with 對(duì)這三行進(jìn)行同時(shí)提取,如下所示
content = selector.xpath('//div[starts-with(@id,"like")]/text()')
不過(guò)這樣有一點(diǎn)麻煩的地方,我們就需要手動(dòng)的去寫(xiě) XPath 路徑了,當(dāng)然也可以復(fù)制粘貼下來(lái)在進(jìn)行修改,這就是提升復(fù)雜度來(lái)?yè)Q取效率的問(wèn)題了。再來(lái)看看標(biāo)簽嵌套標(biāo)簽的提取情況
<!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>
像上面這樣的一個(gè)網(wǎng)頁(yè),如果我們想獲取到 hello world Python 語(yǔ)句,該怎么獲取呢?很明顯這是一種標(biāo)簽嵌套標(biāo)簽的情況,我們按照正常情況進(jìn)行提取,看看結(jié)果如何
content = selector.xpath('//*[@id="text"]/p/text()')
for each in content:
print each
運(yùn)行之后,很遺憾的,只打印出了 hello 字樣,其他字符丟失了,該怎么辦呢?這種情況可以借助于 string(.)如下所示
content = selector.xpath('//*[@id="text"]/p')[0]
info = content.xpath('string(.)')
data = info.replace('\n','').replace(' ','')
print data
這樣就可以打印出正確內(nèi)容了,至于第三行為什么存在,你可以將其去掉看看結(jié)果,到時(shí)候你自然就明白了。
Python 并行化的簡(jiǎn)單介紹
有人說(shuō) Python 中的并行化并不是真正的并行化,但是多線程還是能夠顯著提高我們代碼的執(zhí)行效率,為我們節(jié)省下來(lái)一大筆時(shí)間,下面我們就針對(duì)單線程和多線程進(jìn)行時(shí)間上的比較。
# 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)
# 單線程計(jì)時(shí)
time1 = time.time()
for i in urls:
print i
getsource(i)
time2 = time.time()
print '單線程耗時(shí) : ' + str(time2 - time1) + ' s'
# 多線程計(jì)時(shí)
pool = ThreadPool(4)
time3 = time.time()
results = pool.map(getsource, urls)
pool.close()
pool.join()
time4 = time.time()
print '多線程耗時(shí) : ' + str(time4 - time3) + ' s'
打印結(jié)果為
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 單線程耗時(shí) : 7.26399993896 s 多線程耗時(shí) : 2.49799990654 s
至于以上鏈接為什么設(shè)置間隔為 50,是因?yàn)槲野l(fā)現(xiàn)在百度貼吧上沒(méi)翻一頁(yè),pn 的值就會(huì)增加 50。 通過(guò)以上結(jié)果我們發(fā)現(xiàn),多線程相比于單線程效率提升了太多太多。至于以上代碼中多線程的使用,我就不再過(guò)多講解,我相信只要接觸過(guò) Java 的人對(duì)多線程的使用不會(huì)陌生,其實(shí)都是大差不差。沒(méi)有接觸過(guò) Java ?那就對(duì)不起了,以上代碼請(qǐng)自行消化吧。
實(shí)戰(zhàn) -- 爬取當(dāng)當(dāng)網(wǎng)書(shū)籍信息
一直以來(lái)都在當(dāng)當(dāng)網(wǎng)購(gòu)買(mǎi)書(shū)籍,既然學(xué)會(huì)了如何利用 Python 爬取信息,那么首先就來(lái)爬取一下當(dāng)當(dāng)網(wǎng)中的書(shū)籍信息吧。本實(shí)戰(zhàn)完成之后的內(nèi)容如下所示

在當(dāng)當(dāng)網(wǎng)中搜索 Java ,出現(xiàn)了89頁(yè)內(nèi)容,我選擇爬取了前 80 頁(yè),而且為了比較多線程和單線程的效率,我特意在這里對(duì)二者進(jìn)行了比較,其中單線程爬取所用時(shí)間為 67s,而多線程僅為 15s 。
如何爬取網(wǎng)頁(yè),在上面 XPath 的使用中我們也已經(jīng)做了介紹,無(wú)非就是進(jìn)入網(wǎng)頁(yè),右擊選擇檢查,查看網(wǎng)頁(yè) html 代碼,然后尋找規(guī)律,進(jìn)行信息的提取,在這里就不在多介紹,由于代碼比較短,所以在這里直接上源代碼。
# 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 '多線程耗時(shí) : ' + str(time2 - time1) + 's'
# time1 = time.time()
# for each in urls:
# spider(each)
# time2 = time.time()
# f.close()
# print '單線程耗時(shí) : ' + str(time2 - time1) + 's'
可見(jiàn),以上代碼中的知識(shí),我們都在介紹 XPath 和 并行化 中做了詳細(xì)的介紹,所以閱讀起來(lái)十分輕松。
好了,到今天為止,Python 爬蟲(chóng)相關(guān)系列的文章到此結(jié)束,謝謝你的觀看。
相關(guān)文章
Pycharm生成可執(zhí)行文件.exe的實(shí)現(xiàn)方法
這篇文章主要介紹了Pycharm生成可執(zhí)行文件.exe的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
詳解pyqt5 動(dòng)畫(huà)在QThread線程中無(wú)法運(yùn)行問(wèn)題
這篇文章主要介紹了詳解pyqt5 動(dòng)畫(huà)在QThread線程中無(wú)法運(yùn)行問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
一文學(xué)會(huì)利用python解決文章付費(fèi)限制問(wèn)題
本篇文章主要介紹利用Python爬蟲(chóng)爬取付費(fèi)文章,適合練習(xí)爬蟲(chóng)基礎(chǔ)同學(xué),文中描述和代碼示例很詳細(xì),干貨滿滿,感興趣的小伙伴快來(lái)一起學(xué)習(xí)吧2023-05-05
Python?"手繪風(fēng)格"數(shù)據(jù)可視化方法實(shí)例匯總
這篇文章主要給大家介紹了關(guān)于Python?"手繪風(fēng)格"數(shù)據(jù)可視化方法實(shí)現(xiàn)的相關(guān)資料,本文分別給大家?guī)?lái)了Python-matplotlib手繪風(fēng)格圖表繪制、Python-cutecharts手繪風(fēng)格圖表繪制以及Python-py-roughviz手繪風(fēng)格圖表繪制,需要的朋友可以參考下2022-02-02
Python基于pygame實(shí)現(xiàn)的彈力球效果(附源碼)
這篇文章主要介紹了Python基于pygame實(shí)現(xiàn)的彈力球效果,涉及pygame圖形動(dòng)態(tài)操作的相關(guān)的技巧,并附帶了完整的源碼供讀者下載參考,需要的朋友可以參考下2015-11-11
Python在Excel中添加數(shù)據(jù)條的代碼詳解
在Excel中添加數(shù)據(jù)條是一種數(shù)據(jù)可視化技巧,它通過(guò)條形圖的形式在單元格內(nèi)直觀展示數(shù)值的大小,尤其適合比較同一列或行中各個(gè)單元格的數(shù)值,本文將介紹如何使用Python在Excel中的指定單元格區(qū)域添加數(shù)據(jù)條,需要的朋友可以參考下2024-10-10
Python實(shí)現(xiàn)爬取亞馬遜數(shù)據(jù)并打印出Excel文件操作示例
這篇文章主要介紹了Python實(shí)現(xiàn)爬取亞馬遜數(shù)據(jù)并打印出Excel文件操作,結(jié)合實(shí)例形式分析了Python針對(duì)亞馬遜圖書(shū)數(shù)據(jù)的爬取操作,以及數(shù)據(jù)打印輸出Excel相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05

