即刻APP所有用户信息爬虫
作为即刻的一个老(si)用(zhong)户(fen),不得不向大家推荐这款非常棒的APP,里面的人说话都好听,个个都是人才,借用瓦总的话说:有一种小镇特有的感觉。之前一直想爬取下来这个APP上面的用户,因为看到好多用户名特别有意思,还有好多盗版用户哈哈哈,所以闲着也是闲着,不如写个爬虫来爬一个这个做爬虫的软件。
一、思路
由于即刻目前出了一个网页beta版,相对于APP直接抓包来说比较方便,所以就针对这个网站即刻网页版来进行爬取。然而要想爬取这个软件上的所有用户信息来说并不是想象中的那么简单,首先这个网站的用户个人界面不是按照完全有规律的id进行排序的,每个人都有自己的username,所以不能直接找规律穷举来爬,必须找到比较合适的接口,然后我发现可以根据每个人的被关注信息来进行广度优先遍历,即先找到一个大v号比如即刻首席哈哈官,这个官方号有大约八万人关注,然后我们就可以把这八万人爬取到数据库中,然后再爬取关注这些人的账号到数据库中,这样遍历理论上是可以把所有用户信息爬取到的。
二、爬取准备
1,接口分析
首先进行分析这个关注人数的接口,通过我的观察发现即刻的个人关注人数的接口是非常有规律的,当然像即刻这种动态网站是不可能爬取html界面来获取信息的,肯定是要分析后台的json包来进行动态爬取。
直接打开后台找到名字叫做getFollowerList的包,分析这个包的header信息我们发现每次请求的url都是一样的,只是通过不同的request payload数据post过去得到关注者的信息。由于这个关注列表是下拉动态加载的,每次下拉返回20个人的关注信息,故只要找到每次下拉的loadmorekey就能得到下一个json包了。
每次的loadmorekey都可以在上一个得到的getfollowedList返回包里得到,这样我们就找到了规律,就能遍历得到一个人他的所有的关注人的信息了,然后每次可以通过换request payload里面的username值来获取不同人的关注人信息。这样我们就有了大体的爬取思路了。
2,x-jike-access-token值的更新
当我写完爬虫来进行爬取的时碰到了一个比较头疼的问题,就是这个网站的cookies是动态更新的,而我却一直没找到他是怎么更新的,所以我刚开始爬的时候不能完全自动爬取,也就是爬取大概十分钟就要手动更新一下x-jike-access-token值。当我正苦恼于解决这个问题上网无限找关于cookies保持的资料时,我无意中又看了一下即刻的后台,突然发现又突然多了一个数据包
没错就是这个叫做app_auth_tokens.refresh的json包,点开分析他的header发现,wooooo,这不就是更新token的包么,这样问题突然又变的非常简单了。。。。。。。。。
三、开始肝代码
分析到这里这个爬虫思路应该就非常清晰了,由于即刻网页版是处于内测版,目前还没有太多反爬的措施,最多也就是更新那个token值。主要还是写好爬取的逻辑。
首先是html_downloader函数,由于是post请求所以要把关键的信息加上去
def html_downloader(url,payload,access_token):
headers = {"Accept":"application/json",
"App-Version":"4.1.0",
"Content-Type":"application/json",
"Origin":"https://web.okjike.com",
"platform":"web",
"Referer":"https://web.okjike.com/user/JTYSMHXD/follower",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"}
headers["x-jike-access-token"] = str(access_token)
r = requests.post(url,headers= headers, data=json.dumps(payload))
content = r.text
return content
然后就是得到下拉界面的payload获取
def get_payload(content,usern):
text = json.loads(content)
loadkey = text["loadMoreKey"]
payload ={}
payload["loadMoreKey"] = loadkey #从上一个getfollowedlist里面得到的
payload["username"] = usern #这里就是要爬取的人的username
payload["limit"] = 20
return payload
数据库创建语句
CREATE TABLE `jike_user` (
`username` text NULL ,
`screenName` text NULL ,
`createdAt` text NULL ,
`updatedAt` text NULL ,
`isVerified` text NULL ,
`verifyMessage` text NULL ,
`briefIntro` text NULL ,
`bio` text NULL ,
`thumbnailUrl` text NULL ,
`picUrl` text NULL ,
`gender` text NULL ,
`city` text NULL ,
`country` text NULL ,
`province` text NULL ,
`following` text NULL ,
`ref` text NULL ,
`topicSubscribed` text NULL ,
`topicCreated` text NULL ,
`followedCount` text NULL ,
`followingCount` text NULL ,
`highlightedPersonalUpdates` text NULL ,
`liked` text NULL ,
`id` int NOT NULL ,
PRIMARY KEY (`id`)
)
用户信息json包处理并写入数据库函数
def parser(content):
text = json.loads(content)
data = text["data"]
db = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='jike', charset='utf8')
# 使用cursor()方法获取操作游标
cursor = db.cursor()
for i in data:
#用户个人信息
username = i["username"] #个人id
screenName = i["screenName"]#昵称
createdAt = i["createdAt"]#注册日
updatedAt = i["updatedAt"]#最后一次登陆
isVerified = i["isVerified"]#是否即刻认证用户
verifyMessage = i["verifyMessage"] #
briefIntro = i["briefIntro"] #个人简介&什么话题贡献者
bio = None#i["bio"] #个人简介
thumbnailUrl = i["avatarImage"]["thumbnailUrl"]#头像缩略图
picUrl = i["avatarImage"]["picUrl"] #头像图片
#这里是处理有些人的个人简介里面会出现表情字符导致导入utf-8编码的数据库错误
try:
# python UCS-4 build的处理方式
highpoints = re.compile(u'[\U00010000-\U0010ffff]')
except re.error:
# python UCS-2 build的处理方式
highpoints = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
briefIntro = highpoints.sub(u'??', briefIntro)
briefIntro =briefIntro.replace("\'"," ")
briefIntro = str(briefIntro)
#由于有些人的性别,城市,国家信息没有填写,也即是获得的json包里面没有这些信息,故导入数据库中按照空值处理
try:
gender=i["gender"]
city= i["city"]
country =i["country"]
province =i["province"]
except:
gender=None
city= None
country =None
province =None
following = i["following"]
ref = i["ref"]
#用户状态
topicSubscribed= i["statsCount"]["topicSubscribed"] #话题参与
topicCreated =i["statsCount"]["topicCreated"] #话题创建
followedCount=i["statsCount"]["followedCount"] #被关注的人数
followingCount=i["statsCount"]["followingCount"] #关注的人数
highlightedPersonalUpdates=i["statsCount"]["highlightedPersonalUpdates"]
liked=i["statsCount"]["liked"] #被赞数
#print(username, screenName,briefIntro)
sql1 = "select count(id) from jike_user where username = ('%s')" %(username)#判断数据是否已出现在数据中
cursor.execute(sql1)
numb = cursor.fetchone()
if numb[0] == 0:
sql = "INSERT INTO jike_user(username,screenName,createdAt,updatedAt,isVerified,verifyMessage,briefIntro, \
bio,thumbnailUrl,picUrl,gender,city,country,province,following, \
ref,topicSubscribed,topicCreated,followedCount,followingCount,\
highlightedPersonalUpdates,liked) \
values('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')" \
%(username,screenName,createdAt,updatedAt,isVerified,verifyMessage,briefIntro, \
bio,thumbnailUrl,picUrl,gender,city,country,province,following, \
ref,topicSubscribed,topicCreated,followedCount,followingCount,\
highlightedPersonalUpdates,liked)
try:
cursor.execute(sql)
except:
pass
else:
pass
db.commit()
# 关闭数据库连接
db.close()
更新accesstoken值的函数
def refresh_token(refresh_token):
url = "https://app.jike.ruguoapp.com/app_auth_tokens.refresh"
headers = {"Origin":"https://web.okjike.com",
"Referer":"https://web.okjike.com/user/JTYSMHXD/follower",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"}
headers["x-jike-refresh-token"] = str(refresh_token)
r = requests.get(url,headers= headers)
print(r.status_code)
content = r.text
return content
突然发现自己造了这么多轮子,代码逻辑还是有点混乱。。。。
完整代码放到我的GitHub了,想看的可以自取。
目前这个爬虫还没有运行完,只爬取了60万用户数据,由于是单线程爬取,所以速度大概在一天爬取10万用户左右,不过讲道理越往后爬速度会越来越慢,因为会存在越来越多的重复数据。即刻这个用户数目100万应该封顶了吧。。。。
通过这个爬虫爬取的数据我们可以发现很多店长。。。。
还有瓦总的哈哈哈哈哈哈
今晚八点村口蹦迪啥也不说了。