Python 爬蟲教學-我們來做一個IG抽獎神器吧!(下)

前言

將所有的資料讀取下來之後,還有一個重要的步驟,就是如何處理資料,今天這部分就是教大家將拿到的資料進行抽獎以及條件篩選的動作,比較多會涉及Python本身語法的運用,而如果你對於如何拿到資料還不熟悉的話,記得去看上一篇。

Python -程式碼講解

comments = browser.find_elements_by_class_name("Mr508")

上次我們只是讓網頁完成了加載所有留言這個動作,其實還少了一行,就是把留言資料取出來(剛剛回去看的時候才發現少了這個步驟),所以我們先完成上次沒做的,接下來就開始做處理吧!

挑選符合資格的留言

data = {} #單個符合資格的資料
total_datas =[] #所有符合資格資料的集合
for comment in comments:
        #在此處就要判斷tag人數了
        tag = comment.find_elements_by_class_name("notranslate")
        #print(comment.text) #先看看抓下來的文字形式長怎樣
        if len(tag)>=tag_num: #tag_num為抽獎需要的tag人數
            comment_data= comment.text.split("\n")
            if wanted_comment in comment_data[1]:
                data["user_id"] = comment_data[0]
                data["comment"] = comment_data[1]
                data["user_pic"] = user_pic
                total_datas.append(data)
                #清空重來
                data={}
            comment_data={}

首先我們利用迴圈的方式將資料一筆一筆跑出來,因為剛剛取出來的是所有的留言,我們需要在迴圈的每一次去判斷留言是不是符合抽獎的資格,如果符合的話,我們就將其加入給使用者,而按照一般的抽獎流程我們篩選留言的條件有以下這兩個

1.Tag人數

2.留言內容

處理Tag和留言

我們找到tag的方式,就是上面第三行那樣的,這方面我們找到的方式就跟之前的檢查一樣,就不再提了,並且我們利用len()看看tag的人數有沒有大於抽獎需要的,如果有的話,就符合資格。

留言內容的部分就比較難了,在這邊大家可以先跑跑看你的程式,將我上方註解的print那行註解掉,看看留言內的文字會長怎樣,沒有意外的話,我們會看到這樣的結果

由於Comment.text這個寫法是將裡面所有的文字都拿出來,所以你會看到所有文字都擠在一團,利用這樣的字串去判斷肯定是行不通的,而幸運的是我們可以觀察到他的規律以及他是利用空行的方式做分類,於是我們就可以利用split(“/n”),來把這些資訊整理成陣列

整理過後,我們會發現留言被歸類在陣列的第二個元素,也就是comment_data[1],於是接下來就去判斷抽獎的留言有沒有在裡面,如果有的話,我們就正式滿足了tag人數和留言內容兩個條件了,這時我們便將資料打包放進去。

開始抽獎

if len(total_datas) <winner_num:
        print("留言人數太少")
else:
#------------------------此處做抽獎邏輯
    num = 0
    while num < winner_num:
        repeatOrNot = False #看看當前是否重複
        winner = random.randint(0,len(total_datas)-1)  
        print(num)
        #做重複留言邏輯
        if repeated == False:
            #確認此data是否有重複 若重複則num-1 讓迴圈多跑一次
            for data in winning_datas:
                if total_datas[winner]["user_id"] in data['user_id']:
                    print("重複")
                    num-=1
                    repeatOrNot = True
            #如果不重複則一樣append dtat
            if repeatOrNot == False:
                winning_datas.append(total_datas[winner])
        else: #直接選出
            winning_datas.append(total_datas[winner]) 
        num+=1
    print(winning_datas)

首先我們要先做個防呆,也就是如果抽獎人數要求一百位,但符合資格的只有一位,那肯定不能抽的,所以這邊先利用total_datas的長度判斷是否能開始抽獎

抽獎邏輯

首先會看到一個reapeatOrnot的邏輯,這個是用來判斷是否能重複得獎的,這個我們下面會講解,我們先來了解抽獎的邏輯是怎麼運行的。

