一点资讯js解密

一、需求

目标网站:http://www.yidianzixun.com/
抓取范围:各频道页面下的数据

二、调试

1、确定加密参数

抓包,找到数据接口:http://www.yidianzixun.com/home/q/news_list_for_channel?channel_id=21422801782&cstart=0&cend=10&infinite=true&refresh=1&__from__=pc&multi=5&_spt=yz~eaod8%3B>882%3A%3B%3D28%3A%3B%3A&appid=web_yidian&_=1557501340007
参数说明:

  • channel_id:频道id
  • cstart, cend:开始,结束位置,用于翻页
  • _spt:加密参数
  • _:时间戳

请求该链接时,未正常返回数据,所以将headers全部拷贝,再次请求,有数据,多次尝试后,确定headers中必须的参数:

  • Referer:请求不同频道该参数可以不变
  • Cookies中的JSESSIONID参数

2、调试过程

(1)_splt参数

全局搜索_spt参数,找到js文件:http://static.yidianzixun.com/modules/build/index_pc/channel-6502fbb6.js
生成_spt的js代码如下:

void 0 !== (t = function(n, e, i) {
    e.encodeToken = function(n, e, i, t) {
        for (var o = "sptoken", a = "", c = 1; c < arguments.length; c++)
            o += arguments[c];
        for (var c = 0; c < o.length; c++) {
            var r = 10 ^ o.charCodeAt(c);
            a += String.fromCharCode(r)
        }
        return n += (/\?/.test(n) ? "&_spt=" : "?_spt=") + encodeURIComponent(a)
    }

通过断点调试,确定arguments的参数构成:“sptoken” + channel_id + cstart + cend,通过字符和unicode码转换生成对应的_spt参数

def get_spt(id, start=0, end=10):
    str_par = 'sptoken{}{}{}'.format(id,start, end)
    spt = ''
    for key in str_par:
        spt += chr(10^ord(key))
    return spt

(2)JSESSIONID参数

清空缓存后,全局搜索JSESSIONID对应的值,在请求频道对应的链接(如首页:http://www.yidianzixun.com/ )时,返回cookie中存在该值
在这里插入图片描述
同时多次测试,发现每次清空缓存后,刷新页面,获取的JSESSIONID不同,同时同一频道对应的channel_id每次也都不同,因此判断JSESSIONID与channel_id存在对应关系,且一次请求可以获得一个JSESSIONID和对应的28个频道的channel_id

三、总结

1、注意点

  • IP限制
    请求次数过多会封ip,我在调试过程中就被封了,体现在清空缓存刷新页面时,它提示你 “暂无更新,休息一会儿”,同时请求频道对应的链接时,没有返回JSESSIONID值
  • 获取JSESSIONID
    请求链接获取JSESSIONID数据时,有一定几率失败,多加几次重试,且获取一次JSESSIONID可用时间很长,不必频繁

2、知识点

在这里插入图片描述

四、代码实现

import json
import re
import time
import requests

def get_spt(id, start=0, end=10):
    str_par = 'sptoken{}{}{}'.format(id,start, end)
    spt = ''
    for key in str_par:
        spt += chr(10^ord(key))
    return spt

if __name__ == '__main__':
    ids = set()
    url = 'http://www.yidianzixun.com/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
    }
    while True:
        response = requests.get(url, headers=headers, timeout=15)
        if response.headers.get('Set-Cookie'):
            Cookies = response.headers.get('Set-Cookie').split(';')
            for cookie in Cookies:
                if re.findall(r'JSESSIONID=(.+)', cookie, re.S):
                    set_cookie = re.findall(r'JSESSIONID=(.+)', cookie, re.S)[0]
        else:
            print('----获取cookies失败')
            time.sleep(1)
            continue
        data_json = re.findall(r'window.yidian.docinfo.*?(\{.*?)</script>', response.text, re.S)[0].replace('\\x2f', '')
        data_dict = json.loads(data_json)
        channel_list = data_dict['user_info']['user_channels']
        for j in range(5):
            for channel in channel_list:
                id = channel.get('fromId')
                if not id:
                    print('no id')
                    continue
                offset = 30
                start, end = 0, offset
                for i in range(300):
                    list_url = 'http://www.yidianzixun.com/home/q/news_list_for_channel?channel_id={}&cstart={}&cend={}&infinite=true&refresh=1&__from__=pc&multi=5&_spt={}&appid=web_yidian&_={}'.format(id, start, end, get_spt(id, start, end), int(time.time()*1000))
                    headers = {
                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
                        'Referer': 'http://www.yidianzixun.com/channel/c3',
                        'Cookie': 'JSESSIONID={}'.format(set_cookie)
                    }
                    response = requests.get(list_url, headers=headers, timeout=15)
                    res_dict = json.loads(response.text)
                    if isinstance(res_dict, str):
                        print('---返回结果为字符串, response:{}'.format(res_dict))
                        continue
                    if res_dict.get('reason'):
                        print('-----抓取完成, resonse:{}, channel:{},data_list:{}, ids:{}, pg:{}, start:{}, end:{}'.format(res_dict.get('reason'), channel.get('name'), len(data_list), len(ids), i, start, end))
                        break
                    try:
                        data_list = res_dict['result']
                    except:
                        print(res_dict)
                        continue
                    for data in data_list:
                        if data['itemid'] not in ids:
                            ids.add(data['itemid'])
                    time.sleep(0.5)
                    print('-----channel:{},data_list:{}, ids:{}, pg:{}, start:{}, end:{}'.format(channel.get('name'), len(data_list), len(ids), i, start, end))
                    start, end = start+len(data_list), start+len(data_list)+offset

没有更多推荐了,返回首页