收集了大家的问题。我又重新写了一篇websocket的代码,并添加了注释,在文章最后。希望可以解决大家遇到的问题~
websocket是最近开发很常用的技术之一,他可以一直保持着连接不断,但是你的页面还可以继续展示其它任务,很适用于直播时候的弹幕等。这个是我自己的基础理解,详细理解大家可以参考:https://segmentfault.com/a/1190000013149749
我觉得针对websocket的数据爬取,一定要有逆向思维,首先找到数据,然后去倒推这个流程,进而掌握数据传送的全过程。而且针对websocket数据爬取,有一个很好的框架大家可以使用
import time
import json
import pandas as pd
import requests
import websocket
from requests.adapters import HTTPAdapter
import time
try:
import thread
except ImportError:
import _thread as thread
class websocket_class :
def __init__(self):
pass
#这里就是websocket爬虫的核心,发送请求,接收数据并做最后处理,
def on_message(self,ws, message):
pass
def on_error(self,ws, error):
print(error)
#关闭websocket长连接
def on_close(self,ws):
print("关闭连接")
#程序运行第一步
def on_open(self,ws):
def run(*args):
#这里面就是写大家倒退出来页面请求第一步的代码
pass
thread.start_new_thread(run, ())
if __name__ == "__main__":
header = {
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,',
'Cache-Control': 'no-cache',
'Connection': 'Upgrade',
'Cookie': cookie,
'Host': 'ws-nextbi.yushanfang.com',
'Origin': 'https://nextbi.yushanfang.com',
'Pragma': 'no-cache',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
#这个参数要进行实时修改
'Sec-WebSocket-Key': 'QBn6rnK29DZL6BC6+O2TRA==',
'Sec-WebSocket-Version': '13',
'Upgrade': 'websocket',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
websocket.enableTrace(True)
websocket_obj = websocket_class(cookie, appId, cateId,filename)
ws = websocket.WebSocketApp("wss://ws-nextbi.yushanfang.com/",
on_message = websocket_obj.on_message,
on_error = websocket_obj.on_error,
on_close = websocket_obj.on_close,
header=header)
ws.on_open = websocket_obj.on_open
ws.run_forever()
首先大家可以看到下图,就证明此数据是websocket传输
w
大家可以看到我标红的地方有两个箭头,绿色的就是我们需要模拟的请求,红色朝下的就是请求对应的数据,那么问题来了,页面上那么多请求和接收,我们如何知道哪个请求对应哪个数据呢?
这个rid就是我们找一对请求和接受的依据。大家肯定注意到,这个rid和时间戳很类似,没错这就是一个13位的时间戳,和随机数组合而成的
randomID = str(int(time.time()*1000))+str(self.count).zfill(3)
大家可以依据我上面说的这些,结合自己的网站写爬虫代码。
#这是我的一部分请求代码
randomID = str(int(time.time()*1000))+str(self.count).zfill(3)
self.targetID['tagTitle'][randomID]= self.targetID['targetSecondID'][subrid]
sendMessage = {"method":"/iWidget/list","headers":{"rid":randomID,"type":"PULL"},"body":{"args":{"sheetId":message['body'],"appId":self.appId}}}
ws.send(json.dumps(sendMessage))
我遇到的问题:
1.爬取的时候总是有一大串的报红
我的理解是发送的请求并没有接受的数据,因为rid是根据当前的时间在不停的生成时间戳,所以会出现一些时间戳并没有匹配的数据,当人大家可以看到白色的字体就是我接受的数据。
2.无法批量拉取数据,因为websocket是长连接,批量拉取的话,如果爬取第二个数据,第一个数据的长连接总是无法关掉,导致数据重复爬取,我有进行强制关闭,依然无法批量。
#下面我放一份我自己写的一个关于websocket 抓取数据的代码。
import websocket
from requests.adapters import HTTPAdapter
from mybaseCode import *
try:
import thread
except ImportError:
import _thread as thread
class websocket_class :
count =0
sheetIds = [] #获取所有的标签ID
targetID = {}
targetID['getDataOnWidgetV2'] = {}
targetID['targetSecondID'] = {}
targetID['tagTitle'] = {} #标签名称
targetID['tagTitleTT'] = {} # 标签名称
#需要定期去修改
newDF = pd.DataFrame()
def __init__(self,cookie, appId, brandId,date):
# 配置requests
self.s = requests.Session()
self.NETWORK_STATUS = False
self.REQUEST_TIMEOUT = False
# 配置requests超时重试
self.s.mount('http://', HTTPAdapter(max_retries=3))
self.s.mount('https://', HTTPAdapter(max_retries=3))
self.date=date
self.appId = appId
self.brandId = brandId
self.insertCookie(cookie)
self.getsheetIds()
self.n=0
self.final=pd.DataFrame(columns=['类目','渠道','时间',
'销售金额-本品牌', '销售金额同比-本品牌',
'购买人数-本品牌','购买人数同比-本品牌',
'客单价-本品牌','客单价同比-本品牌',
'人均购买叶子类目数-本品牌', '人均购买叶子类目数同比-本品牌',
'新进叶子类目数-本品牌','新进叶子类目数同比-本品牌'
])
#这个是我封装的cooke请求
def insertCookie(self,cookie):
# 读取cookie
cookies = []
try:
for line in cookie.split(';'):
name, value = line.strip().split('=', 1)
cookies.append({'name': name, 'value': value})
except ValueError:
print('cookie格式错误!')
return {'errCode': -1, 'errMsg': 'cookie格式错误!'}
# cookie注入到requests.Session
for cookie in cookies:
if cookie['name'] == '_tb_token_':
self.x_csrf_token = cookie['value']
self.s.cookies.set(cookie['name'], cookie['value'])
#这里是为了爬虫统一封装的参数
def requests_method(self, url):
try:
result = self.s.get(url,
headers={
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6',
'cache-control': 'no-cache',
'pragma':'no-cache',
'referer':'https://databank.tmall.com/',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
).text
return result
except Exception as e:
print(e)
return False
#这个自定义函数是我发现websocket请求之前有一些参数我需要通过这个请求拿到
def getsheetIds(self):
url='https://strategy.tmall.com/api/scapi?path=/api/v1/category/strategy/listRelation&brandId='+self.brandId
sendPackage = self.requests_method(url)
res = json.loads(sendPackage)
self.tagSheetIds = {}
for item in res['data']:
if item['children'] == None:
self.tagSheetIds[item['cateId']] = item['cateFullName']
else:
self.tagSheetIds[item['cateId']] = item['cateFullName']
for childrenId in item['children']:
self.tagSheetIds[childrenId['cateId']] = childrenId['cateFullName']
#这里其实有点自调函数的意思,因为所有通过ws.send()方法接收的message都会到这里,
# 所以你就需要通过下面的rid取判断,此时接收的message到哪一层(或者说是哪个请求反馈的结果。
def on_message(self,ws, message):
print('---------------进入方法-----------------------')
print(message)
message = json.loads(message)
subrid = message['