新浪微博搜尋網頁使用者資訊爬取,新浪微博使用者資訊
在成功登陸之後,我們可以進行下一波操作了~
接下來,我們的目的是通過輸入關鍵字,找到相關使用者,並收集使用者的一些基本資料
環境
tools
1、chrome及其developer tools
2、python3.6
3、pycharm
Python3.6中使用的庫
1 import urllib.error 2 import urllib.request 3 import urllib.parse 4 import urllib 5 import re 6 import json 7 import pandas as pd 8 import time 9 import logging10 import random11 from lxml import etree
關鍵詞搜尋
我們首先在微博首頁輸入關鍵詞,進入搜尋網頁面
搜尋後,我們發現網址為http://s.weibo.com/user/%25E4%25B8%258A%25E8%25AE%25BF&Refer=weibo_user
很顯然,前面的“http://s.weibo.com/user/”網頁基礎地址,“%25E4%25B8%258A%25E8%25AE%25BF”看起來很像關鍵詞的一種編碼形式,最後的一段內容我們先放一邊,點擊進入下一頁繼續觀察規律
進入第二頁後,我們發現網址變為http://s.weibo.com/user/%25E4%25B8%258A%25E8%25AE%25BF&page=2
這時,我們需要猜想,如果將“page=”之後的數字改為1,是否會跳回首頁:
成功了,這樣我們就可以將url分為三部分。
現在,我們需要對中間編碼部分進行解析,發現它經過兩次url編碼。
因此,最後搜尋網頁的url串連如下:
1 import urllib.request2 3 keyword = '上訪'4 once = urllib.request.quote(keyword)5 pagecode = urllib.request.quote(once)6 7 i = 1 # 頁數8 url = 'http://s.weibo.com/user/' + pagecode + '&page=' + str(i)
使用者基本資料提取
接下來,我要對使用者的一些基本資料進行爬取。
經過觀察,初步確定了搜尋網頁面所能爬取到的使用者資訊欄位:
- 微博名——name
- 地區——location
- 性別——gender
- 微博地址——weibo_addr
- 關注——follow
- 粉絲——follower
- 微博數——weibo_num
- 簡介——intro
- 職業——職業資訊
- 教育——教育資訊
- 標籤——標籤
(其中,紅色欄位是必有的。)
我們將先對【必有欄位】進行爬取,再研究如何爬取【非必有欄位】。
首先我們先觀察網頁源碼,定位到使用者主體部分【可以用Chrome內建的developer tools中的element進行定位】
於是我們發現,開頭為“<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_user_feedList"”,我們可以通過Regex,尋找出該內容
此外,仔細觀察還能發現()裡面的內容為json格式。
萬歲~~
這樣對我們提取所需的html內容十分的有協助~~
1 data = urllib.request.urlopen(url,timeout=30).read().decode('utf-8') 2 3 lines = data.splitlines() 4 for line in lines: 5 if not line.startswith('<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_user_feedList","js":'): 6 continue 7 8 json_pattern = re.compile('\((.*)\)') 9 # 利用正則取出json10 json_data = json_pattern.search(line).group(1)11 # 將json封裝成字典並提取出html內容12 html = json.loads(json_data)['html']
然後開始正式的網頁內容解析,先對【必有欄位】進行解析。
這裡,我們依舊需要藉助Chrome上的developer tools
通過elements,我們定位到微博名對應的html內容,我們可以利用lxml中的etree和xpath擷取title名。
同理,我們也可以得到其他必有欄位,最後我們講所有內容儲存成一個dict,每個key對應的value則為一個list:
1 page = etree.HTML(html)2 info_user['name'] = page.xpath('//a[@class="W_texta W_fb"]/@title')3 info_user['weibo_addr'] = page.xpath('//a[@class="W_texta W_fb"]/@href')4 info_user['gender'] = page.xpath('//p[@class="person_addr"]/span[1]/@title')5 info_user['location'] = page.xpath('//p[@class="person_addr"]/span[2]/text()')6 info_user['follow'] = page.xpath('//p[@class="person_num"]/span[1]/a/text()')7 info_user['follower'] = page.xpath('//p[@class="person_num"]/span[2]/a/text()')8 info_user['weibo_num'] = page.xpath('//p[@class="person_num"]/span[3]/a/text()')
最後是對【非必有欄位】的爬取
該類欄位爬取的思代碼邏輯是這樣的
利用lxml包中的etree抓取子樹(class="person_detail")
再對子樹下的枝幹進行遍曆,判斷是否存在簡介(class="person_info")和標籤(class="person_label"),分別進行不同的處理
值得注意的是,部分標籤下的內容不止一個,因此我們必須對標籤內容進行一次判斷和遍曆。
由於簡介和標籤分別處於兩個枝幹,因此可以編輯兩個不同的函數進行提取:
1 # 提取簡介資訊 2 def info(self, p, path): 3 ''' 4 extract introduction of users 5 :param p: input an etree 6 :param path: input xpath which must be a string 7 :return: a string 8 ''' 9 if type(path) == str:10 info = p.xpath(path)11 if len(info) == 0:12 sub_info = ''13 else:14 sub_info = info[0]15 16 return sub_info17 else:18 print('please enter the path as a string')19 20 # 提取標籤資訊:標籤、教育資訊、工作資訊21 def labels(self, p, path, key):22 '''23 extract labels, such as hobbits, education, job, of users24 :param p: input an etree25 :param path: input xpath which must be a string26 :param key: keywords of labels27 :return: a string28 '''29 label = p.xpath(path)30 if len(label) == 0:31 sub_label = ''32 else:33 for l in label:34 label_name = re.compile('(.*?):').findall(l.xpath('./text()')[0])35 if label_name[0] == key:36 # 讀取出標籤資訊下的所有標籤內容37 all_label = l.xpath('./a/text()')38 l = ''39 for i in all_label:40 l = re.compile('\n\t(.*?)\n\t').findall(i)[0] + ',' + l41 sub_label = l42 else:43 sub_label = ''44 45 return sub_label
構造完函數後,可以提取所有使用者的資訊啦~
需要注意的是,返回的內容是單個子樹下的一個string,遍曆當前頁的所有使用者的資訊,則需要做成list:
1 info_user['intro'] = [] 2 info_user['標籤'] = [] 3 info_user['職業資訊'] = [] 4 info_user['教育資訊'] = [] 5 others = page.xpath('//div[@class="person_detail"]') 6 for p in others: 7 path1 = './div/p/text()' 8 info_user['intro'].append(self.info(p, path1)) 9 path2 = './p[@class="person_label"]'10 info_user['標籤'].append(self.labels(p, path2, '標籤'))11 info_user['職業資訊'].append(self.labels(p, path2, '職業資訊'))12 info_user['教育資訊'].append(self.labels(p, path2, '教育資訊'))
遍曆所有頁面
在成功實踐了使用者基本資料的爬取之後,需要遍曆所有的網頁
這裡,我們可以用很傻瓜的方法,自己觀察一共有幾頁,然後編一個for迴圈
然鵝!!!博主我絕對不會幹這種蠢事的!!!必須要有一個更加裝x的方式來遍曆才行!
於是,繼續依賴developer tools,找到點擊下一頁對應的元素,我們發現了一個神奇的東西——class="page next S_txt1_S_line1"
這不就是絕佳的定位是否存在下一頁的神器麼!!!
於是,最佳代碼邏輯誕生啦~~
通過判斷能否提取出該內容來決定是否進入還需進入下一頁i += 1
1 i = 1 2 Flag = True 3 while Flag: 4 # 構建url 5 url = 'http://s.weibo.com/user/' + pagecode + '&page=' + str(i) 6 try: 7 # 逾時設定 8 data = urllib.request.urlopen(url,timeout=30).read().decode('utf-8') 9 except Exception as e:10 print('出現異常 -->'+str(e))11 12 …………13 14 next_page = page.xpath('//a[@class="page next S_txt1 S_line1"]/@href')[0]15 if len(next_page) == 0 :16 Flag = False17 else:18 page_num = re.compile('page=(\d*)').findall(next_page)[0]19 i = int(page_num)
撒花~~~代碼的整體邏輯就這麼完善啦~~~
最後,我們需要解決的就是反爬的問題了,博主在這方面還沒有深入研究過
不過主要思路還是有的:
最後完整版代碼就不貼上來啦~~如果有疑問可以在下面回複多多交流~
撒花完結~~