常见的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. LiveData

    ViewModel 添加依赖 implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' activity_main.xml < ...

  2. OGRE 渲染引擎 Windows 平台构建及编译

    0 OGRE 机器人领域常见的Rviz和Gazebo可视化的渲染后端. 1 无数的坑 这东西真的可恶,官方教程文档以及项目构建的方式是真的繁琐,在Windows上. CMake 的构建必须要使用 CM ...

  3. 增强 vw/rem 移动端适配,适配宽屏、桌面端、三折屏

    vw 和 rem 是两个神奇的 CSS 长度单位,认识它们之前,我一度认为招聘广告上的"像素级还原"是一种超能力,我想具备这种能力的人,一定专业过硬.有一双高分辨率的深邃大眼睛. ...

  4. C:二进制向十进制变换

    #include<stdio.h> int main() { char z[66]; scanf("%s",z); unsigned long long sum=0; ...

  5. [python]Gunicorn加持,轻松提升Flask超7倍性能

    前言 之前学习和实际生产环境的flask都是用app.run()的默认方式启动的,因为只是公司内部服务,请求量不高,一直也没出过什么性能问题.最近接管其它小组的服务时,发现他们的服务使用Gunicor ...

  6. MySQL,你只需看这一篇文章就够了!

    MySQL--DAY02 distinct 去重 把查询结果去除重复记录[distinct] 注意:原表数据不会被修改,只是查询结果去重. 去重需要使用一个关键字:distinct mysql> ...

  7. Python运行报错:ImportError: cannot import name 'BinarySpaceToDiscreteSpaceEnv' from 'nes_py.wrappers'

    运行Python项目: https://pypi.org/project/gym-super-mario-bros/ 报错: ImportError: cannot import name 'Bina ...

  8. 4.使用二进制方式搭建K8S集群

    使用二进制方式搭建K8S集群 注意 [暂时没有使用二进制方式搭建K8S集群,因此本章节内容不完整... 欢迎小伙伴能补充~] 准备工作 在开始之前,部署Kubernetes集群机器需要满足以下几个条件 ...

  9. Air780E如何发送SMS?一文详解!

    ​ 今天一起来学习使用合宙低功耗4G模组Air780E发送SMS短消息: 一.SMS简介 SMS(短消息服务,ShortMessageService)功能主要用于在蜂窝网络中传输短消息. 在4G网络中 ...

  10. html face属性

    无意间发现邮件里面的字体非常像手写的,然后点击HTML源码发现,使用了这个face属性. 代码如下: <font face="comic sans ms">PS: 你看 ...