Python+Selenium多執行緒基礎微博爬蟲
一、隨便扯扯的概述
大家好,雖然我自上大學以來就一直在關注著CSDN,在這上面學到了很多知識,可是卻從來沒有發過部落格(還不是因為自己太菜,什麼都不會),這段時間正好在機房進行期末實訓,我們組做的是一個基於微博資訊的商品推薦系統,不說這個系統是不是真的可行可用,有價值,我們的主要目的只是通過構建這個系統來學習更多的技術,新學一門語言(額,原諒我現在才開始學Python)。
好,廢話不多說,這次我主要是想記錄一下我在這個專案中的進展,純屬是想做個日誌之類的東西留個紀念吧,我雖然都已經大三了,但還是個菜鳥,其中在爬取微博內容部分引用了某位大神的程式碼https://blog.csdn.net/d1240673769/article/details/74278547,希望各位大神多多給出意見和建議。
這篇文章主要是講我如何通過selenium這個工具來實現通過模擬瀏覽器搜尋微博使用者暱稱,進入使用者微博主頁,並將內容儲存到本地,其中也順帶著把使用者的微博頭像儲存了。
二、環境配置
1.首先我安裝的環境是python3.6,使用的IDE是pycharm,在pycharm中可以直接安裝所需要的selenium和webdriver等等一系列的package。
如果需要匯入相關的package,建立了專案之後,點選File -> settings -> Project: “專案名稱” -> Project Interperter,如下圖所示:
接下來在右側雙擊擊pip,進入所有Package介面,搜尋所需要的package,點選install package即可:
這裡可以同時安裝很多個,選完之後可以直接將視窗叉掉,然後點選OK,程式會在後臺進行安裝。
安裝完成後,pycharm下方會有提示。
2. 下載chromdriver,進入http://npm.taobao.org/mirrors/chromedriver/,通過檢視notes.txt下載與自己的chrome瀏覽器相對應的chromedriver。
下載之後將解壓包直接複製到專案目錄下,例如我這裡直接複製到:
3.下面開始編寫程式。爬取微博我這裡使用的是m站的微博,通過構造https://m.weibo.cn/u/“使用者的OID”來直接訪問使用者的所有微博內容,無需登入。如果通過訪問wap站的話,每個人的微博主頁地址可以更改,規律難尋,技術水平有限。
整個爬蟲的具體思路如下:
模擬瀏覽器訪問https://weibo.com -> 通過搜尋框搜尋微博使用者暱稱 -> 切換到找人頁面 -> 爬取使用者微博主頁地址 並訪問 -> 爬取使用者oid -> 訪問https://m.weibo.cn/u/'oid' -> 正則表示式匹配內容並抓取。
首先我們來構造OidSpider類,引入相關package:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup import re from pyquery import PyQuery as pq
定義driver,訪問https://www.weibo.com,通過定位器在搜尋框輸入使用者微博暱稱,定位器selector獲得如下圖,
程式碼如下:
self.driver = webdriver.Chrome() self.wait = WebDriverWait(self.driver, 10) self.driver.get("https://www.weibo.com/") input = self.wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, "#weibo_top_public > div > div > div.gn_search_v2 > input"))) submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#weibo_top_public > div > div > div.gn_search_v2 > a"))) input.send_keys(self.nickName)
然後同樣的方法定位搜尋按鈕點選搜尋,再通過定位器切換到找人介面:
submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#weibo_top_public > div > div > div.gn_search_v2 > a"))) submit.click() submit = self.wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, '#pl_common_searchTop > div.search_topic > div > ul > li:nth-child(2) > a'))) submit.click()
接下來通過正則表示式匹配獲取使用者微博主頁url
html = self.driver.page_source doc = pq(html) return (re.findall(r'a target="_blank"[\s\S]href="(.*)"[\s\S]title=', str(doc))[0])
訪問使用者微博主頁url,通過正則表示式匹配使用者oid
self.driver.get('HTTPS:'+url) html = self.driver.page_source soup = BeautifulSoup(html, 'lxml') script = soup.head.find_all('script') self.driver.close() return (re.findall(r"'oid']='(.*)'", str(script))[0])
接下來進行WeiboSpider類的構建,引入相關package
from selenium import webdriver import urllib.request import json from selenium.webdriver.support.ui import WebDriverWait
構造請求頭
req = urllib.request.Request(url) req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0") proxy = urllib.request.ProxyHandler({'http': self.__proxyAddr}) opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler) urllib.request.install_opener(opener) data = urllib.request.urlopen(req).read().decode('utf-8', 'ignore') return data
通過xpath找到微博使用者頭像(xpath的用法跟selecor類似,但功能比selector更強大),然後直接儲存在本地
self.driver.get("https://m.weibo.cn/u/" + self.oid) src = WebDriverWait(self.driver, 10).until(lambda driver: self.driver.find_element_by_xpath('//*[@id="app"]/div[1]/div[2]/div[1]/div/div[2]/span/img')) imgurl = src.get_attribute('src') urllib.request.urlretrieve(imgurl, 'D://微博使用者頭像/'+nickName+'.jpg') self.driver.get(imgurl)
然後後迴圈抓取微博內容,寫到txt中
while True: weibo_url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=' + self.oid + '&containerid=' + self.searchContainerId(url) + '&page=' + str(i) try: data = self.constructProxy(weibo_url) content = json.loads(data).get('data') cards = content.get('cards') if (len(cards) > 0): for j in range(len(cards)): print("-----正在爬取第" + str(i) + "頁,第" + str(j) + "條微博------") card_type = cards[j].get('card_type') if (card_type == 9): mblog = cards[j].get('mblog') attitudes_count = mblog.get('attitudes_count') comments_count = mblog.get('comments_count') created_at = mblog.get('created_at') reposts_count = mblog.get('reposts_count') scheme = cards[j].get('scheme') text = mblog.get('text') with open(nickName+'.txt', 'a', encoding='utf-8') as fh: fh.write("----第" + str(i) + "頁,第" + str(j) + "條微博----" + "\n") fh.write("微博地址:" + str(scheme) + "\n" + "釋出時間:" + str( created_at) + "\n" + "微博內容:" + text + "\n" + "點贊數:" + str( attitudes_count) + "\n" + "評論數:" + str(comments_count) + "\n" + "轉發數:" + str( reposts_count) + "\n") i += 1 else: break except Exception as e: print(e)
當然最後不能忘了關閉driver
self.driver.close()
接下來到多執行緒,多執行緒其實比較簡單,python3和python2有些許區別,這裡推薦使用python3裡的threading
from oidspider import OidSpider from weibospider import WeiboSpider from threading import Thread class MultiSpider: userList=None threadList=[] def __init__(self, userList): self.userList=userList def weiboSpider(self,nickName): oidspider = OidSpider(nickName) url = oidspider.constructURL() oid = oidspider.searchOid(url) weibospider = WeiboSpider(oid) weibospider.searchWeibo(nickName) def mutiThreads(self): for niName in self.userList: t=Thread(target=self.weiboSpider,args=(niName,)) self.threadList.append(t) for threads in self.threadList: threads.start()
以下是完整程式碼:
####################################################### # # OidSpider.py # Python implementation of the Class OidSpider # Generated by Enterprise Architect # Created on: 20-六月-2018 10:27:14 # Original author: McQueen # ####################################################### from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from bs4 import BeautifulSoup import re from pyquery import PyQuery as pq class OidSpider: """ 使用者微博ID爬取 使用selenium模擬瀏覽器操作,通過搜尋使用者微博暱稱找到使用者, 爬取網頁重定向所需要的微博使用者主頁URL地址,進入主頁後對HTML程式碼分析, 找到並爬取使用者微博的ID nickName: 微博暱稱 driver: 瀏覽器驅動 wait: 模擬瀏覽器進行操作時所需等待的時間 """ nickName=None driver=None wait=None def __init__(self, nickName): """初始化Oid爬蟲 根據使用者輸入的nickName進行初始化 """ self.nickName=nickName def constructURL(self): """構造URL 模擬瀏覽器搜尋使用者微博暱稱,分析需要跳轉到使用者微博主頁的URL地址 返回值為使用者微博主頁的URL地址 """ self.driver = webdriver.Chrome() self.wait = WebDriverWait(self.driver, 10) self.driver.get("https://www.weibo.com/") input = self.wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, "#weibo_top_public > div > div > div.gn_search_v2 > input"))) submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#weibo_top_public > div > div > div.gn_search_v2 > a"))) input.send_keys(self.nickName) submit.click() submit = self.wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, '#pl_common_searchTop > div.search_topic > div > ul > li:nth-child(2) > a'))) submit.click() html = self.driver.page_source doc = pq(html) return (re.findall(r'a target="_blank"[\s\S]href="(.*)"[\s\S]title=', str(doc))[0]) def searchOid(self, url): """爬取使用者Oid 分析使用者微博主頁HTML程式碼,抓取使用者ID url: 使用者微博主頁的URL地址 返回值為使用者的ID """ self.driver.get('HTTPS:'+url) html = self.driver.page_source soup = BeautifulSoup(html, 'lxml') script = soup.head.find_all('script') self.driver.close() return (re.findall(r"'oid']='(.*)'", str(script))[0])
####################################################### # # WeiboSpider.py # Python implementation of the Class WeiboSpider # Generated by Enterprise Architect # Created on: 20-六月-2018 10:55:18 # Original author: McQueen # ####################################################### from selenium import webdriver import urllib.request import json from selenium.webdriver.support.ui import WebDriverWait class WeiboSpider: """初始化微博爬蟲並根據Oid構造載入微博使用者資訊和微博內容的xhr oid: 使用者ID url: m站用來載入微博使用者的xhr driver: 瀏覽器驅動器 """ __proxyAddr = "122.241.72.191:808" oid=None url=None driver=None def __init__(self, oid): self.oid=oid self.url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=' + oid self.driver=webdriver.Chrome() def constructProxy(self,url): """構造代理 構造請求包,獲取微博使用者的xhr資訊 返回值為xhr資訊 """ req = urllib.request.Request(url) req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0") proxy = urllib.request.ProxyHandler({'http': self.__proxyAddr}) opener = urllib.request.build_opener(proxy, urllib.request.HTTPHandler) urllib.request.install_opener(opener) data = urllib.request.urlopen(req).read().decode('utf-8', 'ignore') return data def searchContainerId(self,url): """構造使用者資訊的xhr地址 url: 需要進行分析的URL地址 返回值為xhr地址 """ data = self.constructProxy(url) content = json.loads(data).get('data') for data in content.get('tabsInfo').get('tabs'): if (data.get('tab_type') == 'weibo'): containerid = data.get('containerid') return containerid def searchWeibo(self, nickName): """爬取微博內容,存為文字文件 對每一個使用者微博內容的xhr資訊進行分析,爬取使用者的微博內容,並將內容輸出到txt檔案中 使用selenium的xpath進行使用者微博頭像的定位,並將使用者頭像下載到本地 nickName: 使用者微博暱稱 """ i = 1 self.driver.get("https://m.weibo.cn/u/" + self.oid) src = WebDriverWait(self.driver, 10).until(lambda driver: self.driver.find_element_by_xpath('//*[@id="app"]/div[1]/div[2]/div[1]/div/div[2]/span/img')) imgurl = src.get_attribute('src') urllib.request.urlretrieve(imgurl, 'D://微博使用者頭像/'+nickName+'.jpg') self.driver.get(imgurl) url=self.url while True: weibo_url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=' + self.oid + '&containerid=' + self.searchContainerId(url) + '&page=' + str(i) try: data = self.constructProxy(weibo_url) content = json.loads(data).get('data') cards = content.get('cards') if (len(cards) > 0): for j in range(len(cards)): print("-----正在爬取第" + str(i) + "頁,第" + str(j) + "條微博------") card_type = cards[j].get('card_type') if (card_type == 9): mblog = cards[j].get('mblog') attitudes_count = mblog.get('attitudes_count') comments_count = mblog.get('comments_count') created_at = mblog.get('created_at') reposts_count = mblog.get('reposts_count') scheme = cards[j].get('scheme') text = mblog.get('text') with open(nickName+'.txt', 'a', encoding='utf-8') as fh: fh.write("----第" + str(i) + "頁,第" + str(j) + "條微博----" + "\n") fh.write("微博地址:" + str(scheme) + "\n" + "釋出時間:" + str( created_at) + "\n" + "微博內容:" + text + "\n" + "點贊數:" + str( attitudes_count) + "\n" + "評論數:" + str(comments_count) + "\n" + "轉發數:" + str( reposts_count) + "\n") i += 1 else: break except Exception as e: print(e) self.driver.close()
from oidspider import OidSpider from weibospider import WeiboSpider from threading import Thread class MultiSpider: userList=None threadList=[] def __init__(self, userList): self.userList=userList def weiboSpider(self,nickName): oidspider = OidSpider(nickName) url = oidspider.constructURL() oid = oidspider.searchOid(url) weibospider = WeiboSpider(oid) weibospider.searchWeibo(nickName) def mutiThreads(self): for niName in self.userList: t=Thread(target=self.weiboSpider,args=(niName,)) self.threadList.append(t) for threads in self.threadList: threads.start()
from MultiSpider import MultiSpider def main(): list=['孟美岐','吳宣儀','楊超越','紫寧'] multispider=MultiSpider(list) multispider.mutiThreads() if __name__ == '__main__': main()
好啦,現在我們就可以爬取各位小姐姐的微博和頭像了,下面就是爬取到的內容