Python爬蟲入門教程 16-100 500px攝影師社群抓取攝影師資料

夢想橡皮擦發表於2018-12-25

寫在前面

今天要抓取的網站為 https://500px.me/ ,這是一個攝影社群,在一個攝影社群裡面本來應該爬取的是圖片資訊,可是我發現好像也沒啥有意思的,忽然覺得爬取一下這個網站的攝影師更好玩一些,所以就有了這篇文章的由來。

Python爬蟲入門教程 16-100 500px攝影師社群抓取攝影師資料

基於上面的目的,我找了了一個不錯的頁面 https://500px.me/community/search/user

在這裡插入圖片描述

不過細細分析之後,發現這個頁面並不能抓取到儘可能多的使用者,因為下拉一段時間,就不能繼續了,十分糟心,難道我止步於此了麼,顯然不可能的,一番的努力之後(大概廢了1分鐘吧),我找到了突破口,任意開啟一個使用者的個人中心頁,就是點選上述連結的任意使用者頭像,出現如下操作。

在這裡插入圖片描述

使用者個人中心頁面,竟然有關注列表唉~~,nice啊,這個好趴啊,F12分析一下。

在這裡插入圖片描述

噠噠噠,資料得到了。 URL是 https://500px.me/community/res/relation/4f7fe110d4e0b8a1fae0632b2358c8898/follow?startTime=&page=1&size=10&type=json

引數分別如下,實際測試發現size可以設定為100

https://500px.me/community/res/relation/{使用者ID}/follow?startTime=&page={頁碼}&size={每頁資料}&type=json
複製程式碼

那麼我們只需要這麼做就可以了

  1. 獲取關注總數
  2. 關注總數除以100,迴圈得到所有的關注者(這個地方為什麼用關注,不用粉絲,是因為被關注的人更加有價值) 明確我們的目標之後,就可以開始寫程式碼了。

擼程式碼

基本操作,獲取網路請求,之後解析頁面,取得關注總數。

使用者的起始,我選擇的id是5769e51a04209a9b9b6a8c1e656ff9566,你可以隨機選擇一個,只要他有關注名單,就可以。 匯入模組,這篇部落格,用到了redismongo,所以相關的基礎知識,我建議你提前準備一下,否則看起來吃力。

import requests
import threading

from redis import StrictRedis
import pymongo

#########mongo部分#########################
DATABASE_IP = '127.0.0.1'
DATABASE_PORT = 27017
DATABASE_NAME = 'sun'
client = pymongo.MongoClient(DATABASE_IP,DATABASE_PORT)
db = client.sun
db.authenticate("dba", "dba")
collection = db.px500  # 準備插入資料

#########mongo部分#########################

#########redis部分#########################
redis = StrictRedis(host="localhost",port=6379,db=1,decode_responses=True)
#########redis部分#########################


#########全域性引數部分#########################
START_URL = "https://500px.me/community/v2/user/indexInfo?queriedUserId={}" # 入口連結
COMMENT = "https://500px.me/community/res/relation/{}/follow?startTime=&page={}&size=100&type=json"
HEADERS = {
    "Accept":"application/json",
    "User-Agent":"你自己去找找可用的就行",
    "X-Requested-With":"XMLHttpRequest"
}

need_crawlids = []  # 待爬取的userid

lock = threading.Lock() # 執行緒鎖
#########全域性引數部分#########################
複製程式碼
def get_followee():
    try:
        res = requests.get(START_URL.format("5769e51a04209a9b9b6a8c1e656ff9566"),
        headers=HEADERS,timeout=3)
        data = res.json()
        if data:
            totle = int(data["data"]["userFolloweeCount"])  # 返回關注數
            userid = data["data"]["id"]	# 返回使用者ID
            return {
                "userid":userid,
                "totle":totle
            }  # 返回總資料
    except Exception as e:
        print("資料獲取錯誤")
        print(e)
if __name__ == '__main__':
    start = get_followee()  # 獲取入口
    need_crawlids.append(start)
    
複製程式碼

