沒有多進程,沒有任何黑科技的裸爬蟲。練手用,爬蟲獲取到的數據皆爲公開且非敏感的用戶信息。
目錄
一、思路
在GitHub上已經有網易雲音樂的node.js API(GitHub:https://github.com/Binaryify/NeteaseCloudMusicApi)。根據這個庫提供的信息,可以很輕易的獲取到網易雲音樂獲取某個用戶的粉絲信息接口的參數(接口限制只能獲取100個),進而繼續獲取這100個粉絲的粉絲…簡單的幾層循環嵌套就能很輕易的拿到十萬級到百萬級的用戶數據(非敏感用戶信息)。
二、參數加密流程分析
__getFormData(data, __get_random_str())
參數1:data是dict數據,包含了表單的各個字段和數據
參數2:16位的隨機字符串
最終return的是一個dict,包含了params和encSecKey兩個參數
params
__get_encText(args1, random16str)
參數1:args1是__getFormData函數的參數1,是dict數據,包含了表單的各個字段和數據
參數2:random16str是__getFormData函數的參數2,是一個16位的隨機字符串
最終返回的是將參數加密後產生的params
__AES_encrypt(args1, args4)
參數1:args1是__get_encText函數的參數1,是dict數據,包含了表單的各個字段和數據
參數2:arg4是一個固定參數
最終返回的是將參數使用AES CBC加密後再進行一次base64加密產生的字符串
使用__AES_encrypt函數首先加密一次參數是(args1,args4)得到一個加密的字符串
在使用加密過一次的字符串作爲參數1,和__get_encText函數傳入的參數2 random16str 這個隨機16位的字符串作爲參數2繼續加密1次
最終得到params
encSecKey
__get_encSecKey(random16str)
參數1:random16str是__getFormData函數的參數2,是一個16位的隨機字符串
最終返回的是通過隨機字符串產生的encSecKey
固定參數arg2
固定參數arg3
通過固定算法,使用隨機16位的字符串random16str與這兩個固定參數產生encSecKey
三、代碼實現
common.py (需要用到的函數)
import base64
from Crypto.Cipher import AES
import random
import codecs
import requests
from fake_useragent import UserAgent
def __AES_encrypt(text, key):
'''
獲取到加密後的數據
:param text: 首先CBC加密方法,text必須位16位數據
:param key: 加密的key
:return: 加密後的字符串
'''
iv = "0102030405060708"
pad = 16 - len(text) % 16
if isinstance(text, str):
text = text + pad * chr(pad)
else:
text = text.deocde("utf-8") + pad * chr(pad)
aes = AES.new(key=bytes(key, encoding="utf-8"), mode=2, IV=bytes(iv, encoding="utf-8"))
res = aes.encrypt(bytes(text, encoding="utf-8"))
res = base64.b64encode(res).decode("utf-8")
return res
def __get_encText(args1, random16str):
args4 = "0CoJUm6Qyw8W8jud"
encText = __AES_encrypt(args1, args4)
encText = __AES_encrypt(encText, random16str)
return encText
def __get_encSecKey(random16str):
'''通過查看js代碼,獲取encSecKey'''
arg2 = "010001"
arg3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
text = random16str[::-1]
rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(arg2, 16) % int(arg3, 16)
return format(rs, 'x').zfill(256)
def __get_random_str():
'''這是16位的隨機字符串'''
str_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
random_str = ""
for i in range(16):
index = random.randint(0, len(str_set) - 1)
random_str += str_set[index]
return random_str
def __getFormData(args1, random16str):
'''獲取到提交的數據'''
data = {"params": __get_encText(args1, random16str), "encSecKey": __get_encSecKey(random16str)}
return data
def __getFans(userID):
userDict = {}
userID = str(userID) # userID="6177307"
data = '{"userId":"' + userID + '","time":"-1","limit":"104334","csrf_token": ""}'
formdata = __getFormData(data, __get_random_str())
ua = UserAgent()
session = requests.Session()
headers = {}
headers["content-type"] = "application/x-www-form-urlencoded"
headers["user-agent"] = ua.random
headers["referer"] = "https://music.163.com/"
response = session.post(url='https://music.163.com/weapi/user/getfolloweds', headers=headers,
data=formdata)
results = response.json()
# print(response.status_code)
results = results.get('followeds')
for one in results:
userDict[one.get('userId')] = one
# print(one.get('userId'))
# print(str(one))
return userDict
demo.py (主程序)
# -*- coding: utf-8 -*-
import common
AllData = {}
data = common.__getFans(6177307)
AllData.update(data)
sum = 0
for item in data:
temp = common.__getFans(item)
AllData.update(temp)
for item1 in temp:
temp2 = common.__getFans(item1)
AllData.update(temp2)
for item2 in temp2:
sum += 1
print(sum)
temp3 = common.__getFans(item2)
AllData.update(temp3)
with open('fans.txt', 'a', encoding='utf-8') as f:
for one in AllData.items():
f.write(str(one) + '\n')
print(str(one))
四、數據
我把demo.py放到了服務器上運行,跑到程序結束大概用了24小時左右後看了一下存儲的文本有50W左右的用戶數據(不含敏感信息),以下用戶信息爲網易雲公開數據,並且已經對部分數據進行打碼。(已通過網易雲音樂客戶端私信徵求用戶同意)
(8****823, {'py': 'm**st', 'time': 1510*****4852, 'userId': 8******3, 'mutual': False, 'followed': False, 'accountStatus': 0, 'nickname': '名劍****收天', 'avatarUrl': 'http://p1.music.126.net/uocXBF145t-_V0pLWDwv0w==/3272146604393759.jpg', 'gender': 1, 'expertTags': None, 'experts': None, 'followeds': 13, 'remarkName': None, 'follows': 19, 'authStatus': 0, 'userType': 0, 'vipType': 0, 'signature': '天下若傾,尚有儒門一手擎天!', 'vipRights': None, 'eventCount': 1, 'playlistCount': 5})
(30****375, {'py': 'tt******y', 'time': 15107******003, 'userId': 305******5, 'mutual': False, 'followed': False, 'accountStatus': 0, 'nickname': '甜****ny', 'avatarUrl': 'http://p1.music.126.net/v9iyq-6I1WC96R7SlbKvXQ==/3420580709664324.jpg', 'gender': 1, 'expertTags': None, 'experts': None, 'followeds': 4, 'remarkName': None, 'follows': 30, 'authStatus': 0, 'userType': 0, 'vipType': 11, 'signature': None, 'vipRights': {'associator': {'vipCode': 100, 'rights': True}, 'musicPackage': None, 'redVipAnnualCount': -1}, 'eventCount': 0, 'playlistCount': 3})
(35****22, {'py': 'x******jl-_', 'time': 150941******9, 'userId': 35******2, 'mutual': False, 'followed': False, 'accountStatus': 0, 'nickname': '醒*****家了-_', 'avatarUrl': 'http://p1.music.126.net/KcAVTPDSC8MrKaFB9_Vd9g==/109951163985306640.jpg', 'gender': 1, 'expertTags': None, 'experts': None, 'followeds': 4, 'remarkName': None, 'follows': 9, 'authStatus': 0, 'userType': 0, 'vipType': 0, 'signature': '身邪不怕影子正', 'vipRights': None, 'eventCount': 0, 'playlistCount': 7})
#python #網易雲音樂 #爬蟲