1. 程式人生 > >爬取nyist-6000張證件照進行微軟小冰顏值分析

爬取nyist-6000張證件照進行微軟小冰顏值分析

Python爬取nyist-6000張證件照進行顏值分析

前言

前幾天學校要求更新檔案資料庫的照片,所以要求每個人去照證件照,大多數人是在學校裡面的一個照相的地方照的,為了容易使同學們拿到照片,他們會每天把每個人的證件照上傳到一個網站,於是我就用爬蟲爬取了6000+照片,結合微軟小冰的人臉識別中的顏值分析功能對每張照片進行打分,然後進行一次簡單的顏值分析,下面簡單說一說我的過程,結論在最後

工具準備:

一、爬取並儲存圖片

import re
import requests
from bs4 import BeautifulSoup
import lxml
import os

我們開啟這些連結發現:

因為日期的不同,所以網頁地址變得只是後面的數字,所以我們只需要找到我們要爬取的第一個頁面的標號和最後一個頁面的標號,然後對每一個頁面分別爬取就好了。
隨便開啟一個頁面,會發現圖片是這樣的:

我們解析一下網頁原始碼會發現是這樣的:

這些圖片都無一例外的藏在img標籤裡面的,所以我們只需要把這些裡面的內容提取出來就可以得到每一張圖片的地址了。
提取的方法有很多種,比如用BeautifulSoup,但是這裡由於這個標籤並不需要解析成文件樹,可以直接寫一個正則表示式提取出來,所以我們可以這麼寫:

/upfile/201709/\d+\.JPG

只需要簡單的匹配一下多位數字就可以了,這樣我們可以寫出具體程式碼:

這樣因為正則匹配的緣故,會返回一個包含所有圖片地址的列表,那麼現在我們要做的就是利用這個列表,進行儲存:
這個也很簡單,我們遍歷一下這個列表,然後構造每一個圖片連結,這裡也需要用正則表示式來匹配一下檔名,用requests請求一下把二進位制的東西儲存成.jpg檔案就好了

現在我們的程式碼已經實現了對每個頁面的圖片進行儲存,剩下的就是呼叫這兩個函數了,我們只需要遍歷一下要爬的網頁的id,然後等著爬取完成就好了,爬蟲爬完後會得到一個資料夾,裡面就是你爬好的照片,就像這樣:

到了現在,第一步就做完了,已經擁有這幾千張證件照了

二、呼叫微軟小冰人臉識別中的測顏值功能

首先,我們開啟微軟小冰的識別頁面:

顯示這樣:

這時我們點”上傳圖片”,然後進行抓包

我們發現有兩個post請求,分別是把當前圖片傳到微軟伺服器,還有一個是進行顏值測試,進行顏值測試的時候其中的一個引數要傳入當前這個圖片在微軟伺服器的地址,所以我們要分兩步來做:

  1. 把圖片上傳至微軟伺服器,並且得到返回的圖片地址
  2. 運用得到的圖片地址,進行顏值顏值測試

通過抓包可以發現兩個post地址,一個是提交圖片的地址,另一個是獲得顏值分數的地址:

upload_url = 'http://kan.msxiaobing.com/Api/Image/UploadBase64'
comp_url = 'https://kan.msxiaobing.com/Api/ImageAnalyze/Process'

我們先說第一個,通過檢視抓到的包,我們會發現每一張圖片在傳入伺服器之前會先經過base64編碼方式對圖片進行編碼,然後會post到微軟的伺服器,然後返回地址,那麼我們就應該先對一張圖片進行base64加密,然後我們會發現返回結果是這樣的:

{'Host': 'https://mediaplatform.msxiaobing.com', 'Url': '/image/fetchimage?key=JMGsDUAgbwOliRBabKjOIkANmt-Cu2pqpAsmCZyiMDJ6wxaO8iaLjrucUw'}

這是一段json資訊,我們需要的是url裡面的值,所以我們就用requests中內建的json解析器對它進行解析,所以,程式碼可以這麼寫:

def get_img_url(file_name):
    with open(file_name,'rb') as f:
        img_base64=base64.b64encode(f.read())
    r=requests.post(upload_url,data=img_base64)
    url='https://mediaplatform.msxiaobing.com' + r.json()['Url']
    return url

