1. 程式人生 > >【實戰】還記得校內網麼(人人網)?當年的同學都在哪?爬取一下就知道

【實戰】還記得校內網麼(人人網)?當年的同學都在哪?爬取一下就知道

引言

不知從何時起,10.24變成了程式設計師的節日,首先祝大家節日快樂!程式碼永無bug!

 

小編最近突然有點懷舊,想到了一個古老的網站——校內網(人人網),在小編還在讀大學的那個時間,校內網真的是火的一塌糊塗,那時候的同學每天都在不停的刷校內,找同學,釋出新鮮事。然而世事難料,誰也不曾想到當時那麼火的校內網現在變得這麼淒涼,如果不是刻意去想,可能都想不到這個網站。當想到這個網站的時候,小編突然想去訪問一下這個網站,開啟瀏覽器,輸入使用者名稱密碼,還好,賬號能夠登陸。看著自己的好友列表,很想知道他們當時都在哪個城市。所以就寫了幾行程式碼,查詢一下,也就有了今天的專案。

 

需求分析

爬取校內網個人主頁的好友列表,然後再一次訪問好友的好友列表頁面,獲取使用者的城市資訊。

 

需要原始碼的同學可以關注公眾號,回覆“校內網”獲取原始碼。

 

知識點

 

爬取資料:WebDriver

多執行緒:ThreadPoolExecutor

圖形分析:pyecharts

資料庫:Mongo

 

獲取好友列表

 

由於使用WebDriver操作瀏覽器,模擬使用者登入操作,所以網頁不需要進行過多的分析,只要能夠定位控制元件位置即可。此處說一下邏輯,首先登陸校內網→訪問個人主頁→訪問個人好友頁面→提取好友資訊(包括:姓名,好友主頁地址,省份)→將提取的資訊存入mongoDB。

 

從mongoDB中讀取需要爬取的好友列表(用status標識)→訪問好友個人主頁(此處由於校內網限制,不能直接訪問,需要登入自己賬號後才可訪問)→訪問好友的好友列表→提取好友資訊→將提取的資訊存入mongoDB。

 

此處本應可以無限迴圈,例如我們提取到了好友的好友列表後,還可以提取好友的好友的好友列表。但是由於校內網隱私設定,我們無法訪問好友的好友的好友列表頁面,此處只能作罷。

 

之前聽說過一句話,大概意思是“通過六個人,你可以認識世界上任何一個人”,本來還想著通過校內查詢六次看看能夠查詢到多少人,結果受到校內設定影響,只能查詢一次。

 

以下是部分程式碼:

 

登陸校內網:

 

def login(driver,url,username,pwd):

    driver.get(url)

    # 輸入使用者名稱
    input_text = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("email")
    )
    input_text.send_keys(username)
    print("輸入使用者名稱 ok")

    # 輸入密碼
    password = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("password")
    )
    password.send_keys(pwd)
    print("輸入密碼 ok")

    # 檢查是否有需要輸入驗證碼
    check_code(driver)

    # 點選登入按鈕
    login_button = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_id("login")
    )
    login_button.click()
    print("點選登入 ok")

    # 進入個人主頁
    hd_name = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_class_name("hd-name")
    )
    href = hd_name.get_attribute("href")
    print("找到個人主頁地址 ok")
    return  href

 

爬取個人主頁好友列表:

 

def get_my_friends_info_list(driver,href):

    # 訪問個人主頁地址
    driver.get(href)
    name = driver.title
    print("訪問%s的個人主頁 ok"%name)

    # 進入好友列表頁面
    friends_button = WebDriverWait(driver,timeout).until(
        lambda d: d.find_element_by_xpath('//*[@id="specialfriend-box"]/div[1]/div/h5/a')
    )
    friends_button.click()
    print("進入%s好友列表 ok"%name)

    # 等待5s,載入資料
    time.sleep(5)

    # 如果滾動條不是在頁面最下方,證明列表需要載入,下拉至頁面最下方
    len_after = 0
    len_before = 1

    # 如果下拉後的好友數量不等於下拉前好友的數量,就繼續下拉頁面
    while len_after != len_before:
        friends_list = WebDriverWait(driver, timeout).until(
            lambda d: d.find_elements_by_class_name("friend-detail")
        )

        # 獲取下拉之前的好友數量
        len_before = len(friends_list)

        # 下拉至頁面最底端
        js = "window.scrollTo(0,document.body.scrollHeight)"
        driver.execute_script(js)
        time.sleep(2)

        friends_list = WebDriverWait(driver, timeout).until(
            lambda d: d.find_elements_by_class_name("friend-detail")
        )

        # 獲取下拉後的好友數量
        len_after = len(friends_list)
        print("滾動條向下滾動到底部一次")

    # 定位 friend-detail 標籤
    friends_list = WebDriverWait(driver, timeout).until(
        lambda d: d.find_elements_by_class_name("friend-detail")
    )
    print("定位li標籤 ok")

    # 定位 .friend-detail a 標籤
    personal_home_pages = WebDriverWait(driver, timeout).until(
        lambda d:d.find_elements_by_css_selector(".friend-detail a")
    )
    print("定位a標籤 ok")

    # 定位省份資訊
    provinces_list = WebDriverWait(driver, timeout).until(
        lambda d: d.find_elements_by_class_name("friends-loc-info")
    )
    print("定位省份資訊 ok")

    # 將定位的資訊組裝成字典並寫入資料庫
    for i in range(len(friends_list)):
        friends_info = {"data_id":friends_list[i].get_attribute("data-id"),
                        "name":friends_list[i].get_attribute("data-name"),
                        "id":friends_list[i].get_attribute("id"),
                        "personal_home_page":personal_home_pages[i].get_attribute("href"),
                        "province":provinces_list[i].get_attribute("title"),
                        "status":0,
                        "from":name,
                        }
        print(friends_info["province"], friends_info["name"])

        #將獲取到的資料寫入資料庫
        write_to_mongo(friends_info,i,name)

    driver.quit()

 

