有没有办法将最后一个 jupyter markdown 单元格的内容作为字符串读取?

is there a way to read the contents of the last jupyter markdown cell as a string?

我正在使用 jupyter 和 pandas read_sql,这工作正常但看起来很丑。

例如我有一个查询:

SELECT * 来自 table_a 作为 限制 10;

我可以这样在降价单元格中很好地显示它:

``` mysql
SELECT *
FROM table_a AS a
LIMIT 10; 
```

我可以这样在代码单元中执行它:

 pd.read_sql('SELECT * FROM table_a AS a LIMIT 10;', conn)

这涉及 copy/paste 并显示两次查询(如果我只想将我的笔记本导出为 pdf 报告则不太好)

有没有办法通过将降价文本读入字符串 python 变量或任何其他方式来避免重复?

@Micah Kornfield 在问题评论中引用的 cellmagic 答案可能适用于许多情况。然而,在问题中据说避免重复是可取的。假设 SQL 很大,我们不想多次看到相同的查询。

不幸的是,现在是 2021 年,没有简单的解决方案。在 jupyter notebook 中有两个世界,后端是内核,在我们的例子中运行 python,前端运行 javascript。只有 javascript 可以看到降价单元格。可以让后端和前端相互通信,这些方法通常有点hacky,但无论如何我们都会依赖其中的一些。

我编写了一个脚本,它以两种不同的方式完成我们的工作,这可能会产生相似的结果。我将这些方法称为文件读取方法和 javascript 方法。

首先,请将以下文件 markdown.py 保存在与 ipython 相同的文件夹中(我们正在使用一个单独的文件,因为您指定您的笔记本最终将转到报告并且它是不希望将此脚本与笔记本一起使用):

from IPython.display import Javascript
from urllib.parse import unquote
from json import loads as jsonloads

def markread(cellnumber,notebookname=None,callbackvar=None):
    try:
        if type(cellnumber) is int:# maybe check if (varname in globals()):
            if callbackvar is not None and type(callbackvar) is str:
                return Javascript("const mdtjs = Jupyter.notebook.get_cells().filter(c=>c.cell_type==\"markdown\")["+str(cellnumber)+"].get_text(); IPython.notebook.kernel.execute(\"mdtp = unquote('\"+encodeURI(mdtjs)+\"');mdtp=mdtp[mdtp.find('\\n',mdtp.find('```'))+1:min(mdtp.rfind('\\n'),mdtp.rfind('```'))].strip();"+callbackvar+"=mdtp;del mdtp\");")
            if notebookname is not None and type (notebookname) is str:
                if not notebookname.endswith('.ipynb'):
                    notebookname += '.ipynb'
                with open(notebookname) as f:
                    j = jsonloads(f.read())
                mdts = [''.join(c['source'][1:]).strip().strip('`').strip() for c in j['cells'] if c['cell_type']=='markdown']
                return mdts[cellnumber]
    except:
        return None
    return None

现在回到笔记本,要加载脚本,您必须导入它:

from markdown import markread, unquote

使用javascript方法需要反引号,否则可以跳过。

1。文件读取方式:

用法:

marktext = markread(2, notebookname='mynotebookname')

这里 marktext 将从 mynotebookname 中的第三个降价单元格中获取值(第三个因为我们生活在一个零索引的世界中,所以 2 表示第三个;如果你跳过 '.ipynb ' notebookname 中的扩展名,因为在这种情况下它将自动附加)。重要 - 此方法读取写在磁盘上的笔记本文件,而不是事物的热状态。如果自上次保存后您更改了任何内容,则可能会出错。

2。 Javascript方法:

用法:

markread(1, callbackvar='marktext')

在这里,我们将第二个降价单元格的值写入一个名为 marktext 的变量。 Javascript 方法比较棘手 - 它是异步的,因此我们必须发送要写入的变量的名称(必须是表示其名称的字符串,而不是变量本身)。重要的是要知道 markread 必须 是单元格中的最后一个命令,因为 javascript 调用的限制。

工作原理

在内部,文件读取方法只是读取 json 的笔记本文件,从 'cells' 中选取值并过滤掉 markdown 的值。

然而 javascript 方法更为复杂。它调用 JS,因为 JS 可以访问包括 markdown 在内的单元格,因此 JS 读取单元格值(从 Jupyter.notebook.get_cells),过滤 markdown 值,调用 python 并发回那些 markdown 单元格 - url同意。这些编码的单元格被解码并分配给 callbackvar。在这两种方法中,我做了一些关于修剪单元格值的开始和结束(``` 和空格)可能不正确的假设。

有改进代码的方法,例如让它自动检测文件读取方法的笔记本名称,但它涉及更多的 hack,再次依赖 javascript 获取笔记本名称或使在端口 8888 上调用 api,但必须处理会话密码。我相信我们的脚本已经涵盖了最重要的内容。如果一种方法不起作用,您可能还会有另一种方法。