我們通過把二進位制資訊進行編碼,然後傳入微軟伺服器,獲得了這一張圖片在伺服器中的地址,然後在通過構造,得到完整的地址

那麼我們現在進行第二步,把資訊發到微軟伺服器,然後得到地址,我們看一下抓的包:

Remote Address:42.159.132.179:443
Request URL:https://kan.msxiaobing.com/Api/ImageAnalyze/Process?service=yanzhi&tid=a14a6fd556cf45c181cb116f06471450
Request Method:POST
Status Code:200 OK
Response Headers
view source
Cache-Control:no-cache
Content-Encoding:gzip
Content-Type:application/json; charset=utf-8
Date:Thu, 07 Sep 2017 17:18:28 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Powered-By:ASP.NET
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8
Connection:keep-alive
Content-Length:194
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Cookie:_ga=GA1.2.1597838376.1504599720; ai_user=sp1jt|2017-09-05T10:53:04.090Z; ARRAffinity=d48a0dd61144e14e99ea10eca4fef5d81bf07933a2831679353d2761cf0db5aa; cpid=YE5cTN4wQk4hTFdMf7FVTzM0bjPlNN1MJrFATNhI10hNAA; salt=761703E5A961C3EC8AD465F2A70F639F; ai_session=pWq6U|1504804573893.97|1504804670104.895
Host:kan.msxiaobing.com
Origin:https://kan.msxiaobing.com
Referer:https://kan.msxiaobing.com/ImageGame/Portal?task=yanzhi&feid=541a6dd6cd178819892a917dd772e702
User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
x-ms-request-id:4KL3u
x-ms-request-root-id:idMkH
X-Requested-With:XMLHttpRequest
Query String Parameters
view source
view URL encoded
service:yanzhi
tid:a14a6fd556cf45c181cb116f06471450
Form Data
view source
view URL encoded
MsgId:1504804707562
CreateTime:1504804707
Content[imageUrl]:https://mediaplatform.msxiaobing.com/image/fetchimage?key=EjJdNJc119RQMNM3GzUGslUzG3NxdRU1EnQqNFt2MVJPbiOGhpbpzmOmXA

我們要構造data裡面的資訊,也就是要構造MsgId,CreateTime,Content[imageUrl]這三個資訊,首先我們觀察MsgId,發現它實質上就是當前的時間戳加了一個3位數的隨機數,而第二個CreateTime其實就是當前的時間戳,第三個Content[imageUrl]那當然就是我們剛剛得到的圖片地址了,我們就把這些引數給發上去

然後把這些請求發上去以後,會發現它的返回結果是none,那麼這是為什麼呢,這是因為微軟添加了cookiesrefer驗證,所以我們要把這兩個東西放在headers裡面一起給傳過去,然後就會發現它返回一段json:

{'msgId': '4d39f92788774d65bd0261d6f4754865', 'timestamp': 1504805911673, 'receiverId': None, 'content': {'text': '有潛力的妹子!6.6分!性格肯定超好,男神最喜歡這種女孩紙咯,羨慕!', 'imageUrl': 'http://mediaplatform.trafficmanager.cn/image/fetchimage?key=UQAfAC8ABAAAAFcAFgAGABYASgAxAEIANwBEAEMAQwA4AEIAMAA3AEQAQQBGADYAMgA4AEQAMwA5AEMAMwA1AEMAMQAwADcANwA5ADMAMwAxAEUA', 'metadata': {'AnswerFeed': 'FaceBeautyRanking', 'w': 'vvXoifT_gNLyhvXNhsPhjMz_pt_kbkdXisHliuToh-_uhcXkgePArM3_sN_kiuzeiuTlhvv-hMDGj_3vrPX5vsXDh_b6gv_khMr6hsjxjtvXrPHKt9XtiPfCg938jtPI', 'aid': '303DB2A95867544E3E5EE047977CECF4'}}}

我們按照慣例,對它進行解析,我們提取出來['content']['text']裡面的內容就行了,所以這一段的程式碼:

