因為參加大學生創新大賽,研究微博博文所表達的情感,需要大量的微博博文,而無論是國內的某度、csdn,還是國外的Google、gayhub、codeproject,都找不到想要的程序,只好自己寫程序。
贊美詩我在《攀登聯盟》中發現了壹個類似的程序,但它是在windows下運行的,並且源代碼是關閉的。而且,在爬取保存的文件並用notepad++打開它時出現了許多奇怪的問題,所以我放棄了。
0x001。基礎知識
這個程序是由python編寫的,因此基本的Python知識是必要的。另外,如果妳有壹定的計算機網絡基礎,妳會在前期準備中走很多彎路。
對於爬行動物,妳需要清楚以下幾點:
1.爬取對象的分類可以分為以下幾類:第壹類是不需要登錄的,比如博主練手時爬取的中國天氣網。這類網頁很難爬取,建議爬蟲新手爬這類網頁;二是登錄,如豆瓣、新浪微博等難爬的;第三類獨立於前兩類,您想要的信息通常是動態刷新的,例如AJAX或嵌入式資源。這種爬蟲是最難的,博主也沒研究過,這裏就不細說了(據同學說淘寶的商品評論都屬於這壹類)。
2.如果同壹個數據源有多種形式(如電腦版、移動版、客戶端等。),呈現方式越“純粹”越好。比如新浪微博有網頁版和手機版,手機版可以通過電腦瀏覽器訪問。這個時候我更喜歡手機版的新浪微博。
3.爬蟲通常將網頁下載到本地,然後通過某些方式提取有趣的信息。換句話說,抓取網頁只完成了壹半,您需要從下載的html文件中提取您感興趣的信息。此時,需要壹些xml知識。在這個項目中,博客作者使用XPath提取信息,也可以使用XQuery等其他技術。詳情請訪問w3cschool。
4.爬蟲應該盡可能模仿人類。現在網站的反抓取機制已經開發出來了。從驗證碼到IP封禁,爬蟲技術和反爬蟲技術可謂博弈不斷。
0x02。去
確定爬蟲的目標後,首先應該訪問目標網頁,找出目標網頁屬於上述哪種爬蟲。此外,記錄為了獲得感興趣的信息而需要采取的步驟,例如是否需要登錄,如果需要登錄,是否需要驗證碼;妳需要做什麽來獲得妳想要的信息,妳是否需要提交壹些表格;您想要獲取信息的頁面的url的規則是什麽,等等。
以下博客文章以blogger項目為例。該項目抓取特定新浪微博用戶自註冊以來的所有微博博文,並根據關鍵詞抓取100頁微博博文(約1000篇帖子)。
0x03。收集必要的信息
首先,訪問目標網頁並發現您需要登錄。進入登錄頁面如下:新浪微博手機版登錄頁面。
請註意,在url的後半部分有許多像“%xx”這樣的轉義字符,這將在本文稍後討論。
從這個頁面可以看到,登錄新浪微博手機版需要填寫賬號、密碼和驗證碼。
這個驗證碼只需要在最近提供(本文創建於2016.3.11)。如果不需要提供驗證碼,將有兩種登錄方式。
第壹種方法是進行js模擬,填寫賬戶密碼後點擊“登錄”按鈕。博主之前用這個方法寫了壹個Java爬蟲,現在找不到項目了,這裏就不贅述了。
第二種類型需要壹定的HTTP基礎,並提交包含所需信息的HTTP POST請求。我們需要Wireshark工具來捕獲登錄微博時發送和接收的數據包。如下圖所示,我抓取了登錄時發送和接收的數據包。Wireshark抓取了1的結果。
在搜索欄“/(display id)中提供搜索條件?page =(pagenum)。這將是我們的爬蟲拼接url的基礎。
接下來,查看網頁的源代碼並找到我們想要的信息的位置。打開瀏覽器開發者工具,直接定位壹個微博,就能找到它的位置,如下圖。
xpath
觀察html代碼,發現所有微博都在《div & gt標簽,這個標簽中有兩個屬性,其中class屬性是“c”和壹個唯壹的id屬性值。獲取這些信息有助於提取所需的信息。
此外,還有壹些因素需要特別註意。
*微博分為原創微博和轉發微博。
*根據發布時間與當前時間的差異,頁面上顯示時間的方式有多種,例如“MM分鐘前”、“今天的HH:MM”、“MM月dd日HH:MM-DD hh: mm: SS”。*手機版新浪微博的壹頁顯示大約65,438+00條微博,因此請註意總數* *。
0x04。編碼
1.抓取用戶微博
這個項目的開發語言是Python 2.7,項目中使用了壹些第三方庫,可以通過pip添加。
由於驗證碼阻止了自動登錄的想法,用戶只有在想要訪問特定用戶的微博頁面時才能提供cookies。
第壹個是Python的請求模塊,它為url請求提供cookies。
導入請求
打印請求。get(URL,cookies = cookies)。內容使用此代碼打印帶有cookies的URL請求頁面結果。
首先,獲取用戶的微博頁面數量。通過檢查網頁的源代碼,找到代表頁數的元素,並通過XPath等技術提取頁數。
頁數
該項目使用lxml模塊通過XPath提取html。
首先,導入lxml模塊,項目中僅使用etree,因此從lxml導入etree。
然後使用下面的方法返回頁碼。
def getpagenum(self):
URL = self . geturl(pagenum = 1)
html = requests . get(URL,cookies = self.cook)。內容#訪問第壹頁獲取頁碼。
選擇器= etree。HTML(HTML)
pagenum = selector . XPath(‘//input【@ name =“MP“】/@ value‘‘)【0】
return int(pagenum)
下壹步是連續拼接url-》訪問URL-& gt;下載網頁。
需要註意的是,由於新浪反抓取機制的存在,如果相同的cookies訪問頁面過於頻繁,它將進入類似的“冷靜期”,即它將返回壹個無用的頁面。通過分析這個無用的頁面,發現這個頁面在特定的地方會有特定的信息,而這個頁面對我們是否有用可以通過XPath技術來判斷。
def ispageneeded(html):
選擇器= etree。HTML(HTML)
嘗試:
title = selector . XPath(‘//title‘‘)【0】
除了:
返回False
返回title.text!=‘微博廣場’和title.text!=‘微博‘
如果有無用的頁面,您只需要再次訪問它們。但是,通過後來的實驗發現,如果長時間頻繁訪問它們,返回的所有頁面都將是無用的,程序將陷入無限循環。為了防止程序陷入無限循環,博主設置了壹個trycount閾值,超過該閾值後該方法將自動返回。
下面的代碼片段顯示了單線程爬蟲的方法。
def開始搜索(self,startpage=1,trycount=20):
嘗試= 0
嘗試:
OS . mkdir(sys . path【0】+‘/Weibo _ raw/‘+self . wanted)Exception,e:
打印字符串(e)
isdone = False
而不是isdone並嘗試& lt嘗試計數:
嘗試:
pagenum = self.getpagenum()
isdone = True
除了例外,e:
嘗試+= 1
if attempt == trycount:
返回False
i =起始頁
當我& lt= pagenum:
嘗試= 0
isneeded = False
html =‘‘‘
當不需要和嘗試& lt嘗試計數:
html = self . getpage(self . geturl(I))
isneeded = self . ispageneeded(html)
如果不需要:
嘗試+= 1
if attempt == trycount:
返回False
self . save html(sys . path【0】+‘/Weibo _ raw/‘+self . wanted+‘/‘+str(I)+‘。txt,html)打印字符串(I)+‘/‘+字符串(pagenum - 1)
i += 1
返回True
考慮到程序的時間效率,在編寫了單線程爬蟲之後,該博主還編寫了多線程爬蟲版本。基本思路是用微博頁面數除以線程數。例如,如果微博中的用戶有65,438+000個微博頁面,程序有65,438+00個線程,那麽每個線程只負責爬取65,438+00個頁面。其他基本思想與單線程類似,只有邊界值需要仔細處理,因此我在此不再贅述。另外,由於多線程的效率比較高,並發量特別大,服務器很容易返回無效頁面,所以trycount的設置比較重要。這位博主在寫這條微博時,用了壹個新的cookie來測試誰爬上了北京郵電大學的微博。3976篇微博文章全部成功爬取,並提取了博文。只花了15s,這實際上可能與新舊cookies和網絡環境有關。命令行設置如下,項目網站中解釋了命令行的含義:python main.py _ T _ WM = xxxSUHB = xxxSUB = xxxGSID _ CTANDWM = XXX UBUPPT M 20 20以上關於爬蟲的基本介紹工作就結束了,接下來分析爬蟲的第二部分。因為項目提供了多線程抓取方法,而多線程壹般是亂序的,但微博的博文是按時間排序的,所以項目采用了壹種折中的方法,將下載的頁面保存在本地文件系統中,每個頁面以其頁碼作為文件名。爬行工作完成後,將遍歷並解析文件夾中的所有文件。
通過前面的觀察,我們了解到了微博的博文都有哪些特點。通過使用XPath技術,從該頁面中提取具有該特性的所有標簽並不困難。
再次,微博分為轉發微博和原創微博,以及時間表達。此外,由於我們的研究主題只對微博文本感興趣,因此我們不考慮插圖。
def start parsing(self,parsing time = datetime . datetime . now()):
basepath = sys . path【0】+‘/Weibo _ raw/‘+self . uid for filename in OS . listdir(basepath):
if filename . starts with(‘。‘):
繼續
path = base path+‘/‘+文件名
f =打開(路徑,‘r‘)
html = f.read()
選擇器= etree。HTML(HTML)
weiboitems = selector . XPath(‘//div【@ class =“c“】【@ id】‘)用於Weibo items中的項目:
微博=微博()
weibo.id = item.xpath(。/@ id’)【0】
cmt = item.xpath(。/div/span【@ class =“CMT“】‘)if len(CMT)!= 0:
Weibo . is post = True
Weibo . content = CMT【0】。文本
否則:
weibo.isrepost = False
ctt = item.xpath(。/div/span【@ class =“CTT“】‘)【0】
如果ctt.text不為None:
微博內容+= ctt.text
對於ctt.xpath(。/a’):
如果a.text不為None:
微博內容+= a.text
如果a.tail不為None:
weibo.content += a .尾巴
if len(CMT)!= 0:
reason = CMT【1】。text。split(u‘\ xa0‘)
if len(原因)!= 1:
Weibo . reports on = reason【0】
ct = item.xpath(。/div/span【@ class =“CT“】‘)【0】
time = CT . text . split(u‘\ xa0‘‘【0】
Weibo . time = self . gettime(self,time,parsing time)self . weibos . append(Weibo。__dict__)
關閉()
方法傳遞的參數parsingtime設置的初衷是在開發初期可能不會同時進行抓取和解析(並非嚴格意義上的“同時”),而微博時間顯示是根據訪問時間進行的,例如抓取時間為10:00,5分鐘前發布了壹條微博顯示,但如果解析時間為10:30,解析時間就會出錯,所以。到爬蟲基本發育結束時,爬行和解析的開始時間差距就會縮小,時間差就是爬行過程的時間,基本可以忽略。
解析結果保存在列表中。最後,列表以json格式保存到文件系統中,並刪除過渡文件夾。
定義保存(自身):
f = open(sys . path【0】+‘/Weibo _ parsed/‘+self . uid+‘。txt,‘w‘)jsonstr = JSON . dumps(self . weibos,indent=4,確保_ ascii = False)f . write(jsonstr)
關閉()
抓取關鍵詞
同樣,收集必要的信息。在微博手機搜索頁面輸入“python”,觀察網址,研究其規律。雖然第壹頁上沒有規則,但我們在第二頁上發現了壹條規則,並且這條規則可以應用回第壹頁。
第二頁
申請後的第壹頁
觀察url,我們可以發現url中唯壹的變量是關鍵字和頁面(實際上,hideSearchFrame對我們的搜索結果和爬蟲沒有任何影響),因此我們可以在代碼中控制這兩個變量。
此外,如果關鍵字是中文,那麽url需要轉換漢字。例如,如果我們在搜索框中鍵入“Happy”並進行搜索,我們會發現url顯示Happy Search如下。
但是它被復制為
/search/mblog?hideSearchFrame = & amp關鍵字= % E5 % BC % 80 % E5 % BF % 83 & ampPage=1幸運的是,python的urllib庫具有qoute方法處理中文轉換的功能(如果是英文,則不會轉換),因此在拼接URL之前使用此方法處理參數。
此外,考慮到關鍵字搜索屬於數據收集階段使用的方法,我們在此僅提供網頁的單線程下載。如果有多線程的需求,可以按照多線程抓取用戶微博的方法自己重寫。最後,提取並保存下載的網頁(我知道這個模塊設計有點奇怪,所以我打算在我想重新創建它時更改它(hao),所以我們就這樣做吧)。
def關鍵字爬網(自身,關鍵字):
real keyword = urllib . quote(keyword)#用中文處理關鍵字。
嘗試:
OS . mkdir(sys . path【0】+‘/keywords‘)
除了例外,e:
打印字符串(e)
微博=【】
嘗試:
high points = re . compile(u‘【\ u 00010000-\ u 0010 ffff】‘)#處理表情符號,但似乎不起作用。
除了re.error:
high points = re . compile(u‘【\ ud 800-\ uDBFF】【\ UDC 00-\ uDFFF】‘)pagenum = 0
isneeded = False
不需要時:
html = self . get page(‘/search/mblog?關鍵字= % s & amppage = 1“% real keyword)is needed = self . ispageneeded(html)
如果需要:
選擇器= etree。HTML(HTML)
嘗試:
pagenum = int(selector . XPath‘//input【@ name =“MP“】/@ value‘‘【0】),以下情況除外:
pagenum = 1
對於範圍內的I(1,pagenum + 1):
嘗試:
isneeded = False
不需要時:
html = self . get page(‘/search/mblog?關鍵字= % s & amppage = % s“%(real keyword,str(I))is needed = self . ispageneeded(html)
選擇器= etree。HTML(HTML)
weiboitems = selector . XPath(‘//div【@ class =“c“】【@ id】‘)用於Weibo items中的項目:
cmt = item.xpath(。/div/span【@ class =“CMT“】‘)if(len(CMT))= = 0:
ctt = item.xpath(。/div/span【@ class =“CTT“】‘)【0】
如果ctt.text不為None:
text = etree . tostring(CTT,method =‘text‘,encoding =“unicode“)tail = CTT . tail
if text . ends with(tail):
index =-len(tail)
text = text【1:index】
text = high points . sub(u‘\ u25FD‘,text)#表情符號的處理方式似乎不起作用。
weibotext =文本
微博附加(微博文本)
打印字符串(I)+‘/‘+字符串(pagenum)
除了例外,e:
打印字符串(e)
f = open(sys . path【0】+‘/keywords/‘+keyword+‘。txt,‘w’)嘗試:
f . write(JSON . dumps(weibos,indent=4,確保_ ascii = False))Exception除外,例如:
打印字符串(ex)
最後:
關閉()
博客作者以前從未編寫過任何爬蟲程序。為了獲得新浪微博的博文,博主們編寫了三種不同的爬蟲程序,包括Python和Java。爬行動物不能用是正常的。不要氣餒。爬蟲程序和反爬行機制壹直在不停地玩遊戲。
另外,轉載請告知博主,認為博是老板的不需要告知。