常见的java调用python脚本方式

  • 通过jython提供的类库实现
  • 通过Runtime.getRuntime()开启进程来执行脚本文件

通过jython提供的类库实现

  通过jython实现的话,我们需要引入jar包(jython官网:https://www.jython.org/),具体我写了一个demo,假设你的python代码为test.py:

public static void main(String[] args) {
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.execfile("E:\\workspace\\pycharm_workspace\\weixincrawer\\test.py");
PyFunction function = (PyFunction)interpreter.get("my_test",PyFunction.class);
PyObject pyobject = function.__call__(new PyString("huzhiwei"),new PyString("25"));
System.out.println("anwser = " + pyobject.toString());
}

输出结果:

name: huzhiwei
age: 25
anwser = success

  到此是没有什么问题的,我们使用function.call方法传入参数调用python函数,使用pyobject.toString()方法拿到python中my_test函数的返回值,但是如果你把test.py稍微做下修改如下:

import requests

def my_test(name, age):
response = requests.get("http://www.baidu.com")
print("name: "+name)
print("age: "+age)
return "success"

  不修改java调用代码的情况下,你会得到下面异常信息:

  ImportError: No module named requests

  没错,这就是我要讨论的问题,因为jython不可能涵盖所有python第三方类库的东西,所以在我们得python文件中用到requests类库的时候,很显然会报找不到模块的错误,这个时候我们是可以通过Runtime.getRuntime()开启进程来执行python脚本文件的。

通过Runtime.getRuntime()开启进程来执行脚本文件

使用这种方式需要同时修改python文件以及java调用代码,在此我同样在上面test.py的基础上进行修改:

import requests
import sys def my_test(name, age):
response = requests.get("http://www.baidu.com")
print("url:"+response.url)
print("name: "+name)
print("age: "+age)
return "success" my_test(sys.argv[1], sys.argv[2])

和上面test.py代码最大的区别在于,我们此处开启进程的方式实际上是在隐形的调用dos界面进行操作,因此在python代码中我们需要通过sys.argv的方式来拿到java代码中传递过来的参数。

java调用代码部分:

public static void main(String[] args) {
String[] arguments = new String[] {"python", "E:\\workspace\\pycharm_workspace\\weixincrawer\\test.py", "huzhiwei", "25"};
try {
Process process = Runtime.getRuntime().exec(arguments);
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
int re = process.waitFor();
System.out.println(re);
} catch (Exception e) {
e.printStackTrace();
}
}

结果输出:

url:http://www.baidu.com/
name: huzhiwei
age: 25
0

在此需要注意的一点,java代码中的process.waitFor()返回值为0表示我们调用python脚本成功,返回值为1表示调用python脚本失败,这和我们通常意义上见到的0与1定义正好相反。

java通过Process.waitFor()调用python模块返回错误代码1的一种解决办法

  在本人的实际项目需求开发时,基本上也是模仿上面的思路进行的,python脚本成功在PyCharm Community中也执行也成功,获得了所请求的网页数据,但是在java环境中通过Process.waitFor()调用python模块返回错误代码1。

  经过多次尝试和分析,发现

  问题原因:我的请求的URL设置为安全Http了,即https了,在PyCharm Community IDE可能存在HTTPS相关的处理模块所以执行成功。但是在java环境中通过Process.waitFor()调用python模块,实质上是通过dos/cmd命令行执行python.exe *.py命令的。而在这种情况下,由于环境中缺少HTTPS相关的处理模块所以执行不成功。

  解决办法:将请求的URL由安全Https修改为http即可。

下面附上我的示例代码:

被调用的python代码模块为:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/3/6 23:41
# @Author :
# @File : URLibTest.py import urllib.request
import sys def WebSpider(name, age):
response = urllib.request.urlopen('http://celestrak.com/satcat/tle.php?CATNR=25994')
print(response.read().decode('utf-8')) WebSpider(sys.argv[1], sys.argv[2])
# WebSpider("huzhiwei", "25")

主调的java代码模块为:

package cn.cetc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader; public class JavaInvokePython {
private static final String pyInterpreterPath = "C:\\Anaconda3\\python.exe";//注意:当命令行参数分开写的时候,exe后面不用添加一个空格。当命令行参数一起写的时候,exe后面一定要添加一个空格
private static final String pyFilePath = "URLibTest.py";//如果未指定.py文件的完全路径,则默认从工程当前目录下搜索
private static Process proc = null;//java进程类 /**
* 执行*.py文件
*/
public static void execPy() {
try {
String[] arguments = new String[] {pyInterpreterPath, pyFilePath, "huzhiwei", "25"};//实际上后两个参数传进入也没使用。当真正需要有参数传入时可以是利用这种方式传参
proc = Runtime.getRuntime().exec(arguments);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close(); int re = proc.waitFor();//返回0:成功。其余返回值均表示失败,如:返回错误代码1:操作不允许,表示调用python脚本失败
System.out.println(re);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
execPy();
} }

注意:上述java代码运行环境不需要额外安装Jython-x.x.x.jar,只需要一般的JDK环境和常规的需要的包导入。

参考链接:

1、java调用python脚本

2、在Java中调用Python代码

3、java调用python的几种用法(看这篇就够了)

4、Java调用Python程序方法总结

5、java调用python脚本无法正确获取返回值

6、在Java中调用Python

[转]java调用python脚本以及通过Process.waitFor()直接调用python模块返回错误代码1的一种解决办法的更多相关文章

  1. 解决:执行python脚本,提示错误:/usr/bin/python^M: 解释器错误: 没有那个文件或目录。

    执行python脚本,提示错误: /usr/bin/python^M: 解释器错误: 没有那个文件或目录. 产生错误原因: \r字符被显示为^M,这时候只需要删除这个字符就可以了. Linux环境下: ...

  2. Java链接Redis时出现 “ERR Client sent AUTH, but no password is set” 异常的原因及解决办法

    Java链接Redis时出现 "ERR Client sent AUTH, but no password is set" 异常的原因及解决办法 [错误提示] redis.clie ...

  3. java调用python脚本 并传参(根据配置文件获取python文件地址)

    方式一: Java代码 package com.mybatis.plus.utils; import cn.hutool.core.lang.Console; import java.io.Buffe ...

  4. 如何让python脚本支持命令行参数--getopt和click模块

    一.如何让python脚本支持命令行参数 1.使用click模块 如何使用这个模块,在我前面的博客已经写过了,可参考:https://www.cnblogs.com/Zzbj/p/11309130.h ...

  5. Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用

    #!/usr/bin/Python指定用什么解释器运行脚本以及解释器所在的位置 # -*- coding: utf-8 -*-用来指定文件编码为utf-8的 估计有不少人注意过一些python脚本开头 ...

  6. 关于Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用 – 指定文件编码类型

    #!/usr/bin/python指定用什么解释器运行脚本以及解释器所在的位置 # -*- coding: utf-8 -*-用来指定文件编码为utf-8的PEP 0263 -- Defining P ...

  7. 【转】关于Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用 – 指定文件编码类型

    原文网址:http://www.crifan.com/python_head_meaning_for_usr_bin_python_coding_utf-8/ #!/usr/bin/python 是用 ...

  8. 【转载】关于Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用 – 指定文件编码类型

    1.#!/usr/bin/python 是用来说明脚本语言是 python 的 是要用 /usr/bin下面的程序(工具)python,这个解释器,来解释 python 脚本,来运行 python 脚 ...

  9. 【亲测有效】Nodepad++/Sublime Text3中Python脚本运行出现语法错误:IndentationError: unindent does not match any outer indentation level解决策略

    我在开发游戏的时候,发现一个python脚本,本来都运行好好的,然后写了几行代码,而且也都确保每行都对齐了,但是运行的时候,却出现语法错误: IndentationError: unindent do ...

  10. (二)Python脚本开头两行的:#!/usr/bin/python和# -*- coding: utf-8 -*-的作用

    #!usr/bin/env python # -*- coding: utf-8 -*- def test(): print('hello, world') if __name__ == " ...

随机推荐

  1. KubeSphere 社区双周报 | KubeKey 支持 Web UI | 2023.10.13-10.26

    KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...

  2. CF1187E 题解

    Title translation 给定一棵 \(n\) 个点的树,初始全是白点. 要做 \(n\) 步操作,每一次选定一个与一个黑点相隔一条边的白点,将它染成黑点,然后获得该白点被染色前所在的白色联 ...

  3. mysql替换内容

    UPDATE storage SET guige = REPLACE(guige, '×', 'x')

  4. 微信小程序、uniapp、vue生命周期钩子函数

    生命周期是指从创建到销毁的过程 一.微信小程序 小程序里面有两种生命周期函数,第一个:通过App()来注册一个小程序 ,第二个:通过Page()来注册一个页面 应用生命周期函数   app( ) ap ...

  5. Expo上手

    RN中文网:https://reactnative.cn/docs/next/native-modules-android RN路由:https://reactnavigation.org/docs/ ...

  6. CSPS2024题目总结

    T1 决斗 签到题,考场上10min就做出来了. 我的方法是排序之后贪心打怪,就是用尽量小的怪去打现在场上最小的怪.用一个同侧双指针实现. \(O(nlogn)\). 另一种方法注意到了值域很小,可以 ...

  7. bootstrap-fileinput使用手册

    一.准备 1.插件下载地址:https://github.com/kartik-v/bootstrap-fileinput/ 下载后的压缩包解压文件夹内容如下: js:插件核心js代码,引用filei ...

  8. Docker容器使用问题:Failed to get D-Bus connection: Operation not permitted

    原因是dbus-daemon没能启动.其实systemctl并不是不可以使用.将你的CMD或者entrypoint设置为/usr/sbin/init即可.如: docker run --privile ...

  9. AOP实践:java.lang.instrument的使用

    背景 在 rcjp 项目中,需要调用 ASM API(用于字节码处理的开源库)对字节码进行处理,目标是实现对 Java 程序运行时各种对象的动态跟踪,并进一步分析各个对象之间的关系.在此之前,需要考虑 ...

  10. Spring源码学习 ------ IoC——AOP

    一直想抽空把Spring源码拿来读读,但真正去做这件事的时候发现不简单,Spring发展这么多年,它的规模已不是一个一般的开源框架所能比的,它的主要架构和流程不是非常清晰,很难抓到要害,但有一点可以肯 ...