爬取360Doc用户馆藏存为html-包括js解密

首先当然是最难的,在获取文章分类中的列表时,请求体中的sign值有加密

分析用户请求

获取文章目录下的所有文章列表

进入用户界面后点击相应的文章分类→自动驾驶,请求方式为带参数的get请求,分析请求体,发现sign参数为加密参数,点击不同的分类时sign参数发生变化

爬取360Doc用户馆藏存为html-包括js解密

在sources界面进行全局搜索请求url中的关键字 ‘ArticleHandler’ 得到以下结果

该js为eval(function()){}()直接执行函数生成返回值

爬取360Doc用户馆藏存为html-包括js解密

复制该函数至console中直接执行,得到返回值p,p为js代码,保存到本地后搜索关键字 ‘sign’,得到以下结果,其中json中的各项参数与请求体中的参数一致,表明sign是通过此处 ‘doccgjio.xfejh’ 加密实现

爬取360Doc用户馆藏存为html-包括js解密

接着全局搜索 ‘doccgjio' 后发现doccgjio.js为所有加密算法处理文件

爬取360Doc用户馆藏存为html-包括js解密

接着在将js中代码放进console中执行,并调用doccgjio.xfejh( ),得到的sign值与请求体中的一致,结束,接下来就可以使用PyExecJS库进行处理生成sign值

爬取360Doc用户馆藏存为html-包括js解密

下面附上全部代码,用的都是常规库,目前还有点问题

  1. 跑一段时间会封ip,有兴趣的可以加上随机代理
  2. 为了不给服务器太大压力,没用多线程,并且加入了sleep,跑的比较慢,可以加上多线程跑
import time
import re
import os
import requests
import json
import execjs
import math
import random
from urllib.parse import unquote
from requests_html import HTMLSession


def get_art_cate(userid, url, headers, cur_time):  # 获取文章分类
    params = {'type': 3, 'userid': userid, '-': cur_time}
    req = requests.get(url, headers=headers, params=params)

    # 对响应信息进行urldecode并去除多余信息
    cate_list = unquote(req.content)[1:len(unquote(req.content)) - 3]
    # 将str中字典提取进列表
    cate_list = re.findall(r'{ id.*?"}', cate_list)

    for i in range(len(cate_list)):
        # 去除多余字符串,方便转为字典
        cate_list[i] = cate_list[i].replace('"', '').replace(
            ' ', '').strip('{').strip('}')
        # print(cate_list[i])
        # 将字符串转为字典,方便后续取值
        cate_list[i] = {
            n.split(':')[0]: n.split(':')[1]
            for n in cate_list[i].split(',')
        }
        # print(cate_list[i])

    return cate_list


def get_art_list(userid, url, headers, cur_time, cid,
                 artnum):  # 获取文章某个分类下的所有文章列表
    artnum = math.ceil(int(artnum) / 10)

    for i in range(1, artnum + 1):
        time.sleep(random.random())
        sign = gen_sign(userid, cid, i)
        params = {
            'op': 'getothersuserarticle',
            'curnum': i,
            'pagenum': '10',
            'cid': cid,
            'isoriginal': '0',
            'userid': userid,
            'sortarttype': '1',
            'sign': sign,
            '_': cur_time
        }
        req = requests.get(url, headers=headers, params=params)
        # print(json.loads(unquote(req.content)))

        for i in json.loads(unquote(req.content))['listitem']:
            time.sleep(random.random())
            yield i['arturl']


def save_html(url, headers, target_dir):  # 保存网页到本地
    session = HTMLSession()
    req = session.get(url, headers=headers)
    #取出文章所在的位置,为列表,first=ture提取第一个
    content = req.html.find('#articlecontent', first=True)

    #取出文章标题
    art_name = req.html.xpath('//*[@id="titiletext"]/text()', first=True)
    #替换标题中的非法字符,生成文件夹的时候出错
    rstr = r"[\/\\\:\*\?\"\<\>\|]"  # '/ \ : * ? " < > |'
    art_name = re.sub(rstr, "_", str(art_name))
    #目标存储路径
    target_dir = target_dir
    #根据文章标题创建文件夹
    art_dir_path = os.path.join(target_dir, art_name)
    #然后根据文件夹创建相应的html文件
    art_path = os.path.join(art_dir_path, art_name + '.html')

    os.makedirs(art_dir_path, exist_ok=True)
    img_list = req.html.xpath('//img[contains(@src,"image109")]/@src')
    html_str = content.html

    for img_url in img_list:  #获取文章中的所有图片的连接
        save_img(headers, img_url, art_dir_path)
        if re.search(img_url, content.html):  #匹配url并替换为本地图片地址
            new_img_url = img_url.split('/')[-1] + '.jpg'
            #str是常量需重新赋值才行
            html_str = html_str.replace(img_url, new_img_url)

    with open(art_path, 'w', encoding='utf-8') as f:
        f.write(html_str)  # 此处如果使用requests-html中css选择器将文章部分保存为html,图片无法加载


def save_img(headers, url, dir_path):  # 将html中图片保存到本地
    req = requests.get(url, headers=headers)
    img_path = os.path.join(dir_path, url.split('/')[-1]+ '.jpg')

    with open(img_path, 'wb') as f:
        f.write(req.content)


def gen_sign(userid, cid, curnum):  # 利用excejs执行本地js文件
    x = {
        "op": "getothersuserarticle",
        "curnum": curnum,
        "pagenum": 10,
        "cid": cid,
        "isoriginal": 0,
        "userid": userid,
        "sortarttype": 1
    }
    with open(r'D:\WorkSpace\Python\Test\360Doc\encrypt_method.js',
              encoding='utf-8') as f:
        js_data = f.read()
    c = execjs.compile(js_data)
    code = "wlht2019({})".format(x)  #wlht2019是文件中的某个函数,x是传入的参数
    sign = c.eval(code)
    return sign


def main(userid):
    cur_path = os.path.split(os.path.realpath(__file__))[0]
    cate_url = 'http://www.360doc.com/ajax/getmyCategory.ashx'
    art_url = 'https://api.360doc.com/Ajax/ArticleHandler.ashx'
    cur_time = str(time.time() * 1000).split('.')[0]
    useragent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36 Edg/89.0.774.77'
    headers1 = {
        'User-Agent': useragent,
        'Referer': 'http://www.360doc.com/userhome.aspx?userid=' + str(userid)
    }
    headers2 = {'User-Agent': useragent, 'Referer': 'http://www.360doc.com'}

    cate_list = get_art_cate(userid, cate_url, headers1, cur_time)
    for i in cate_list[1:]:
        cate_path = os.path.join(cur_path, i['selftitle'])
        os.makedirs(cate_path, exist_ok=True)
        art_list = get_art_list(userid, art_url, headers2, cur_time, i['id'], i['artnum'])
        for j in range(int(i['artnum'])+1):
            try:
                x = next(art_list)
                print('before '+x)
                save_html(x, headers2, cate_path)
            except:
                pass
            continue


if __name__ == "__main__":
    userid = 48488381
    main(userid)

上一篇:Ajax学习应用---art-template模板引擎使用


下一篇:《嵌入操作系统 – 玩转ART-Pi开发板》第7章 环境监测系统(二)