1. 程式人生 > >Python+Selenium多執行緒基礎微博爬蟲

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()

        好啦,現在我們就可以爬取各位小姐姐的微博和頭像了,下面就是爬取到的內容