我們宣告一個num代表當前已經抽出來的得獎人,利用一個while迴圈去判斷得獎人是否已經夠了,而再來我們利用random()這個函數去隨機抽出 0~total_data(符合資格人數)其中一個號碼,將抽中的號碼放進去winning_datas內,每次抽完就將,得獎人+1,這樣最後就可以得出結果了。

重複的話怎麼辦

有些人可能會思考,我這邊使用for回圈就行了,為什麼要使用while呢,原因就在於要判斷重複抽獎。

首先我們利用repeatOrnot這個變數來作為判斷當前抽出來的人是否有重複,一開始設false,而repeated則是使用者自己去選擇是否要重複,能重複的話(也就是true),按照抽獎邏輯內寫的就直接完成抽獎了。

但如果是False的話,我們這時便需要利用一個for迴圈去看放得獎人資訊的winning_datas,去確認id是否有重複,如果有重複的話,我們就必須將num-1,並且不將這次抽中的人放進winning_datas,這樣就能一直抽,直到沒有重複的中獎者才會結束回圈,到此,我們就完成整個抽獎的流程了。

完整程式碼

#爬蟲
from selenium import webdriver #利用selenium做爬蟲
from selenium.webdriver.common.alert import Alert #處理跳窗
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
#抽獎功能
import time
import random
#處理檔案
import json
#處理環境變數
import os
from dotenv import load_dotenv

