Python简记

本文详细介绍了Python编程的基础概念,包括模块、包、变量、注释、代码块、序列、函数和类的定义。此外,还涵盖了常用操作,如判断循环、JSON操作、文件处理、Web框架Django和Flask的使用,以及多线程等内容。同时,文章还提到了Python与其他语言如Perl和Jython的区别,并提供了实用的函数和操作示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、概念

二、基础

1. 入门

2. module,package

3. 文档结构:

4. 变量

5. 注释符 

6. 代码块和语句分割

7. 序列

8. 函数定义

9. 类定义

10. 安装python包

1)easy_install(低旧版本Python)

2)pip

3)为pip配置国内镜像源

11. Python可执行文件

三、常用操作

1. 判断和循环操作

2. json操作

3. 给python文件传递参数

4. 文件操作

5. web框架django和flask

1)Django

2)Flask

6. dict操作

7. 函数

1)函数装饰器 @符号

2)Lambda表达式

8. 多线程

四、常用函数

五、琐碎细节(不重要):

六、末节

七、实例

1. 录制和回放数据

2. json diff脚本

3. 将多个网址URL或本地路径文件的Json内容进行嵌套合并

4. 提取Json数据的结构

5. 支持多种常见预处理的嵌套合并Json内容的脚本


一、概念

Python:易读
Perl字符串模式匹配,python的正则表达式引擎基于Perl。但Perl符号语法晦涩。
Jython是java版实现的python,优点:可以直接调用Java类库。
Ruby完全面向对象,python借鉴了函数化编程结构。
 

二、基础

1. 入门

python使用内置函数help(obj)来获得参考帮助

主提示符>>> 等待新输入
次提示符... 等待某个未完的后续输入

py文件经过编译后,生成一种扩展名为pyc的二级制文件,是一种byte code;py文件变成pyc文件后,加载的速度有所提高,而且pyc是一种跨平台的字节码,是由python的虚拟机来执行的;但pyc的内容,是跟python的版本相关的,不同版本编译后的pyc文件是不通用的。

2. module,package

Python中的module,说白了,就是Python文件,而python文件一般后缀为py,所以就是你的xxx.py而已。

Python中的package,可以简单的理解为,一组的module,一堆(相关的)module组合而成的。

3. 文档结构:

1.起始行 #! /usr/bin/env python
    编码行 #-*- coding: utf-8 -*-    (该编码行只能在前2行,该格式是兼容emac编辑器的注释方式的,中间也可以用coding=utf-8的形式,直接使用#coding=utf-8也可以被python识别,编码方式支持gbk、cp939等参数,不加该编码行,python无法解析中文字符会报错)
2.模块文档(文档字符串)
3.模块导入
4.全局变量定义
5.类定义(若有)
6.函数定义(若有)
7.主程序

无论本文件(模块)是被别的模块导入还是作为脚本直接执行,都会执行主程序的代码。故一般主程序的代码如下:
if __name__ == '__main__':
    main() #如果是直接执行,调用main函数;如果模块是被导入,__name__的值为模块名字