儲存資料到mongoDB:

 

def write_to_mongo(friends_info,i,name):

    # 將資料寫入資料庫
    curr = pymongo.MongoClient()
    database = curr["renren"]
    coll = database['friends']
    coll.insert(friends_info)
    print("%s的第%s個好友寫入資料庫成功"%(name,i+1))
    curr.close()

資料分析:

 

def fenxi():
    # 讀取資料庫中的資料
    curr = pymongo.MongoClient()
    database = curr["renren"]
    coll = database['friends']
    friends_list_all_find = coll.find({})
    friends_list_all = []
    for f in friends_list_all_find:
        friends_list_all.append(f)
    curr.close()

    # 統計省份資訊
    result = {}
    for f in friends_list_all:
        if f["province"] not in result.keys():
            result[f["province"]] = 1
        else:
            result[f["province"]] += 1

    # 對統計資料進行過濾,去除沒有省份的資料和小於100的資料
    result_gt100 = {}
    for k,v in result.items():
        if v >= 100:
            if k.strip():
                result_gt100[k] = v

    # 對資料按照省份值進行排序
    result_gt100_sorted = sorted(result_gt100.items(), key=lambda x: x[1], reverse=True)

    # 統計出key列表和value列表,下面生成統計圖使用
    key = []
    value = []
    for k in result_gt100_sorted:
        key.append(k[0])
        value.append(k[1])
    # 柱形圖
    bar = Bar("柱狀圖",width=2000,height=1000)
    bar.add("",key,value,is_label_show=True)
    bar.render(path="柱狀圖.html")

    # 全國地圖
    map = Map("全國地圖示例", width=2000, height=1000)
    map.add("", key, value, maptype='china',is_visualmap=True, visual_text_color="#000",is_map_symbol_show = False,
            visual_range=[0, 1000])
    map.render(path="地圖.html")

    # 詞雲圖
    wordcloud = WordCloud(width=2000, height=1000)
    wordcloud.add("", key, value, word_size_range=[20, 100])
    wordcloud.render(path="詞雲.html")

 

資料分析結果

 

本人校內網好友154位,通過這154位好友,一共抓取到了27215位好友資訊(本人筆名27315,跟這個數字就差100),這裡面未對好友進行去重,如果想要更精準一些,需要按照id去重。其中一位同學居然有3345個好友。。。嗯,是在下輸了。

 

爬取到這些結果後,對資料進行了過濾,去除掉小於100的資料,再去除掉空資料(空資料是因為有些人未設定地區屬性)。下面是結果分析。

 

地區分佈柱形圖:

可以看到有一個省份獨佔鰲頭,相信同學們應該能夠通過此圖猜到小編是哪個省份的了。沒錯,就是排在第一位的黑龍江。

 

去除掉黑龍江之後,我們再來看一下結果:

省份還是比較平均的。

 

地圖分佈:

通過此圖可以看見,好友分佈由東北向西南遞減。注:空白地區不是沒有資料,而是資料過少被過濾掉了。

 

最後看下詞雲結果:

 

原始碼

連結:https://pan.baidu.com/s/1yXS3ryDV2PfFFKN9xdjZAg 密碼:k4nz

 

以上內容為第一次爬取,後來在朋友的建議下做了優化:

昨天釋出了爬取校內網好友分佈的程式碼,然而還不夠完善,在多執行緒的部分聽取了一位朋友的意見,使用了submit方法,保證可以迴圈多次。另外將提取好友資訊的部分單獨拿出來做成了一個方法,方便程式碼以後的管理。由於是在昨日的程式碼中進行了優化,這次我就不寫內容了,直接放上原始碼。大家可以按需下載。

 

想要提醒大家的是,多執行緒如果開的太多,會導致校內網在登陸的時候使用驗證碼驗證,目前還沒有有效的自動識別驗證碼的方法,所以只能開少量執行緒,本人目前開的是3個,實測5個多執行緒在爬取第二輪的時候就會出現驗證碼,大家還是要控制爬取速度。

原始碼:

連結:https://pan.baidu.com/s/1RWQaR-MJbBH-CdRyIr994g 密碼:kt42