上面程式碼中有一個非常重要的邏輯,就是為什麼要先匹配種子地址的【關注數】和【使用者ID】,這兩個值是為了拼接下面的URL https://500px.me/community/res/relation/{}/follow?startTime=&page={}&size=100&type=json 經過分析,你已經知道,這個地方第一個引數是使用者id,第二個引數是頁碼page,page需要通過關注總數除以100得到。不會算的,好好在紙上寫寫吧~

我們可以通過一個方法,獲取到了種子使用者的關注列表,以此繼續爬取下去,完善生產者程式碼。關鍵程式碼都進行了註釋標註。

思路如下:

  1. 死迴圈不斷獲取need_crawlids 變數中的使用者,然後獲取該使用者的關注者列表。
  2. 爬取到的資訊,寫入redis方便驗證重複,快速儲存。
class Product(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self._headers = HEADERS

    def get_follows(self,userid,totle):
        try:
            res = requests.get(COMMENT.format(userid,totle),headers=HEADERS,timeout=3)
            data = res.json()

            if data:
                for item in data:
                    yield {
                        "userid":item["id"],
                        "totle":item["userFolloweeCount"]
                    }
        except Exception as e:
            print("錯誤資訊")
            print(e)
            self.get_follows(userid,totle)  # 出錯之後,重新呼叫

    def run(self):

        while 1:
            global need_crawlids  # 呼叫全域性等待爬取的內容

            if lock.acquire():
                if len(need_crawlids)==0:  # 如果為0,無法進入迴圈
                    continue

                data = need_crawlids[0]  # 取得第一個
                del need_crawlids[0]  # 使用完刪除

                lock.release()

            if data["totle"] == 0:
                continue

            for page in range(1,data["totle"]//100+2):
                for i in self.get_follows(data["userid"],page):
                    if lock.acquire():
                        need_crawlids.append(i)  # 新獲取到的,追加到等待爬取的列表裡面
                        lock.release()
                    self.save_redis(i)  # 儲存到redis裡面


    def save_redis(self,data):
        redis.setnx(data["userid"],data["totle"])
        #print(data,"插入成功")

複製程式碼

由於500px無反爬蟲,所以執行起來速度也是飛快了,一會就爬取了大量的資料,目測大概40000多人,由於我們是寫教程的,我停止了爬取。

Python爬蟲入門教程 16-100 500px攝影師社群抓取攝影師資料

在這裡插入圖片描述

這些資料不能就在redis裡面趴著,我們要用它獲取使用者的所有資訊,那麼先找到使用者資訊介面,其實在上面已經使用了一次 https://500px.me/community/v2/user/indexInfo?queriedUserId={} 後面的queriedUserId對應的是使用者id,只需要從剛才的資料裡面獲取redis的key就可以了,開始編寫消費者程式碼吧,我開啟了5個執行緒抓取。

class Consumer(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while 1:
            key = redis.randomkey() # 隨機獲取一個key
            if key:
                # 刪除獲取到的key
                redis.delete(key)
                self.get_info(key)



    def get_info(self,key):
        try:
            res = requests.get(START_URL.format(key),headers=HEADERS,timeout=3)
            data = res.json()
            if data['status'] == "200":
                collection.insert(data["data"])  # 插入到mongodb中
        except Exception as e:
            print(e)
            return
if __name__ == '__main__':
    start = get_followee()  # 獲取入口
    need_crawlids.append(start)


    p = Product()
    p.start()

    for i in range(1,5):
        c = Consumer()
        c.start()

複製程式碼

程式碼沒有特別需要注意的,可以說非常簡單了,關於redis使用也不多。

redis.randomkey() # 隨機獲取一個key
redis.delete(key)  # 刪除key
複製程式碼

(⊙o⊙)…經過幾分鐘的等待之後,大量的使用者資訊就來到了我的本地。

Python爬蟲入門教程 16-100 500px攝影師社群抓取攝影師資料

完整程式碼評論留言傳送。

寫在後面

emmmmmm...... 天天在CSDN寫部落格,明天就爬CSDN部落格吧~~~

相關文章