打印helloworld的文档结构示例文件:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""本处即为模块说明文档:
该文件是hello world的示例文件"""

#模块导入
import sys

#默认使用utf8编码
reload(sys)
sys.setdefaultencoding('utf8')

#当我们使用“import A.B”导入模块的时候, 在代码中使用B,仍然得使用“A.B”这种方式才能正确索引;而对于“from A import B”这样的语句,我们可以在代码中直接使用B就可以索引到B。
#但这种方法有可能使得当前程序的命名空间混乱,可以使用“from A import B as m_B”,这样在后面的程序就用m_B来代替B了,这样就不会出重名的问题啦
from <module> import <class/function>

#全局变量定义
global_var="hello"


#类定义
class PrintWho():
    """类内的文档字符串,说明类的作用"""
    who=""
    def __init__(self, who):
        """初始化函数"""
        self.who=who
    def printwho(self):
        """打印who变量"""
        print self.who
    def getwho(self):
        """返回who变量"""
        return self.who

#函数定义
def main():
    """函数内的文档字符串,说明函数功能"""
    # 全局变量需要在本地改变时,要先声明
    # global global_var
    # global_var = global_val + '11111'
    local_var="world"
    objwho=PrintWho(local_var)
    print global_var + " " + objwho.getwho()

#主程序
if __name__ == '__main__':
    main() #如果是直接执行,调用main函数;如果模块是被导入,__name__的值为模块名字

4. 变量

python是弱变量类型,不需要提前声明变量,也不需要声明变量的类型。

__xxx 不用‘from module import *’导入(该语句表示导入命名空间)

__xxx__ 系统定义名字
__xxx 类中的私有变量
使用None表示空/Null
布尔值使用True, False
 

5. 注释符 

使用#作为注释符
文档字符串:写在模块、类、函数开始处的一个字符串,通过__doc__访问,例如:’print function_name.__doc__‘ 可以打印出该函数的文档字符串,而且这个语言可以就在函数function_name里使用。并且利用help命令看到的帮助信息也是取自文档字符串。

6. 代码块和语句分割

可以使用分号;分割同行的语句
通过缩进对齐表达代码块:建议缩进4个空格,避免制表符,因为各个平台或编辑器中解释不一致

7. 序列

对于序列(字符串、列表、元组)可用切片操作符:[],[:],[::]

元组是一种不可变类型,和列表相互转换使用list()、tuple()函数。
字典{key:value, key:value}
 

8. 函数定义

def funname(parm, parm=defaultvalue):

    """文档字符串""""
    code
    return 1
 

9. 类定义

class Child(Parent1,Parent2):

    var=val
    def __init__(self):
        """初始化函数""""
    @staticmethod
    def astaticmethod():
        """静态函数""""
    def ageneralmethod(self, parm):
        """普通函数""""

10. 安装python包

easy_insall的作用和perl中的cpan,ruby中的gem类似,都提供了在线一键安装模块的傻瓜方便方式,而pip是easy_install的改进版,提供更好的提示信息,删除package等功能。

1)easy_install(低旧版本Python)

#安装一个包,增加版本参数时应该使用双引号括住
easy_install <package_name>
easy_install "<package_name>==<version>"
easy_install "<package_name>>=<version>"
#升级一个包
easy_install -U "<package_name>"


2)pip

# 安装一个包
pip install <package_name>
pip install <package_name>==<version>
pip install <package_name>>=<version>
# 升级一个包
pip install --upgrade <package_name>
# 删除一个包
pip uninstall <package_name> 

3)为pip配置国内镜像源

执行`mkdir -p ~/.pip`创建目录,然后执行`vi  ~/.pip/pip.conf`编辑pip.conf配置文件并填入以下内容:

[global]
# 阿里镜像源
index-url = http://mirrors.aliyun.com/pypi/simple/
# 清华镜像源(设置为了备份源)
extra-index-url = https://pypi.tuna.tsinghua.edu.cn/simple/

[install]
trusted-host=mirrors.aliyun.com

更多国内镜像源可以参考 国内开源软件镜像站点参考 自行配置,配置过多的备份源固然可以起到平衡负载的作用,但当包不可用时会依次查找所有源,时间会变长不少。

11. Python可执行文件

#!/usr/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: '<模块名>==<版本号>','console_scripts','<entry_point项>'
__requires__ = '<模块名>==<版本号>'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        # <entry_point项>:在模块同级的"<模块名>-<版本号>-py2.7.egg-info"文件夹的下面的"entry_points.txt"文件中有相应程序的入口点
        load_entry_point('<模块名>==<版本号>', 'console_scripts', '<entry_point项>')()
    )


entry_points.txt文件示例:

表明需要执行的entry_point:MyEntryPointName,对应的是MyModule模块下的myPyFile.py文件中的myFunction()函数。

[console_scripts]
MyEntryPointName = MyModule.myPyFile:myFunction

三、常用操作

1. 判断和循环操作

if a<1 and b<1:
    pass #pass表示不做任何操作
elif a<1 or (not b<1):
    pass
else:
    pass

while a<10
    a+=1

for i in range(1, 10, 1)
    pass

2. json操作

从json格式的内容的文件中读取到变量:

f = file("json.txt");
s = json.load(f)
f.close()


把字符串、数组等类型转换为json格式并打印或者输出到文件:

list={"a":"aa","b":"bb"}
print json.dumps(list)
f = open('json_out.txt', mode='w+')
json.dump(list, f)

3. 给python文件传递参数

import sys

print sys.argv[0] ##脚本名
print len(sys.argv)  ##参数个数
print sys.argv[1] ## 第一个参数
print sys.argv  ###参数数组

4. 文件操作

import os
filename="/home/dir/file"
if(os.path.isfile(filename)):
#或者os.path.exists(filename)
        print filename + "exist!!!"
else:
        print "File not exist!!!!"

5. web框架django和flask

1)Django

官方首页:https://www.djangoproject.com/

2)Flask

安装:

pip install Flask

使用实例,test.py文件:

#! env python
# -*- coding: utf-8 -*-

from flask import Flask, request, session

app = Flask(__name__)
 
 
@app.route('/')
def hello_world():
    return '你好,flask!'


@app.route('/post/hello.do', methods=['POST'])
def hello_post():
    return '{"post": true, "text": "hello"}'


@app.route('/user/<username>')
def show_user_profile(username):
    return 'User %s' % username


@app.route('/test.do', methods=['GET', 'POST'])
def test():
    # 访问路径,/test.do
    print request.path
    # 方法,GET或POST
    print request.method
    # get参数
    print request.args
    # post参数
    print request.form
    # json类型参数
    print request.json
    # file内容,"the_file"对应前端表单name属性
    if 'the_file' in request.files:
        f = request.files['the_file']
        print f.filename
    # cookies
    print request.cookies


    # 返回html模板
    # return render_template('hello.html', name=name)
    # 返回时设定header等
    # resp = make_response(render_template('hello.html'), name=name)
    # resp.set_cookie('username', 'the username')
    # resp.headers['X-Something'] = 'A value'
    # return resp

    # 使用session
    session['login'] = 'admin'
    print session['login']
    session.pop('login', None)

    return 'test'


if __name__ == '__main__':
    app.secret_key = 'session使用的密钥'
    app.debug = True
    app.run()

使用gunicorn来部署Flask服务:

安装:pip install gunicorn

部署:gunicorn --workers=4 --bind=0.0.0.0:8080 test:app

6. dict操作

#初始化一个dict类型,然后增加和删除元素,然后删除整个dict
my_dict = {}
my_dict["myKey1"] = "xxx"
del my_dict["myKey1"]
del my_dict

#判断dict是否存在某个key
if key in my_dict:
    xxx #存在的处理逻辑

#使用key进行遍历
for key in my_dict:
    print key, my_dict[key]

#使用key和value进行遍历
for k,v in my_dict:
    print k, v

#使用key来遍历,因为有删除操作,所以,需要先得到一个keys数组
for key in my_dict.keys():
    if xxx:
        del my_dict[key]

7. 函数

函数可以作为函数变量,函数入参,函数返回值来使用。

举例:

def add(a, b):
    return a + b
my_fun = add
my_fun(3, 4)

1)函数装饰器 @符号

函数装饰器 @符号 是一种函数调用函数的简单写法,例如:

@my_decorator
def add(a, b):
    return a + b;
# 等价于:
add=my_decorator(add)

# 故,可以在my_decorator(func)函数中对函数进行预处理和后处理。举例:
def my_decorator(fun):
    def inner_fun(a, b):
        # 预处理和后处理相关事情
        return -1 * fun(a, b);
    return inner_fun        

# 进阶1:多个装饰器修饰一个函数时,嵌套顺序为最下面的装饰器在最里面
# 进阶2:当装饰器带有多个参数的时候, 装饰器函数就需要多加一层嵌套:
def my_decorator2(d1, d2):
    def my_decorator(fun):
        def inner_fun(a, b):
            # 预处理和后处理相关事情
            return d1 * d2 * fun(a, b);
        return inner_fun
#对应调用形式为:
@my_decorator2(d1=1, d2=2)
def add(a, b):
    return a + b;

2)Lambda表达式

Python 使用 lambda 来创建匿名函数,语法如下:

lambda [arg1 [,arg2,.....argn]]:expression

其中冒号左侧为匿名函数的输入参数,冒号右侧为匿名函数的返回值。

8. 多线程

t1=threading.Thread(target=run,args=("arg1",))   创建一个线程实例

t1.setDaemon(True)     把当前子线程设置为守护线程(一定要在start前设置),主线程结束,这些守护线程会强制结束。
t1.start()   启动这个线程实例
t1.join()   不加join的话,主线程和子线程完全是并行的;加了join,主线程得等这个子线程执行完毕,才能继续往下执行。

如果想获取多线程的返回结果,需要使用自定义继承了Thread类的自定义类的方式,举例:

class MyThread(threading.Thread):
    def __init__(self, arg1, arg2):
        super(MyThread, self).__init__()
        self.arg1 = arg1
        self.arg2 = arg2
    
    def run(self):
        self.result = self.arg1 + self.arg2

    def get_result(self):
        try:
            return self.result
        except:
            return None

四、常用函数

help(obj) 帮助
exit(int)退出,输入为0表示正常退出,其他值表示非正常退出
dir(obj) 列出模块定义的标识符
raw_input(str) 获取输入
int(obj) 转换为整型
str(obj) 转换为字符串
open(filename, mode) 打开文件
len(obj) 获取长度
type(obj) 显示类型
range(start=0, stop, step=1) 获取范围的数值x序列,start<=x<stop,步长为step
id(obj) 显示id,近似于指针的值
randint(int, int) 获取2个参数之间的随机数
locals()返回所有局部变量
 

五、琐碎细节(不重要):

打印使用print关键字,支持类似c的%来替换变量
三引号(三个单引号或双引号连写)表示该字符串可以包含转义等特殊字符。
不支持前置或后置的单操作符:++ --
不支持case语句
不支持类型:char或byte,指针
赋值语句不是合法表达式,比如y=(x=x+1)不合法
python没有三元操作符(:?),但是可以类似如下形式来替代:min = x if x < y else y
3<4<5 合法的判断
支持增量赋值+=,多重赋值x=y=1
多元赋值,例如x,y=y,x
;同一行书写多条语句
位操作符:~ << >> & ^ |
常见操作符:+ - * / // % **,其中//为地板除(python中/一定是实际的除),**为幂运算
r/R原始字符串操作符,使用方法:置于引号前,如r'',则引号内的转义字符失效。
函数支持前向引用,假设A函数中调用B函数,那么B函数可以放在A函数之后实现。
对象赋值实际上是简单的对象引用(浅拷贝);深拷贝要使用deepcopy()函数。
局部变量可以替代全局变量,但是这样有危险:1.从局部变量所在块的开始和局部变量定义之前,全局变量就已经不可见,故此时试图访问全局变量会报错2.没有直接的指示符可以方便的访问同名的全局变量
python是解释性语言,不像c在编译时会自动优化一些常见的问题,需要自己注意优化程序。比如,不要把求长度函数放在循环里。
python解释器承担了内存管理的任务。垃圾收集器负责释放内存:寻找计数为0的对象和循环引用。
 

六、末节

\换行符,多行书写
支持函数内部内嵌函数
可以传递函数做参数
支持复数类型

支持异常模式:try-except-else-finally,raise

七、实例

1. 录制和回放数据

recorder.py文件:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import ConfigParser
import socket, select
import time


def record(listen,datafile,backendIp,backendPort):
    print listen,datafile,backendIp,backendPort
    #create socket to accept original request.
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listen = int(listen)
    #bind local port;
    serverSocket.bind(('0.0.0.0', listen))
    #start listen, max connection number: 20
    serverSocket.listen(20)
    #wait until a client connect, then return a New socket object and client's address. You can use the New socket object to communicate with client.
    #call accept(), recv(), send() will blocked, so one time can ONLY process one connection. 
    #usually, out of accept() there is 'while True:' to circularly process the connections, and after accept(), should create a new thread to process connection ( such as recv() & send() ), so can process multi-connection at one time. It's classic Blocked Model.
    serverConnection,address = serverSocket.accept()
    try:
        reqData = ''
        while True:
            try:
                serverConnection.settimeout(0.1)
                #recive the request, once max len: 8024
                req = serverConnection.recv(8024)
                reqData += req
                if len(req)==0 : break
            except:
                break

        backendPort = int(backendPort)
        #create socket to resend original request to real server.
        clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        clientSocket.connect((backendIp, backendPort))
        #resend the original request
        clientSocket.send(reqData)

        resData = ''
        while True:
            #receive the real response, once max len: 1024
            res = clientSocket.recv(1024)
            if not res: break
            resData += res

        #send the response back to client, and close connection.
        serverConnection.send(resData)
        time.sleep(0.01)
        serverConnection.close()

        #open file to wirte the real response, use as stub response. 'w' means create or open & clear a file, then write. so always keep the LAST response.
        datafileHandle=open(datafile,'wb')
        datafileHandle.write(resData);
        datafileHandle.close()
    except socket.timeout:
        print 'record error'
        return
 

def replay(listen,datafile):
    print listen,datafile
    datafileHandle=open(datafile,'rb')
    #read the recorded response.
    response = datafileHandle.read()
    datafileHandle.close()
    
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #when socket is closed, its port can be use immediately.
    serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listen = int(listen)
    serverSocket.bind(('0.0.0.0', listen))
    #max connection number: 200
    serverSocket.listen(200)
    #set to non-blocking
    serverSocket.setblocking(0)

    #Non-blocking Mode, using epoll method.
    epoll = select.epoll()
    #listen on data-arriving event.
    epoll.register(serverSocket.fileno(), select.EPOLLIN)
    
    try:
        connections = {}; responses = {}
        arrivedTimes = {};
        while True:
           #inquire happened events and return (fd, event-code). max wait time: 1s.
           events = epoll.poll(1)
           for fileno, event in events:
               #serverSocket's read event, create a new socket to process.
               if fileno == serverSocket.fileno():
                   try:
                       connection, address = serverSocket.accept()
                       #Non-blocking Mode, register to the same epoll object.
                       connection.setblocking(0)
                       epoll.register(connection.fileno(), select.EPOLLIN)
                       #index the connection, and set response to stub one.
                       connections[connection.fileno()] = connection
                       responses[connection.fileno()] = response
                   except:
                       print "epoll accept error"
               #it's read event, so it's client's request arriving.
               elif event & select.EPOLLIN:
                   try:
                       #receive data, max len: 18024
                       connections[fileno].recv(18024)
                       arrivedtime = int(time.time()*1000)
                       arrivedTimes[fileno] = arrivedtime
                       #change to listen to write-event.
                       epoll.modify(fileno, select.EPOLLOUT)
                   except:
                       print "epoll read error"
               #it's wirte event, so now can send client a response.
               elif event & select.EPOLLOUT:
                   try:
                       #time.time() return a float type time-stamp. so, here means: send time must after receive time 50ms and more.
                       #epoll is default level-trigged mode. so, 50ms is simulating the Network Delay.
                       currenttime = int(time.time()*1000)
                       if(currenttime - arrivedTimes[fileno] > 50):
                           #write response, and when all is sended, then unregister write-event and close write & read, wait for client close the connection.
                           byteswritten = connections[fileno].send(responses[fileno])
                           responses[fileno] = responses[fileno][byteswritten:]
                           if len(responses[fileno]) == 0:
                               epoll.modify(fileno, 0)
                               connections[fileno].shutdown(socket.SHUT_WR)
                   except:
                       print "epoll write error"
               #HUP event means client close the connection. so unregister wirte-event and close connection socket.
               elif event & select.EPOLLHUP:
                   try:
                       epoll.unregister(fileno)
                       connections[fileno].close()
                       del connections[fileno]
                   except:
                       print "epoll close error"
    finally:
       print 'error'
       epoll.unregister(serverSocket.fileno())
       epoll.close()
       serverSocket.close()

if __name__=='__main__':
    configParser = ConfigParser.ConfigParser()
    configParser.read('stub.conf');
    if(not configParser.has_option("stub", "mode")):
        print 'no mode option in config file.'
        exit
    mode = configParser.get("stub", "mode")
    if(not configParser.has_option("stub", "listen")):
        print 'no listen option in config file.'
        exit
    listen = configParser.get("stub", "listen")
    if(not configParser.has_option("stub", "datafile")):
        print 'no datafile option in config file.'
        exit
    datafile = configParser.get("stub", "datafile")
    
    if(mode == '1'):
        if( not configParser.has_option("backend", "ip")):
            print 'no backend ip option in config file.'
            exit
        backendIp = configParser.get("backend", "ip")
        if(not configParser.has_option("backend", "port")):
            print 'no backend port option in config file.'
            exit
        backendPort = configParser.get("backend", "port")
        if(listen and datafile and backendIp and backendPort):
            record(listen,datafile,backendIp,backendPort)
        else:
            print 'config option lost,please check listen,datafile,backend ip,backend port.'
            exit
    else:
        if(listen and datafile):
            replay(listen,datafile)
        else:
            print 'config option lost,please check listen,datafile.'
            exit


stub.conf文件:

[stub]
mode : 0 
listen : 3000
datafile : mydata 

[backend]
ip : 127.0.0.1
port : 3000

2. json diff脚本

#! /bin/env python
# -*- coding: utf-8 -*-

import json
import commands
import re
import traceback
import datetime


KEY_PREFIX_SEPARATOR = "."
VALUES_SEPARATOR = u"@@@"
DEFAULT_ENCODING = "UTF-8"
NOT_EXIST_FLAG = "__Not_Exist__"


def __merge_values(value1, value2):
    if isinstance(value1, (unicode, str)):
        value1 = "\"" + value1 + "\""
    if isinstance(value2, (unicode, str)):
        value2 = "\"" + value2 + "\""
    if value1 is None:
        value1 = u"Null"
    if value2 is None:
        value2 = u"Null"
    if not isinstance(value1, unicode):
        value1 = unicode(str(value1), DEFAULT_ENCODING)
    if not isinstance(value2, unicode):
        value2 = unicode(str(value2), DEFAULT_ENCODING)
    return value1 + VALUES_SEPARATOR + value2


def json_diff(json1, json2):
    """
    将2个json进行比较,找出差异,并存储为ignore文件
    :param json1: 第1遍运行时的request的json字符串
    :param json2: 第2遍运行时的request的json字符串
    :return: 有差异的键的绝对路径,以及该键在2个json中对应的值(这些信息是用于提供xts系统对使用者展示)
    """
    # todo: 入参校验
    # f1=open(file1_path)
    # f2=open(file2_path)
    # json1=f1.read()
    # json2=f2.read()
    # f1.close()
    # f2.close()
    # print type(json1), json1
    # print json2
    # print "\n+++++++++++++++\n"
    # print json1
    # print "\n+++++++++++++++\n"
    # print json2
    # print "\n+++++++++++++++\n"
    json_obj1 = json.loads(json1, encoding=DEFAULT_ENCODING)
    json_obj2 = json.loads(json2, encoding=DEFAULT_ENCODING)
    # print type(json_obj1), json_obj1
    # print type(json_obj2), json_obj2
    result = {}  # 差异结果dict
    result1 = {}
    result2 = {}
    __get_key_prefix_dict(None, result1, json_obj1)
    __get_key_prefix_dict(None, result2, json_obj2)
    # print "\n+++++++++++++++\n"
    # print result1
    # print "\n+++++++++++++++\n"
    # print result2
    # print "\n+++++++++++++++\n"
    # print type(result1["key1"].encode("utf-8"))
    # print result1["key1"].encode("utf-8") == "中文1"

    # 遍历得到的前缀key数组并进行比较,将不同记入结果dict
    for k in result1:
        if k in result2:
            # 均存在,比较值是否相同
            if not isinstance(result1[k], type(result2[k])) or result1[k] != result2[k]:
                result[k] = __merge_values(result1[k], result2[k])
        else:
            # 只在result1存在,记入差异结果dict
            result[k] = __merge_values(result1[k], NOT_EXIST_FLAG)

    for k in result2:
        if k not in result1:
            result[k] = __merge_values(NOT_EXIST_FLAG, result2[k])

    return result


def json_assert(expect_json, actual_json, absolute_ignore, relactive_ignore, value_ignore, is_disorder_array,
                is_full_compare, analyze_reference):
    """
    验证实际的json是否和期望的json相符
    :param expect_json: 期望的json字符串
    :param actual_json: 实际的json字符串
    :param absolute_ignore: 绝对路径表示的ignore集合;对性能几乎无影响
    :param relactive_ignore: 相对路径表示的ignore集合(正则形式);随着输入个数的增加,性能成线性下降
    :param value_ignore: 要忽略的value的集合(正则形式);随着输入个数的增加,性能成线性下降
    :param is_disorder_array: 是否需要无序比较数组;开启后性能下降,性能下降幅度取决于无序的结果个数和验证失败的个数,且随着数组的层级上升成n^2比例下降
    :param is_full_compare: 为false时只验证期望是否是实际的子集;为true时验证期望和实际为相等集;对性能几乎无影响
    :param analyze_reference: 是否对值为引用的情况进行解析并展开;开启后性能验证下降,建议仅对失败的结果使用该选项复查
    :return: 有差异的键的绝对路径,以及该键在2个json中对应的值
    """

    # todo: 入参校验

    json_exp = json.loads(expect_json, encoding=DEFAULT_ENCODING)
    json_act = json.loads(actual_json, encoding=DEFAULT_ENCODING)
    result = {}
    result_exp = {}
    result_act = {}
    __get_key_prefix_dict(None, result_exp, json_exp)
    __get_key_prefix_dict(None, result_act, json_act)

    # 处理需要忽略的绝对路径(可以不存在)
    for abs_key in absolute_ignore:
        if abs_key in result_exp:
            # print "ERROR"  # todo:
            # return None
            del result_exp[abs_key]
        if abs_key in result_act:
            del result_act[abs_key]

    # 处理需要忽略的相对路径(使用正则匹配,可以无匹配结果)
    for rel_key in relactive_ignore:
        # 因为有删除操作,所以,使用dict.keys()来遍历
        for exp_key in result_exp.keys():
            if re.search(rel_key, exp_key):
                del result_exp[exp_key]
        for act_key in result_act.keys():
            if re.search(rel_key, act_key):
                del result_act[act_key]

    # 处理需要忽略的值的集合(使用正则匹配,可以无匹配结果)
    for ign_value in value_ignore:
        # 因为有删除操作,所以,使用dict.keys()来遍历
        for exp_key in result_exp.keys():
            if re.search(ign_value, str(result_exp[exp_key])):
                del result_exp[exp_key]
        for act_key in result_act.keys():
            if re.search(ign_value, str(result_act[act_key])):
                del result_act[act_key]

    # 递归地处理引用形式:xxx.xxx.$ref=$.xxx.xxx.xx[x].xxx
    if analyze_reference:
        REF_PARTERN = '\.\$ref$'
        # 处理期望结果dict
        has_recursive_reference = True
        while has_recursive_reference:
            has_recursive_reference = False
            for exp_key in result_exp.keys():
                # 匹配出引用形式,然后再循环匹配其值的前缀,然后替换前缀后,加入expand_rst数组
                if re.search(REF_PARTERN, exp_key):
                    ref_value = result_exp[exp_key][2:].replace('[', '.[')
                    if not ref_value:
                        # 例如内容为..即表示循环引用,类似这样的情况不做引用解析
                        continue
                    ref_value_pattern = u'^' + ref_value.replace('.', '\.').replace('[', '\[').replace(']', '\]')
                    replace_sucessful = False
                    for exp_key_inner in result_exp.keys():
                        if re.search(ref_value_pattern, exp_key_inner):
                            if re.search(REF_PARTERN, exp_key_inner):
                                # 说明为递归引用,被引用的地方仍然引用了其他地方
                                has_recursive_reference = True
                            # 解析后引用放到result_exp中,但是需要下轮大循环才能使用
                            result_exp[exp_key_inner.replace(ref_value, exp_key[0:-5], 1)] = result_exp[exp_key_inner]
                            replace_sucessful = True
                    if replace_sucessful:
                        del result_exp[exp_key]
                    else:
                        print exp_key

        # 处理实际结果dict
        has_recursive_reference = True
        while has_recursive_reference:
            has_recursive_reference = False
            for act_key in result_act.keys():
                # 匹配出引用形式,然后再循环匹配其值的前缀,然后替换前缀后,加入expand_rst数组
                if re.search(REF_PARTERN, act_key):
                    ref_value = result_act[act_key][2:].replace('[', '.[')
                    if not ref_value:
                        # 例如内容为..即表示循环引用,类似这样的情况不做引用解析
                        continue
                    ref_value_pattern = u'^' + ref_value.replace('.', '\.').replace('[', '\[').replace(']', '\]')
                    replace_sucessful = False
                    for act_key_inner in result_act.keys():
                        if re.search(ref_value_pattern, act_key_inner):
                            if re.search(REF_PARTERN, act_key_inner):
                                # 说明为递归引用,被引用的地方仍然引用了其他地方
                                has_recursive_reference = True
                            # 解析后引用放到result_act中,但是需要下轮大循环才能使用
                            result_act[act_key_inner.replace(ref_value, act_key[0:-5], 1)] = result_act[act_key_inner]
                            replace_sucessful = True
                    if replace_sucessful:
                        del result_act[act_key]
                    else:
                        print act_key

    # 区分数组比较方式,2种方法进行比较
    if is_disorder_array:
        # 无序比较数组,验证期望数据在实际中是否存在
        for exp_key in result_exp:
            # 直接验证存在的值是否相等,提高数组有序时的比较效率
            if exp_key in result_act:
                if result_exp[exp_key] == result_act[exp_key]:
                    continue
            # 进行数组的无序比较
            if not __disorder_array_assert(None, exp_key, result_exp[exp_key], result_act):
                # 无序比较存在不同,记录结果
                if exp_key in result_act:
                    result[exp_key] = __merge_values(result_exp[exp_key], result_act[exp_key])
                else:
                    result[exp_key] = __merge_values(result_exp[exp_key], NOT_EXIST_FLAG)
    else:
        # 有序比较数组,验证期望数据在实际中是否存在
        for exp_key in result_exp:
            if exp_key not in result_act:
                result[exp_key] = __merge_values(result_exp[exp_key], NOT_EXIST_FLAG)
            elif result_exp[exp_key] != result_act[exp_key]:
                result[exp_key] = __merge_values(result_exp[exp_key], result_act[exp_key])

    # 上面的比较是判断了期望包含的在实际结果的情况,相等判断则需要再添加不再期望中却在实际结果中的情况
    if is_full_compare:
        # 验证是否有实际数据在期望中不存在
        # fixme: 此处不支持数组无序比较
        for act_key in result_act:
            if act_key not in result_exp:
                result[act_key] = __merge_values(NOT_EXIST_FLAG, result_act[act_key])

    return result


def __get_key_prefix_dict(key_prefix, result, json_obj):
    if not isinstance(result, dict):
        print "输入参数错误:存储结果用的变量result类型错误."  # todo:
        return None

    if isinstance(json_obj, dict):
        # json串的情况,循环调用该Json串的所有键值
        if not json_obj:
            # dict为空,类似:"key":{},出口
            result[key_prefix] = "{}"
            return
        next_key_prefix = "" if (key_prefix is None) else (key_prefix + KEY_PREFIX_SEPARATOR)
        for (k, v) in json_obj.items():
            __get_key_prefix_dict(next_key_prefix + k, result, v)

    elif isinstance(json_obj, list):
        # json数组情况,循环调用该json数组的所有元素
        if not json_obj:
            # list为空,类似:"key":[],出口
            result[key_prefix] = "[]"
            return
        next_key_prefix = "" if (key_prefix is None) else (key_prefix + KEY_PREFIX_SEPARATOR)
        for i in range(0, len(json_obj)):
            __get_key_prefix_dict(next_key_prefix + "[" + str(i) + "]", result, json_obj[i])

    else:
        # 基本元素,即递归函数出口,得到一个结果放入结果dict
        result[key_prefix] = json_obj


def __disorder_array_assert(key_prefix, exp_key, exp_value, result_act):
    array_begin = exp_key.find("[")
    if array_begin < 0:
        # 递归出口:不含有数组,直接进行比较即可
        full_key = ("" if key_prefix is None else key_prefix) + exp_key
        if full_key in result_act:
            if exp_value == result_act[full_key]:
                return True
            else:
                return False
        else:
            return False
    else:
        # 将最上层的数组循环进行处理,递归调用求解
        array_end = exp_key.find("]")
        if array_end < array_begin:
            print "ERROR"  # todo:
            return None
        array_prefix = ("" if key_prefix is None else key_prefix) + exp_key[0:array_begin + 1]
        array_postfix = exp_key[array_end:]
        i = 0
        while True:
            array_full = array_prefix + str(i) + array_postfix
            if array_full in result_act:
                # 期望中存在对应的数组原因,递归调用进行比较
                if __disorder_array_assert(array_prefix + str(i) + "]", array_postfix[1:], exp_value, result_act):
                    return True
            else:
                # 数组越界
                return False
            i += 1


if __name__ == '__main__':

    (status, output1) = commands.getstatusoutput('cat ./input1.txt')
    (status, output2) = commands.getstatusoutput('cat ./input2.txt')
    list_out1 = output1.split("\n")
    list_out2 = output2.split("\n")

    commands.getstatusoutput('echo "" > ./json_diff_result.txt; echo "" > ./json_diff_exception.txt; echo "" > ./json_diff_retry_data.txt')
    f = file("./json_diff_result.txt", "a+")
    f_retry1 = file("./json_diff_retry_input1.txt", "a+")
    f_retry2 = file("./json_diff_retry_input2.txt", "a+")
    f_e = file("./json_diff_exception.txt", "a+")
    f_log = file("./json_diff_log.txt", "a+")

    i = 0
    lines_count = len(list_out1)
    for str_line in list_out1:
        try:
            begin = datetime.datetime.now()
            # 0-null, 1-input1, 2-input2
            list_col = []
            list_col.append("null")
            list_col.append(str_line)
            list_col.append(list_out2[i])
            # merge input1 and input2
            str_line = "null###" + str_line + "###" + list_out2[i]
            i += 1
            now_line = str(i) + "/" + str(lines_count) + ": "
            if list_col[1] == list_col[2]:
                end = datetime.datetime.now()
                print now_line + "same (" + str(end - begin) + ")"
                f_log.writelines("\n=========================\n")
                f_log.writelines(list_col[1])
                f_log.writelines("\n=====\n")
                f_log.writelines(list_col[2])
                continue
            # rst = json_assert(list_col[1], list_col[2], ["priceInfo.specialMinPriceMap","updatedCombinedCodebase","hotelPrice.localTimeupdatedCombinedCodebase","hotelPrice.localTime"], ["salesInfos", "bookingTime"], [], True, True)
            rst = json_assert(list_col[1], list_col[2], ["priceInfo.specialMinPriceMap", "updatedCombinedCodebase",
                                                         "hotelPrice.localTimeupdatedCombinedCodebase",
                                                         "hotelPrice.localTime"], [], [], True, True, False)
            if rst:
                # 记入文件
                f.writelines("\n=========================\n")
                f.writelines(json.dumps(rst))
                f.writelines("\n=====\n")
                f.writelines(list_col[1])
                f.writelines("\n=====\n")
                f.writelines(list_col[2])
                f_retry1.writelines(list_col[1])
                f_retry1.writelines("\n")
                f_retry2.writelines(list_col[2])
                f_retry2.writelines("\n")
                end = datetime.datetime.now()
                print now_line + "---->Find Diff! (" + str(end - begin) + ")"
            else:
                end = datetime.datetime.now()
                print now_line + "same (" + str(end - begin) + ")"
                f_log.writelines("\n=========================\n")
                f_log.writelines(list_col[1])
                f_log.writelines("\n=====\n")
                f_log.writelines(list_col[2])
        except Exception, e:
            # 记入文件
            f_e.writelines("\n=========================\n")
            f_e.writelines(e)
            f_e.writelines("\n=====\n")
            f_e.writelines(traceback.format_exc())
            f_e.writelines("\n=====\n")
            f_e.writelines(list_col[1])
            f_e.writelines("\n=====\n")
            f_e.writelines(list_col[2])
            print now_line + "====>Exception!"

3. 将多个网址URL或本地路径文件的Json内容进行嵌套合并

将多个网址URL或本地路径文件的Json内容进行嵌套合并。当List的元素为dict类型时,特别处理:如果dict元素的字段“key”相同,则认为其相同,从而进行替换而不是添加。

调用 ./<scriptName>.py [input.txt] [output.txt] 会处理 input.txt里的所有网站URL或本地路径,并输出到output.txt中。

#! /usr/bin/env python3
# pip install deepmerge
from deepmerge import Merger
import datetime
import json
import sys
import re
import requests
from pathlib import Path

# 定义常量
INPUT_FILE_PATH = "input.txt"
OUTPUT_FILE_PATH = "output.txt"

def remove_comments_from_string(input_string):
    # 使用正则表达式替换所有以 // 或 # 等行注释符开头直到行末的内容
    # r'//[^\n]*' 匹配以 // 开头直到行末的内容
    # re.MULTILINE 使得 ^ 和 $ 分别匹配每一行的开始和结束
    input_string = re.sub(r'^[ ]*//[^\n]*', '', input_string, flags=re.MULTILINE)
    input_string = re.sub(r'^[ ]*#[^\n]*', '', input_string, flags=re.MULTILINE)
    input_string = re.sub(r'^[ ]*/\*.*?\*/', '', input_string, flags=re.DOTALL)
    return input_string

def is_json(content):
    """检查内容是否为有效的 JSON 字符串"""
    try:
        json.loads(content)
    except ValueError:
        return False
    return True

def get_local_file_content(file_path):
    """读取本地文件内容"""
    try:
        with open(file_path, 'r') as file:
            content = file.read()
        print(f"Read local file: {file_path}")
        return content
    except Exception as e:
        print(f"Error reading local file {file_path}: {e}")
        return None

def get_url_content(url, timeout=10):
    """通过 URL 获取内容,并设置超时时间"""
    try:
        response = requests.get(url, timeout=timeout)
        response.raise_for_status()  # 检查 HTTP 请求是否成功
        content = response.text
        print(f"Fetched URL: {url}")
        return content
    except requests.Timeout as e:
        print(f"Request timed out for URL {url}: {e}")
        return None
    except requests.RequestException as e:
        print(f"Error fetching URL {url}: {e}")
        return None

def process_input_file(input_file_path=INPUT_FILE_PATH):
    """处理输入文件"""
    results = []
    try:
        with open(input_file_path, 'r') as input_file:
            for line in input_file:
                trimmed_line = line.strip()
                print(f"Processing line: {trimmed_line}")

                if trimmed_line.startswith('/') or trimmed_line.startswith('.'):
                    content = get_local_file_content(trimmed_line)
                elif trimmed_line.startswith('http'):
                    content = get_url_content(trimmed_line)
                else:
                    print("Line does not start with '/' or 'http', skipping.")
                    continue

                if content is not None:
                    # 去除注释
                    content = remove_comments_from_string(content)

                    # 去除换行符,以防有些字段值内部使用导致解析非法
                    content = content.replace("\n", "").replace("\r", "")
                    #print(content)

                if content is not None and is_json(content):
                    parsed_dict = json.loads(content)
                    results.append(parsed_dict)
                    print("Parsed JSON to dict successfully.")
                else:
                    print("Content is not valid JSON, skipping.")

        return results
    except FileNotFoundError:
        print(f"The file {input_file_path} was not found.")
    except Exception as e:
        print(f"An error occurred while processing the file: {e}")


def custom_list_merge(merger, path, list1, list2):
    # 检查列表中的元素是否为字典类型
    if all(isinstance(item, dict) for item in list1 + list2):
        # 创建一个字典来存储 key 或 id 对应的字典
        key_id_dict = {}
        # 字段数组,用于查找唯一的标识符
        identifier_fields = ['key', 'id', 'name']

        for item in list1:
            # 查找第一个存在的标识符字段
            identifier = next((item.get(field) for field in identifier_fields if item.get(field)), None)
            if identifier is not None:
                key_id_dict[identifier] = item
            else:
                # 如果没有找到标识符字段,则直接添加
                key_id_dict[id(item)] = item

        # 合并 list2 中的字典
        for item in list2:
            # 查找第一个存在的标识符字段
            identifier = next((item.get(field) for field in identifier_fields if item.get(field)), None)
            if identifier is not None:
                if identifier in key_id_dict:
                    # 如果标识符已经存在,则更新字典
                    key_id_dict[identifier].update(item)
                else:
                    # 如果标识符不存在,则添加字典
                    key_id_dict[identifier] = item
            else:
                # 如果没有找到标识符字段,则直接添加
                key_id_dict[id(item)] = item

        # 将字典转换回列表
        merged_list = list(key_id_dict.values())
        return merged_list
    else:
        # 如果列表中的元素不是字典类型,则简单地合并列表
        return list1 + list2


# 创建一个自定义的合并策略
custom_merger = Merger(
    [
     # 列表合并策略
     (list, custom_list_merge),
     # 集合合并策略
     (set, "union"),
     # 元组合并策略
     (tuple, "concat"),
     # 字典合并策略
     (dict, "merge"),
    ],
    # 当遇到不可合并的类型时,使用覆盖策略
    ["override"],
    # 当遇到不可合并的类型时,使用覆盖策略
    ["override"]
)

# 示例字典
dict1 = {
    "name": "Alice",
    "age": 30,
    "details": {
        "height": 165,
        "weight": 60,
        "hobbies": [{"hours": 2}, {"hours": 1}],
        "hobbiesWithKey": [{"key": "reading", "hours": 2}, {"key": "painting", "hours": 1}],
        "numbers": [1, 2, 3]
    }
}

dict2 = {
    "age": 35,
    "details": {
        "height": 170,
        "skills": ["coding", "cooking"],
        "hobbies": [{"hours": 3}, {"hours": 1}],
        "hobbiesWithKey": [{"key": "reading", "hours": 3}, {"key": "swimming", "hours": 1}],
        "numbers": [4, 5, 6]
    }
}

# 使用自定义合并策略
#merged_dict = custom_merger.merge(dict1, dict2)
#print(merged_dict)

def merge_dicts(dicts_list):
    """合并列表中的所有字典"""
    merged_dict = {}
    for d in dicts_list:
        merged_dict = custom_merger.merge(merged_dict, d)
    return merged_dict

def write_json_to_file(data, file_path=OUTPUT_FILE_PATH):
    """将数据写入 JSON 文件"""
    try:
        with open(file_path, 'w') as output_file:
            json.dump(data, output_file, indent=4)
        print(f"Data written to JSON file: {file_path}")
    except Exception as e:
        print(f"Error writing data to JSON file {file_path}: {str(e)}")


# 主函数
if __name__ == "__main__":
    # 从命令行参数获取输入和输出文件路径
    input_file_path = INPUT_FILE_PATH
    # 使用当前时间生成默认输出文件名
    current_time = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    output_file_path = current_time + "-" + OUTPUT_FILE_PATH

    # 如果提供了命令行参数,则使用它们
    if len(sys.argv) > 1:
        input_file_path = sys.argv[1]
    if len(sys.argv) > 2:
        output_file_path = sys.argv[2]

    result_dicts = process_input_file(input_file_path)
    #print("All parsed dictionaries:", result_dicts)

    # 合并所有字典
    final_merged_dict = merge_dicts(result_dicts)
    #print("Merged dictionary:", final_merged_dict)

    # 写入 JSON 文件
    write_json_to_file(final_merged_dict, output_file_path)

4. 提取Json数据的结构

#! /usr/bin/env python3

import json

def extract_structure(data, n=1):
    """
    获取字典的结构,对于字典类型的值保留所有的键值对,
    对于数组类型的值保留数组类型,但只保留前n个元素。

    :param data: 输入的数据(可以是字典或列表)
    :param n: 保留的数组元素数量,默认为1
    :return: 处理后的数据结构
    """
    if isinstance(data, dict):
        # 如果是字典类型,则递归处理每个键值对
        return {key: extract_structure(value, n) for key, value in data.items()}
    elif isinstance(data, list):
        # 如果是列表类型,则保留前n个元素,并保持列表类型
        trimmed_list = data[:n]
        return [extract_structure(item, n) for item in trimmed_list]
    else:
        # 如果既不是字典也不是列表,则直接返回该值
        return data

def read_and_process_json_file(file_path, n=1):
    """
    从指定文件中读取 JSON 字符串内容,并使用 extract_structure 函数解析内容。

    :param file_path: 文件路径
    :param n: 保留的数组元素数量,默认为1
    :return: 解析后的数据结构
    """
    with open(file_path, 'r') as file:
        json_data = json.load(file)

    processed_data = extract_structure(json_data, n)

    return processed_data

# 示例使用
file_path = 'input.json'  # 请确保替换为实际文件路径

# n定义每个数组输出的示例元素数量
result = read_and_process_json_file(file_path, n=2)

# 将处理后的字典转换为美化格式的 JSON 字符串并输出
formatted_json = json.dumps(result, indent=4, ensure_ascii=False)
print(formatted_json)

5. 支持多种常见预处理的嵌套合并Json内容的脚本

#! /usr/bin/env python3
# pip install deepmerge
from deepmerge import Merger
import datetime
import json
import sys
import re
import requests
from pathlib import Path

# 定义常量
INPUT_FILE_PATH = "input.txt"
OUTPUT_FILE_PATH = "output.txt"

def remove_comments_from_string(input_string):
    # 使用正则表达式替换所有以 // 或 # 等行注释符开头直到行末的内容
    # r'//[^\n]*' 匹配以 // 开头直到行末的内容
    # re.MULTILINE 使得 ^ 和 $ 分别匹配每一行的开始和结束
    input_string = re.sub(r'^[ ]*//[^\n]*', '', input_string, flags=re.MULTILINE)
    input_string = re.sub(r'^[ ]*#[^\n]*', '', input_string, flags=re.MULTILINE)
    input_string = re.sub(r'^[ ]*/\*.*?\*/', '', input_string, flags=re.DOTALL)
    return input_string

def is_json(content):
    """检查内容是否为有效的 JSON 字符串"""
    try:
        json.loads(content)
    except ValueError:
        return False
    return True

def get_local_file_content(file_path):
    """读取本地文件内容"""
    try:
        with open(file_path, 'r') as file:
            content = file.read()
        print(f"Read local file: {file_path}")
        return content
    except Exception as e:
        print(f"Error reading local file {file_path}: {e}")
        return None

def get_url_content(url, timeout=10):
    """通过 URL 获取内容,并设置超时时间"""
    try:
        response = requests.get(url, timeout=timeout)
        response.raise_for_status()  # 检查 HTTP 请求是否成功

        # 获取 Content-Type 头中的 charset 参数
        content_type = response.headers.get('Content-Type', '')
        
        # 如果 Content-Type 头中不携带 charset 参数,默认使用 UTF-8 编码
        if 'charset=' not in content_type:
            content = response.content.decode('utf-8')
        else:
            # 如果 Content-Type 头中携带 charset 参数,使用 requests 自动处理
            content = response.text

        print(f"Fetched URL: {url}")
        return content
    except requests.Timeout as e:
        print(f"Request timed out for URL {url}: {e}")
        return None
    except requests.RequestException as e:
        print(f"Error fetching URL {url}: {e}")
        return None

def detect_encoding(s):
    # 尝试 UTF-8 编码
    try:
        content_bytes = s.encode('utf-8')
        return 'utf-8'
    except UnicodeEncodeError:
        pass
    
    # 尝试 GBK 编码
    try:
        content_bytes = s.encode('gbk')
        return 'gbk'
    except UnicodeEncodeError:
        pass
    
    # 如果以上都不行,返回 None
    return None

def process_input_file(input_file_path=INPUT_FILE_PATH):
    """处理输入文件"""
    results = {}
    try:
        with open(input_file_path, 'r') as input_file:
            for line in input_file:
                trimmed_line = line.strip()
                print(f"Processing line: {trimmed_line}")

                if trimmed_line.startswith('/') or trimmed_line.startswith('.'):
                    content = get_local_file_content(trimmed_line)
                elif trimmed_line.startswith('http'):
                    content = get_url_content(trimmed_line)
                else:
                    print("Line does not start with '/' or 'http', skipping.")
                    continue

                if content is not None:
                    # 去除注释
                    content = remove_comments_from_string(content)

                    # 去除换行符,以防有些字段值内部使用导致解析非法
                    content = content.replace("\n", "").replace("\r", "")
                    #print(content)

                if content is not None and is_json(content):
                    # 检测 content 的编码
                    encoding = detect_encoding(content)
                    # 根据检测到的编码解码 content (得到 Unicode 字符串)
                    if encoding == 'gbk':
                        content = content.encode('gbk').decode('gbk')
                    else:
                        content = content.encode('utf-8').decode('utf-8')
                    parsed_dict = json.loads(content)
                    results[trimmed_line] = parsed_dict
                    print("Parsed JSON to dict successfully.")
                else:
                    print("Content is not valid JSON, skipping.")

        return results
    except FileNotFoundError:
        print(f"The file {input_file_path} was not found.")
    except Exception as e:
        print(f"An error occurred while processing the file: {e}")


def custom_list_merge(merger, path, list1, list2):
    # 检查列表中的元素是否为字典类型
    if all(isinstance(item, dict) for item in list1 + list2):
        # 创建一个字典来存储 key 或 id 对应的字典
        key_id_dict = {}
        # 字段数组,用于查找唯一的标识符
        identifier_fields = ['key', 'id', 'name']

        for item in list1:
            # 查找第一个存在的标识符字段
            identifier = next((item.get(field) for field in identifier_fields if item.get(field)), None)
            if identifier is not None:
                key_id_dict[identifier] = item
            else:
                # 如果没有找到标识符字段,则直接添加
                key_id_dict[id(item)] = item

        # 合并 list2 中的字典
        for item in list2:
            # 查找第一个存在的标识符字段
            identifier = next((item.get(field) for field in identifier_fields if item.get(field)), None)
            if identifier is not None:
                if identifier in key_id_dict:
                    # 如果标识符已经存在,则更新字典
                    key_id_dict[identifier].update(item)
                else:
                    # 如果标识符不存在,则添加字典
                    key_id_dict[identifier] = item
            else:
                # 如果没有找到标识符字段,则直接添加
                key_id_dict[id(item)] = item

        # 将字典转换回列表
        merged_list = list(key_id_dict.values())
        return merged_list
    else:
        # 如果列表中的元素不是字典类型,则union方式合并不重复项
        unique_items = set(list1).union(set(list2))
        return list(unique_items)


# 创建一个自定义的合并策略
custom_merger = Merger(
    [
     # 列表合并策略
     (list, custom_list_merge),
     # 集合合并策略
     (set, "union"),
     # 元组合并策略
     (tuple, "concat"),
     # 字典合并策略
     (dict, "merge"),
    ],
    # 当遇到不可合并的类型时,使用覆盖策略
    ["override"],
    # 当遇到不可合并的类型时,使用覆盖策略
    ["override"]
)

# 示例字典
dict1 = {
    "name": "Alice",
    "age": 30,
    "details": {
        "height": 165,
        "weight": 60,
        "hobbies": [{"hours": 2}, {"hours": 1}],
        "hobbiesWithKey": [{"key": "reading", "hours": 2}, {"key": "painting", "hours": 1}],
        "numbers": [1, 2, 3]
    }
}

dict2 = {
    "age": 35,
    "details": {
        "height": 170,
        "skills": ["coding", "cooking"],
        "hobbies": [{"hours": 3}, {"hours": 1}],
        "hobbiesWithKey": [{"key": "reading", "hours": 3}, {"key": "swimming", "hours": 1}],
        "numbers": [4, 5, 6]
    }
}

# 使用自定义合并策略
#merged_dict = custom_merger.merge(dict1, dict2)
#print(merged_dict)

def merge_dicts(dicts_list):
    """合并列表中的所有字典"""
    merged_dict = {}
    for d in dicts_list:
        merged_dict = custom_merger.merge(merged_dict, d)
    return merged_dict

def write_json_to_file(data, file_path=OUTPUT_FILE_PATH):
    """将数据写入 JSON 文件"""
    try:
        with open(file_path, 'w', encoding='utf-8') as output_file:
            json.dump(data, output_file, indent=4, ensure_ascii=False)
        print(f"Data written to JSON file: {file_path}")
    except Exception as e:
        print(f"Error writing data to JSON file {file_path}: {str(e)}")


def flatten_video_content(d):
    """
    如果字典 d 包含 "video" 键,则将其内容移至顶层字典并删除 "video" 键。

    :param d: 输入字典
    """
    if "video" in d:
        # 将 "video" 下的内容移动到顶层字典
        for key, value in d["video"].items():
            d[key] = value

        # 删除 "video" 键
        del d["video"]

def rename_keys(d, pairs):
    """
    根据键值对字典 pairs 重命名字典 d 中的键。

    :param d: 主字典
    :param pairs: 键值对字典,用于重命名 d 中的键
    """
    for old_key, new_key in pairs.items():
        if old_key in d:
            # 保存旧键的值
            value = d.pop(old_key)
            # 将值赋给新键
            d[new_key] = value

def process_spider_value(url, spider_value):
    # 如果url为本地文件则不做额外处理
    if url.startswith((".", "/")):
        return spider_value

    # 检查 spider_value 是否需要拼接处理
    if not spider_value.startswith((".", "/")):
        return spider_value

    # 分离 URL 的基地址和路径
    base_url = url.split("/", 1)[0]  # 获取协议和域名部分

    # 根据 spider_value 的不同前缀进行处理
    if spider_value.startswith("."):
        # 保留除最后一个 '/' 以外的所有路径
        path = url[len(base_url):].rstrip("/").rsplit("/", 1)[0]
        # 将 '.' 替换为 '/'
        processed_spider_value = spider_value.replace(".", "/", 1)
    else:  # spider_value.startswith("/")
        # 保留 URL 的主机部分
        path = ""
        processed_spider_value = spider_value.lstrip("/")

    # 拼接新的 URL
    new_url = base_url + path + "/" + processed_spider_value.lstrip("/")

    return new_url

def process_spider(url, d):
    """
    处理输入字典 d,如果 d 有 "spider" 字段,则:
    如果 d 有 "sites" 字段且为数组,则将它的数组中所有 dict 类型的元素添加一个 "jar" 字段且其值设置为 d["spider"]。
    如果已经存在 "jar" 字段且不为空字符串,则跳过处理。

    :param d: 输入的字典
    """
    # 检查是否有 "spider" 字段
    if "spider" in d:
        spider_value = d["spider"]

        # 对spider地址进行预处理
        spider_value = process_spider_value(url, spider_value)

        # 检查是否有 "sites" 字段且为列表
        if "sites" in d and isinstance(d["sites"], list):
            sites = d["sites"]

            # 遍历 "sites" 字段中的每个元素
            for site in sites:
                # 检查元素是否为字典类型
                if isinstance(site, dict):
                    # 检查是否已有 "jar" 字段且不为空字符串
                    if "jar" not in site or site["jar"] == "":
                        site["jar"] = spider_value
                    else:
                        site["jar"] = process_spider_value(url, site["jar"])

def add_original_url(url, d):
    # 检查URL是否以点或斜线开头,如果不是则继续处理
    #if not url.startswith('.') and not url.startswith('/'):
        # 如果'originalUrl'不存在于字典d中,则创建一个新的空列表
        if 'originalUrl' not in d:
            d['originalUrl'] = []

        # 检查'originalUrl'是否已经是字符串类型
        if isinstance(d['originalUrl'], str):
            # 将原来的字符串转换为列表
            d['originalUrl'] = [d['originalUrl'], url]
        else:
            # 如果'originalUrl'已经是列表,则直接追加新的URL
            d['originalUrl'].append(url)

def preprocess_result(result):
    """
    对结果进行预处理函数。

    :param result: dict内容为url和对应的字典数组
    """

    rename_keys_dict = {
        "iptv": "lives",
        "channel": "lives",
        "analyze": "parses"
    }


    for key, value in result.items():
        flatten_video_content(value)
        add_original_url(key, value)
        if "originalUrl" in value and isinstance(value["originalUrl"], list) and \
        value["originalUrl"] and value["originalUrl"][0] != "":
            process_spider(value["originalUrl"][0], value)
        else:
            process_spider(key, value)
        rename_keys(value, rename_keys_dict)



# 主函数
if __name__ == "__main__":
    # 从命令行参数获取输入和输出文件路径
    input_file_path = INPUT_FILE_PATH
    # 使用当前时间生成默认输出文件名
    current_time = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
    output_file_path = current_time + "-" + OUTPUT_FILE_PATH

    # 如果提供了命令行参数,则使用它们
    if len(sys.argv) > 1:
        input_file_path = sys.argv[1]
    if len(sys.argv) > 2:
        output_file_path = sys.argv[2]

    result = process_input_file(input_file_path)
    result_dicts = list(result.values())
    #print("All parsed dictionaries:", result_dicts)

    # 预处理所有数据
    preprocess_result(result)

    # 合并所有字典
    final_merged_dict = merge_dicts(result_dicts)
    #print("Merged dictionary:", final_merged_dict)

    # 写入 JSON 文件
    write_json_to_file(final_merged_dict, output_file_path)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小白杂货铺

打赏是一种友谊,让我们更亲密。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值