def get_score(img_url):
    sys_time = int(time.time())
    payload = {'service': 'yanzhi',
               'tid': '7531216b61b14d208496ee52bca9a9a8'}
    form = {
        'MsgId': str(sys_time) + '733',
        'CreateTime': sys_time,
        'Content[imageUrl]': img_url,
    }
    headers={
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
        'Cookie':'_ga=GA1.2.1597838376.1504599720; _gid=GA1.2.1466467655.1504599720; ai_user=sp1jt|2017-09-05T10:53:04.090Z; cpid=YDLcMF5LPDFfSlQyfUkvMs9IbjQZMiQ2XTJHMVswUTFPAA; salt=EAA803807C2E9ECD7D786D3FA9516786; ARRAffinity=3dc0ec2b3434a920266e7d4652ca9f67c3f662b5a675f83cf7467278ef043663; ai_session=sQna0|1504664570638.64|1504664570638'+str(random.randint(11, 999)),
        'Referer': 'https://kan.msxiaobing.com/ImageGame/Portal?task=yanzhi&feid=d89e6ce730dab7a2410c6dad803b5986'
    }
    r = requests.post(comp_url,params=payload, data=form,headers=headers)
    print(r.json())
    text=r.json()['content']['text']
    return text

這樣的話,我們就可以獲得小冰對這個照片的評價,那麼對於後期處理,我們只要用正則表示式把其中的關於顏值分數的地方提取出來就好了,那麼這時候就需要儲存剛剛獲得的評價分數,所以我們採用的方式就是對檔案重新命名,就是把檔名改成:分數+原來的檔名的形式,程式碼如下:

為了方便我們對於程式的除錯,所以用一個txt檔案隨時把程式的log資訊傳進去,就像這樣:

至此,打分環節也完成了,下面進行下一項

三、把分數用圖表的方式繪製出來

這一步需要用到matplotlib庫中的pyplot,用它可以繪製出很多種不同的圖,先引入庫:

import matplotlib.pyplot as plt
import os
plt.rcParams['font.sans-serif']=['SimHei'] #用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus']=False #用來正常顯示負號

由於微軟小冰的API呼叫次數的限制,我只成功的對2118張圖片進行了顏值的打分,很容易的得到了每個分數段區間的數量:

def get_score():
    score_num=[]
    for i in range(6):
        score_num.append(0)
    list=os.listdir()
    for name in list:
        if len(name)==24:
            score=float(name[:3])
            if score>=3 and score<4:
                score_num[0]+=1
            elif score>=4 and score<5:
                score_num[1]+=1
            elif score>=5 and score<6:
                score_num[2]+=1
            elif score>=6 and score<7:
                score_num[3]+=1
            elif score>=7 and score<8:
                score_num[4]+=1
            elif score>=8 and score<=9:
                score_num[5]+=1
    return score_num

這樣返回一個列表,裡面都是每個分數區間裡面的人數。

現在我們要做的就是繪製統計圖,就繪製成條形圖和餅圖,具體的用法請參考matplotlib的文件,我把繪製圖的程式碼直接貼出來:

柱形圖:

def autolabel(rects):
    for rect in rects:
        height = rect.get_height()
        plt.text(rect.get_x()+ 0.3, height+ 0.05, '%d' % height, ha='center', va='bottom')

def draw_column():
    score=get_score()
    plt.title('nyist證件照顏值分數分析   總數:2118')
    plt.xlabel('顏值區間')
    plt.ylabel('人數')
    plt.xticks([0,1,2,3,4,5], ['3~4分', '4~5分','5~6分','6~7分','7~8分','8~9分'])
    rect=plt.bar(left=(0,1,2,3,4,5), height=score, width=0.7, align="center")
    plt.legend((rect,), ('數量',))
    autolabel(rect)
    plt.show()

餅圖:

def draw_circle():
    score = get_score()
    labels='3~4分', '4~5分','5~6分','6~7分','7~8分','8~9分'
    sizes=score
    explode=(0,0,0,0.05,0,0)
    plt.pie(sizes,explode=explode,labels=labels,autopct='%1.1f%%',shadow=False,startangle=90)
    plt.axis('equal')
    plt.show()

最後的結果如下圖所示:


顏值突出的非常少,只有2%,但是大部分長得還過的去,估計是微軟小冰不好意思打特別低的分~~微軟小冰的顏值分析是有限制的,一分鐘限制10次,一個小時限制120次,一天限制360次,我用代理ip的方式也會被限制,所幸路由器重新撥號可以破除這個限制,於是就斷斷續續爬了2000+照片寫了這個部落格~

tips:至於照片檔案,還是私下找我要吧~