日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

XPath与多线程爬虫

發布時間:2023/11/27 生活经验 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 XPath与多线程爬虫 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.??Xpath的介紹與配置

1.??????XPath是什么

XPath是一門語言

XPath可以在XML文檔中查找信息

XPath支持HTML

XPath通過元素和屬性進行導航

總結:

XPath可以用來提取信息(和正則表達式類似)

XPath比正則表達式更加厲害

XPath比正則表達式更加的簡單

如果你之前用正則表達式進行開發,很多時候,明明感覺自己的匹配是正確的,但是就是找不到自己想要的內容,還有時候就是,網頁特別復雜,網頁的結構層次也十分復雜,你不知道該如何匹配,當你認真的學習了XPath之后,這些問題就會迎刃而解。

2.??????如何安裝使用XPath

XPath屬于lxml庫,所以首先我們需要安裝這個庫,這個庫的具體安裝步驟我已經寫在我的博客里面了大家可自行翻閱。

安裝好之后我們將使用from lxml import etree和Selector = etree.HTML(網頁源代碼)和Selector.xpath(一段神奇的代碼)

二.??神器XPath的使用

1.??????XPath與HTML結構

HTML是樹狀結構,他可以逐層展開,我們利用這一特點結合XPath就可逐層定位

???????? ?? 下面請看一個小的頁面,我們接下來的測試用例會講到這個頁面。

用chrome打開這個網頁,然后打開檢查,如下界面(這里我把全部的信息顯示出來,便于分析檢查)


2.??????獲取網頁元素的XPath

手動分析法

這里我們分析一下,如果我們需要找到“這是第一條信息”我們通過下面的方式去查找

?html->body->div->ul[@useful]->li

補充說明:上面的這段內容肯定不是XPath的代碼,這里只是一種形象的表示方法。大家查看上圖可以發現ul標簽有兩個內容,這里我們選擇的是id等于useful的標簽,所以上面的形象查找部分是ul[@useful],另外就是這里我想要查找的是“這是第一條信息”但是我上面的只有li,大家很容易就明白了,這里返回的應該是一個列表,列表的內容包括了三條信息,“這是第一條信息,這是第二條信息,這是第三條信息”

?

Chrome分析法

手動分析法,在結構比較復雜的網頁中分析起來還是比較麻煩,這里我們可以使用chrome分析法,準確快速的定位。我們右擊頁面,點擊檢查,或者審查元素,彈出他的代碼結構,然后在我們感興趣的內容對應的代碼上面右擊,選擇Copy,然后選擇Copy XPath,把它復制下來,我把剛剛那段感興趣的內容粘貼下來,大家和上面的手動分析比較一下

//*[@id="useful"]/li[1]

是不是和咱們手動分析的內容有幾分類似呢。分析一下chrome給我們的代碼,這里有一個*號,而我們手動分析的代碼是html,body…這是因為id=useful的這個id只有一個內容,所以我們這里用*進行了省略,如果我們在其他的標簽下面也有id=useful這樣的內容,是需要像我們手動分析的那樣的類似的內容的。代碼中li[1]就表示我們的列表里面的第一段內容了,這里就不會出現我們手動分析中的把三段內容都抓取下來的結果了。

3.??????應用XPath提取內容

//定位根節點

/往下層尋找

提取文本內容:/text()

提取屬性內容:/@xxxx????? (xxxx是屬性的名字)

下面就是代碼部分,具體部分的講解內容和XPath的使用,我已經注釋到代碼部分了,大家可以仔細閱讀:

?

#-*-coding:utf8-*-
from lxml import etree  #導入etree
#下面是一個多行字符串,實際就是一個小網頁的源代碼
html = '''
<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title>測試-常規用法</title>
</head>
<body>
<div id="content"><ul id="useful"><li>這是第一條信息</li><li>這是第二條信息</li><li>這是第三條信息</li></ul><ul id="useless"><li>不需要的信息1</li><li>不需要的信息2</li><li>不需要的信息3</li></ul><div id="url"><a href="http://jikexueyuan.com">極客學院</a><a href="http://jikexueyuan.com/course/" title="極客學院課程庫">點我打開課程庫</a></div>
</div></body>
</html>
'''selector = etree.HTML(html)  #使用etree將多行字符串轉化成XPath可以識別的對象,然后傳遞給selector#提取文本
#運行的結果是打印出了“這是第一條信息,這是第二條信息,這是第三條信息”#運用XPath中的/text()獲取標簽li里面的內容,其中id="useful",限定了ul,而不是所有的ul
#這里如果我們把限定條件給去掉,下一句的代碼變成了content = selector.xpath('//ul/li/text()')
#則打印的結果是“這是第一條信息,這是第二條信息,這是第三條信息,不需要的信息1,不需要的信息2,不需要的信息3”
#因為這里的ul[@id="useful"]是獨一無二的,我們不必擔心,當然,如果我們想保險一點的話,還可以在ul的前面,加上他的上一層標簽
#//div/ul[@id="useful"]/li/text()我們還可以在div的后面添加代碼變成div[@id="content"]
content = selector.xpath('//ul[@id="useful"]/li/text()')
for each in content:print each#提取屬性,下面代碼的運行結果是打印出上面網頁代碼中的兩個鏈接
#這里就使用了上文所講的方式,提取屬性內容:/@xxxx      (xxxx是屬性的名字)
#這里的網頁比較簡單,當我們做其他的操作時,肯定不能像這個一樣,我們就可以像上文一樣給它加限定比如下面的代碼
#//div[@id="url"]/a/@href
link = selector.xpath('//a/@href')
for each in link:print each#下面這段代碼的結果是提取“極客學院課程庫”這些內容,大家可以嘗試一下
title = selector.xpath('//a/@title')
print title[0]


下文的截圖是代碼運行的結果


三.??神奇XPath的特殊用法

1.??????以相同的字符開頭的情況

starts-with(@屬性名字,屬性字符相同部分)

2.??????標簽套標簽

string(.)

下面是代碼詳解

#-*-coding:utf8-*-
from lxml import etree#首先將一下下面代碼的困難之處,在html1中的body標簽下的div標簽里面,有三個id,且三個id不同
#這就是我們需要處理的第一類問題,以相同的字符開頭的情況
html1 = '''
<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><div id="test-1">需要的內容1</div><div id="test-2">需要的內容2</div><div id="testfault">需要的內容3</div>
</body>
</html>
'''#在接下來的html2中出現標簽套標簽
html2 = '''
<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><div id="test3">我左青龍,<span id="tiger">右白虎,<ul>上朱雀,<li>下玄武。</li></ul>老牛在當中,</span>龍頭在胸口。</div>
</body>
</html>
'''selector1 = etree.HTML(html1)
#下面代碼里面//div[starts-with(@id,"test")]/text()'注意講到的一個新用法,這里的意思是,標簽id中以“test”開頭的所有標簽都會被提取出來
#運行的結果是“需要的內容1,需要的內容2,需要的內容3”
content1 = selector1.xpath('//div[starts-with(@id,"test")]/text()')
for each in content1:print each#下面的代碼是標簽套標簽的錯誤使用,就是我們利用上一章節的使用方法
#直接提取div[@id="test3"],運行的結果是只能提取div里面的內容,而div里面嵌套的標簽的內容無法提取
#運行的結果是“我左青龍  龍頭在胸口”,這顯然不是我們想要的內容
selector2 = etree.HTML(html2)
content_2 = selector2.xpath('//div[@id="test3"]/text()')
for each in content_2:print each#下面的內容就是我們提取的完整的內容
#我們這里的思想還是先大后小的思想,我們先提取div下面的所有的內容
#然后直接使用info = data.xpath('string(.)'),但是注意的是這一句執行的結果會把換行,空格都給提取出來
#所以我們需要使用替換,把換行符合空格符全部替換掉
#運行的結果是我左青龍,右白虎,上朱雀,下玄武。老牛在當中,龍頭在胸口。
selector3 = etree.HTML(html2)
data = selector3.xpath('//div[@id="test3"]')[0]
info = data.xpath('string(.)')
content_3 = info.replace('\n','').replace(' ','')
print content_3

四.??Python并行化介紹與演示

1.??????并行化簡單理解

這里可以理解為Python的多線程(這里的多線程不是真正的多線程)

多個線程同時處理任務,提高效率,具有高效和快速的特點

2.??????map使用實現爬蟲并行化

map函數包括序列操作,參數傳遞和結果保存等一系列操作

使用map函數時需要導入Pool這個類,使用代碼:

from multiprocessing.dummy import Pool

根據自己的計算機核數的不同,下面的代碼使用的數字有改動:

pool = Pool(4)

接著使用result = pool.map(爬取函數,網址列表)

下面是代碼詳解:

#-*-coding:utf8-*-#導入map所在的Pool這個類,然后重新命名為ThreadPool
#導入requests抓取網頁源代碼
#導入time計算時間,比較單線程和多線程的時間
from multiprocessing.dummy import Pool as ThreadPool
import requests
import time#定義了一個函數,其作用是獲取傳入的URL的源代碼
def getsource(url):html = requests.get(url)urls = []#下面的這些代碼生成20行網址,這里range函數使用頭但是不使用尾,所以這里傳入的是21
for i in range(1,21):newpage = 'http://tieba.baidu.com/p/3522395718?pn=' + str(i)urls.append(newpage)  #將這二十個網址全部添加到urls這個列表里面time1 = time.time()  #該語句的作用是記下程序運行到這一步的時間
for i in urls:print igetsource(i)
time2 = time.time()  #記下時間2
print u'單線程耗時:' + str(time2-time1)  #兩個時間相減就是上面這段代碼執行完的總時間#使用Python的并行化操作
pool = ThreadPool(4)  #初始化一個實例
time3 = time.time()
results = pool.map(getsource, urls)  #使用getsource和map函數進行爬取
#map的作用就是并行的處理getsource這個函數,然后傳入的是urls的內容
pool.close()
pool.join()
time4 = time.time()
print u'并行耗時:' + str(time4-time3)#運行結果是單線程爬蟲比多線程爬蟲的時間要長的多#這里map只是做一個了解,以后還會講到scrypy



?

五.??實戰—百度貼吧爬蟲

目標網站:http://tieba.baidu.com/p/3522395718

目標內容:跟帖用戶名,跟帖內容,跟帖時間

涉及知識:

????????? requests爬取網頁源代碼

????????? XPath提取內容

????????? map實現多線程爬蟲

請看代碼分析:(這之前你得熟悉想要找的內容,這里建議大家使用chrome的檢查或者審查元素功能)

#-*-coding:utf8-*-
from lxml import etree #需要使用XPath的etree
from multiprocessing.dummy import Pool as ThreadPool   #使用多線程爬蟲
import requests   #抓取網頁的源代碼
import json   #這里導入了json的庫,是因為源代碼中有一部分是用json保存的,這里以后會說到
import sysreload(sys)sys.setdefaultencoding('utf-8')  #上面三行代碼的作用是使內容強制轉化成utf-8,不然會出現亂碼'''重新運行之前請刪除content.txt,因為文件操作使用追加方式,會導致內容太多。'''#定義了一個函數,將內容寫入文本中
def towrite(contentdict):f.writelines(u'回帖時間:' + str(contentdict['topic_reply_time']) + '\n')f.writelines(u'回帖內容:' + unicode(contentdict['topic_reply_content']) + '\n')f.writelines(u'回帖人:' + contentdict['user_name'] + '\n\n')#爬蟲函數
def spider(url):headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}html = requests.get(url,headers = headers)   #獲取網頁源代碼#print htmlselector = etree.HTML(html.text)  #將其轉化成可以使用XPath處理的對象content_field = selector.xpath('//div[@class="l_post l_post_bright "]') #獲取一整個div,這里使用了先抓大后抓小的技巧item = {}  #定義了一個字典for each in content_field:reply_info = json.loads(each.xpath('@data-field')[0].replace('"','')) #再次使用XPath,抓取每一個回帖里面的評論內容#其中還使用了替換和json.loads,將json的內容轉換成字典格式author = reply_info['author']['user_name']  #使用字典的形式將作者的信息進行存儲#下面的代碼獲取所有評論信息content = each.xpath('div[@class="d_post_content_main"]/div/cc/div[@class="d_post_content j_d_post_content "]/text()')[0]reply_time = reply_info['content']['date']print content  #將獲取的信息打印出來print reply_timeprint authoritem['user_name'] = author#將信息傳入到字典里面item['topic_reply_content'] = contentitem['topic_reply_time'] = reply_timetowrite(item)if __name__ == '__main__':pool = ThreadPool(4)   #此處是為了實現多線程爬蟲,提高效率f = open('content.txt','a')  #打開一個文件#生成20個網頁鏈接,將其保存在一個列表中page = []for i in range(1,5):newpage = 'http://tieba.baidu.com/p/3522395718?pn=' + str(i)page.append(newpage)print page# results = spider('http://tieba.baidu.com/p/3522395718?pn=1')results = pool.map(spider, page)  #使用并行技術將其爬取下來pool.close()pool.join()f.close()
(上文的代碼可能因為網頁的變動,使得有些代碼不能測試,大家可以根據上文修改)

?

?

總結

以上是生活随笔為你收集整理的XPath与多线程爬虫的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。