昨天開始了極客學院《XPath與多線程爬蟲》課程的學習,主要涉及到XPath和requests的使用,在測試過程當中出現了不少問題,通過不斷摸索以及前輩們的幫助,現將經驗總結以下:
1. Python3下面文本編碼問題
雖然Python3相對於2已經集成了不少編碼方式,使咱們不須要過多去關心和指定編碼,但有時候在文本讀取,寫入的時候仍是須要多當心,在測試過程當中屢次出如今寫入文件時報告錯誤「UnicodeEncodeError: 'ascii' codec can't encode character '\u56de' in position 0: ordinal not in range(128)」,這是因爲咱們在抓取網頁的時候採用的是UTF-8編碼,而存儲時沒有指定編碼,在存儲到文件的過程當中就會報錯。
解決辦法爲:
在讀取文件時加入指定UTF-8編碼的選項html
f = open('content.txt','a',encoding='UTF-8')
另外須要注意的是使用requests獲取到網頁以後一樣要指定編碼正則表達式
html = requests.get(url) html = re.sub(r'charset=(/w*)', 'charset=UTF-8', html.text)
2. XPath的用法
XPath能夠很方便的解析XML文件的節點和屬性,使用也很簡單,相比於正則表達式來講,XPath的查詢方式更加高效準確,它來自於lxml包內的etree,在使用以前應該聲明編程
from lxml import etree
在使用XPath應該遵循「先抓大,再抓小」的原則,現定位到大的節點,獲取到全部字節點再一層一層往下尋找,直到獲取所須要的信息
例如,咱們想要抓取百度貼吧的網頁每個樓層的信息(包括做者,回帖時間,回帖內容等等),經過Chrome-Inspect element能夠審查代碼,獲得某一個樓層的代碼樓層最外層都有聲明:json
<div class="l_post j_l_post l_post_bright "
使用XPath先獲取整個樓層的全部節點(Node)多線程
content_field = selector.xpath('//div[@class="l_post j_l_post l_post_bright "]')
再往下尋找,發現咱們要提取的內容位於app
<div class="d_post_content_main">
這一個節點之內,再繼續往下挖掘:編程語言
content =each.xpath('div[@class="d_post_content_main"]/div/cc/div[@class="d_post_content j_d_post_content clearfix"]/text()')
這樣一步步獲得想要的內容ide
3.JSON格式
網頁中不少內容使用JSON來傳輸,咱們要把內容還原出來須要使用json模塊函數式編程
import json reply_info = json.loads(each.xpath('@data-field')[0].replace('"',''))
4.Python中的多線程
多線程能夠很大幅度提升軟件的處理速度,能夠充分利用計算機性能,不一樣的核處理不一樣的任務,並行執行,提升處理速度,使用方法以下:函數
from multiprocessing.dummy import Pool as ThreadPool pool = ThreadPool(8) results = pool.map(spider,page) pool.close() pool.join()
map 這一小巧精緻的函數是簡捷實現 Python 程序並行化的關鍵。map 源於 Lisp 這類函數式編程語言。它能夠經過一個序列實現兩個函數之間的映射。上面的這兩行代碼將 page這一序列中的每一個元素做爲參數傳遞到 spyder 方法中,並將全部結果保存到 results 這一列表中。其結果大體至關於:
results = [] for page in pages: results.append(spyder(page))
上述代碼中調用join以前,先調用close函數,不然會出錯。執行完close後不會有新的進程加入到pool,join函數等待全部子進程結束。
所有代碼:
#-*-coding:utf8-*- from lxml import etree from multiprocessing.dummy import Pool as ThreadPool import requests import json import re import sys '''從新運行以前請刪除content.txt,由於文件操做使用追加方式,會致使內容太多。''' def towrite(contentdict): #f=open("content.txt",'wb') f.writelines(u'回帖時間:' + str(contentdict['topic_reply_time']) + '\n') f.writelines(u'回帖內容:' + str(contentdict['topic_reply_content']) + '\n') f.writelines(u'回帖人:' + contentdict['user_name'] + '\n\n') #f.close() def spider(url): html = requests.get(url) #print(html.text) html = re.sub(r'charset=(/w*)', 'charset=UTF-8', html.text) selector = etree.HTML(html) # print(selector) #content_field = selector.xpath('//div[starts-with(@class,"l_post l_post_bright")]')p_content p_content_nameplate #content_field = selector.xpath('//*[@id="j_p_postlist"]') content_field = selector.xpath('//div[@class="l_post j_l_post l_post_bright "]') item = {} for each in content_field: reply_info = json.loads(each.xpath('@data-field')[0].replace('"','')) author = reply_info['author']['user_name'] # content1 = each.xpath('//div[@class="d_post_content_main"]') content = each.xpath('div[@class="d_post_content_main"]/div/cc/div[@class="d_post_content j_d_post_content clearfix"]/text()') reply_time = reply_info['content']['date'] print("content:{0}".format(content)) print("Reply_time:{0}".format(reply_time)) print("Author:{0}".format(author)) item['user_name'] = author item['topic_reply_content'] = content item['topic_reply_time'] = reply_time towrite(item) if __name__ == '__main__': pool = ThreadPool(8) f = open('content.txt','a',encoding='UTF-8') # f = open('content.txt','a') page = [] for i in range(1,21): newpage = 'http://tieba.baidu.com/p/3522395718?pn=' + str(i) page.append(newpage) results = pool.map(spider,page) pool.close() pool.join() f.close()
結果以下:
回帖時間:2015-01-11 16:52 回帖內容:[' 6和plus糾結買哪款。還有 買完新機可讓他上色嗎'] 回帖人:鬥已轉0 回帖時間:2015-01-11 16:53 回帖內容:[' 我如今是以貼吧高級會員的身份幫你頂貼,請注意你的態度'] 回帖人:暑假幹啥 回帖時間:2015-01-11 16:57 回帖內容:[' 我去'] 回帖人:qw518287200 回帖時間:2015-01-11 16:57 回帖內容:[' 能教我怎麼看序列號或imei號麼,大神\uf618'] 回帖人:花顏誘朕醉
須要注意的是,極客學院附帶資料的源代碼是沒法使用的,以上說到的幾點就是我在調試過程當中淌過的坑,要注意使用Chrome對要抓取的網頁進行細心分析,修改xpath參數並不斷試驗。
+++++++明日計劃++++++++++++++++加入計時功能,測試單線程與多線程的性能差異嘗試抓取網頁中的圖片並保存