在成功登錄以後,咱們能夠進行下一波操做了~html
接下來,咱們的目的是經過輸入關鍵字,找到相關用戶,並收集用戶的一些基本信息python
環境正則表達式
toolschrome
一、chrome及其developer toolsjson
二、python3.6app
三、pycharmdom
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 logging 10 import random 11 from lxml import etree
關鍵詞搜索ui
咱們首先在微博首頁輸入關鍵詞,進入搜索頁面編碼
搜索後,咱們發現網址爲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.request 2 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)
用戶基本信息提取
接下來,我要對用戶的一些基本信息進行爬取。
通過觀察,初步肯定了搜索頁面所能爬取到的用戶信息字段:
(其中,紅色字段是必有的。)
咱們將先對【必有字段】進行爬取,再研究如何爬取【非必有字段】。
首先咱們先觀察網頁源碼,定位到用戶主體部分【能夠用Chrome自帶的developer tools中的element進行定位】
因而咱們發現,開頭爲「<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_user_feedList"」,咱們能夠經過正則表達式,尋找出該內容
此外,仔細觀察還能發現()裏面的內容爲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 # 利用正則取出json 10 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['_id'] = page.xpath('//a[@class="W_btn_b6 W_fr"]/@uid') 4 info_user['weibo_addr'] = page.xpath('//a[@class="W_texta W_fb"]/@href') 5 info_user['gender'] = page.xpath('//p[@class="person_addr"]/span[1]/@title') 6 info_user['location'] = page.xpath('//p[@class="person_addr"]/span[2]/text()') 7 info_user['follow'] = page.xpath('//p[@class="person_num"]/span[1]/a/text()') 8 info_user['fans'] = page.xpath('//p[@class="person_num"]/span[2]/a/text()') 9 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_info 17 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 users 24 :param p: input an etree 25 :param path: input xpath which must be a string 26 :param key: keywords of labels 27 :return: a string 28 ''' 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] + ',' + l 41 sub_label = l 42 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 = False 17 else: 18 page_num = re.compile('page=(\d*)').findall(next_page)[0] 19 i = int(page_num)
撒花~~~代碼的總體邏輯就這麼完善啦~~~
最後,咱們須要解決的就是反爬的問題了,博主在這方面尚未深刻研究過
不過主要思路仍是有的:
最後完整版代碼就不貼上來啦~~若是有疑問能夠在下面回覆多多交流~
撒花完結~~