def test():
    #----------------前置資料處理
    wanted_comment = "ja" #指定留言內容
    winner_num = 5  #中獎人數
    tag_num = 0  #標記人數
    repeated = False  #可否重複中獎
    total_datas = []
    winning_datas = []
    data = dict()
    #post_URL = 'https://www.instagram.com/p/CQd0iXwBNZs/' #測試網址
    post_URL = 'https://www.instagram.com/p/CQZPk9tD1Rp/'

    driver_path = 'chromedriver'  #導入chrome driver才能開模擬器
    browser = webdriver.Chrome()

    #----------------開始爬蟲 登入
    web = 'https://www.instagram.com/accounts/login/'; #ig登入
    browser.get(web)

    ig_account_ele = WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH, '//*[@id="loginForm"]/div/div[1]/div/label/input')))
    print(ig_account_ele)

    ig_account_ele.send_keys('your_account')#將使用者的資訊填入

    ig_password_ele = WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH, '//*[@id="loginForm"]/div/div[2]/div/label/input')))
    print(ig_account_ele)

    ig_password_ele.send_keys('your_password')#將使用者的資訊填入


    login_ele = WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH, '//*[@id="loginForm"]/div/div[3]/button/div')))
    login_ele.click()

    #確認登入狀態
    WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH, '//*[@id="react-root"]//*[contains(@class,"_47KiJ")]')))

    #----------------爬蟲 到指定網址進行爬蟲
    browser.get(post_URL)

    post =  WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH,'//*[@id="react-root"]/section/main/div/div[1]/article/div[3]/div[1]/ul')))
    print(post)
    scroll_time = 0

    #確定while的停止條件(done)
    #做抽留言條件()
    #做資料處理轉換成json
    maxTop = 0 
    stop = False
    #while not stop:
        #滑動
    #    scrollTop = browser.execute_script('arguments[0].scrollTop +=300;return arguments[0].scrollTop', post)
    #    scroll_time+=1
    #    print(scrollTop)
    #    if scrollTop>1000: #每次scroll如果看得到load more就點 
    #        print("執行中")
    #        more_ele =  WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH,'//*[@id="react-root"]/section/main/div/div[1]/article/div[3]/div[1]/ul/li/div/button')))
    #        more_ele.click()
        #終止條件
    #    if scrollTop>maxTop: #代表有下滑到則將maxtop換為新的
    #        maxTop = scrollTop 
    #    elif scrollTop == maxTop: #代表上次的和這次的一樣
    #        stop = True
    while scroll_time<10:
        #滑動
        scrollTop = browser.execute_script('arguments[0].scrollTop +=300;return arguments[0].scrollTop', post)
        scroll_time+=1
        print(scrollTop)
        if scrollTop>950: #每次scroll如果看得到load more就點 
            print("執行中")
            more_ele =  WebDriverWait(browser, 20, 0.5).until(EC.presence_of_element_located((By.XPATH,'//*[@id="react-root"]/section/main/div/div[1]/article/div[3]/div[1]/ul/li/div/button')))
            more_ele.click()
        #終止條件
        if scrollTop>maxTop: #代表有下滑到則將maxtop換為新的
            maxTop = scrollTop 
        elif scrollTop == maxTop: #代表上次的和這次的一樣
            stop = True
        time.sleep(1)
        scroll_time+=1
    #爬取資料 
    comments = browser.find_elements_by_class_name("Mr508")
    #做資料的處理
    for comment in comments:
        #在此處就要判斷tag人數了
        tag = comment.find_elements_by_class_name("notranslate")
        #print(len(tag))
        if len(tag)>=tag_num:
            comment_data= comment.text.split("\n")
            print(comment_data)
            #判斷使用者要的留言內容
            #判斷tag人數
            if wanted_comment in comment_data[1]:
                #user_pic = comment.find_element_by_class_name("_6q-tv") #圖片有跨網域問題無法抓取
                #user_pic = user_pic.get_attribute("src")
                data["user_id"] = comment_data[0]
                data["comment"] = comment_data[1]
                data["user_pic"] = user_pic
                total_datas.append(data)
                #清空重來
                data={}
            comment_data={}
    print(total_datas)
    if len(total_datas) <winner_num:
        print("留言人數太少")
    else:
        #------------------------此處做抽獎邏輯
        num = 0
        while num < winner_num:
            repeatOrNot = False #看看當前是否重複
            winner = random.randint(0,len(total_datas)-1)  
            print(num)
            #做重複留言邏輯
            if repeated == False:
                #確認此data是否有重複 若重複則num-1 讓迴圈多跑一次
                for data in winning_datas:
                    if total_datas[winner]["user_id"] in data['user_id']:
                        print("重複")
                        num-=1
                        repeatOrNot = True
                #如果不重複則一樣append dtat
                if repeatOrNot == False:
                    winning_datas.append(total_datas[winner])
            else: #直接選出
                winning_datas.append(total_datas[winner]) 
            num+=1
        print(winning_datas)
        
        #最後做json處理之後導出 此處是如果有要去串前端,或者其他用途,可以先轉成json再丟出去
        #jsonData = json.dumps(winning_datas)
        #return jsonData
test()

到此我們就完成整個教學了,這次主要的重點在於動態加載的概念,以及將資料爬取下來之後,我們要如何做處理並方便其他的功能去使用這些資料

如果你不是要爬像是IG這種公開API很難用的網站的話,其實和網站的API互動會是更好的選擇

因為在爬蟲這個領域,很多網站都開始有了反爬蟲偵測的功能,像是這一次我想要將這個作品部署在Heroku時,就因為IG的反爬蟲而失敗了。

之後我們的下一個教學就會是和Python相關的API教學,有任何問題都可以在下方留言喔,我會盡快的回覆你,也可以告訴我你想要看到什麼樣相關的教學!

馬上看Python爬蟲教學 -試著作自己的搶購機器吧!(上)

2 thoughts on “Python 爬蟲教學-我們來做一個IG抽獎神器吧!(下)”

  1. Hello,I want to ask for line 14 “from dotenv import load_dotenv”,but the program has no “dotenv” module,should I download what application or something? I look for related information like install python3-dotenv or python-dotenv,but I am not sure if it is correct.
    Thank you!

    1. hardco2020@gmail.com

      Yes , because “dotenv” is not a default module built in python3 , so you have to type the command on your cmd “pip3 install python-dotenv”,thanks for the comments!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *