发布自己的Python包 - ohHTMLToMarkdown

背景

之前一直说要自己写个Python库,本来想写个简单的,比如说自动排序Python文件头的import语句,后来发现已经有现成的了,名为isort。最终实现了一个略微复杂一点的HTML转Markdown的库,名为ohHTMLToMarkdown。

为了学习一下如何发布一个Python包,我又厚颜无耻的把它传到了PyPI上。

实现思路

目前支持的HTML标签有:h1~h6, p, a, img, del, b, strong, i, em, hr, br, ul, ol, table, blockquote, code, pre, span, title, time, iframe, section, div, html, body, head

处理主要靠的是BeautifulSouphtml.parser解析器和.descendants对象。前者可以把html文档解析成一个树状模型,访问.contents可以访问到当前层级的所有子树的根节点;后者可以返回一个生成器,它包含了当前子树的所有节点(包含本身)。

通过判断descendants的长度可以知道该子树有没有子节点,之后根据当前tag的类型,可以选择继续进行深度遍历还是将它转换为Markdown的语义。

一个问题

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<h1>a</h1>", 'html.parser')
>>> print (tuple(soup.descendants))
(<h1>a</h1>, 'a')
>>> print (soup)
<h1>a</h1>

这里可是看出descendants其实是包含其本身的,并且最底层的文字也会作为后代之一。这个问题在处理类似于<h1>h1</h1>的时候不会有问题,但是在处理<b><i>boldita</i></b>就会有问题,我用了一个叫__special()的方法解决了这个问题,但是看起来有点蠢。

测试

使用unittest库写了19个测试用例,包含一些基本的功能测试。同时还从我的博客、简书、知乎上抓取了一些文章进行转换,总体效果还不错。

但是同样也有问题,就是每个网站在使用HTML时可能习惯不一样,比如说我的博客在渲染Markdown时,如果需要带行号显示,那么这块代码会被渲染成没有theadtable,而不是pre;而在简书中我见到了类似<p><b><br></b></p>的代码。后者在处理的时候没问题,而前者会多出一大堆行号。

后来我就想这里其实可以定制一些子类,用来专门处理特定网站的特定标签格式,这些类继承Parser类。

TODO

  • 扩展子块用于解析特性网站的特定标签
  • Nested列表(当前版本可以识别这种列表,但是无法输出正确格式的markdown,不会计算缩进)
  • 其他未涉及的HTML标签
  • 命令行工具
  • 其他需要修改的问题

发布

如果想要把你的库发布到PyPI上,你至少需要一个PyPI账号。

这里还有一些其他步骤,我不确定这些是不是都是必须的,但是通过这些步骤,我上传成功了。有个详细的文档:

Python-OpenSource-Project-Developer-Guide’s documentation

这里想提一句的是,PyPI似乎更新和简化了其项目发布的步骤,很多网上能搜到的教程都是老版的教程,我自己没试过。我这里所述的是我自己结合官方文档和同行项目之后,得到的精简步骤。

文件结构

因为我这个库只有一个py文件,只要把它放在同名目录下即可,同时可以新建其他三个文件setup.pyREADME.mdLICENSE。其中后两者也不是必须的,但是一般都会有。如图:

>>> tree ohHtmlToMarkdown /F
D:\ONEDRIVE\DOCUMENTS\PYTHON和数据挖掘\CODE\OHHTMLTOMARKDOWN
    LICENSE
    ohHtmlToMarkdown.py
    README.md
    setup.py

这里提一句,虽然官方说模块名字最好用全小写,但其实带大写也不会有什么问题。

setup.py

一个setup的模板:

import setuptools

with open('README.md', 'r',encoding='utf-8') as fh:
    long_description = fh.read()

setuptools.setup(
    name="ohHtmlToMarkdown",
    version="1.0.0",
    description="A Python Library for converting HTML to Markdown.",
    long_description=long_description,
    long_description_content_type="text/markdown",
    author="Ethan Huang",
    author_email="wfgydbu@163.com",
    url='https://github.com/wfgydbu/ohHTML2Markdown',
    packages=setuptools.find_packages(),
    license='GNU GPL 3',
    classifiers=[
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: GNU General Public License (GPL)',
        'Programming Language :: Python :: 3.6',
        "Operating System :: OS Independent"
    ],
    install_requires=['beautifulsoup4==4.6.0'],
    py_modules=['ohHtmlToMarkdown'],
    include_package_data=True,
    zip_safe=False,
)
  • 关于classifiers的选择,可以参考https://pypi.org/pypi?%3Aaction=list_classifiers
  • 关于version的填写,格式为major.minor[.patch[.sub]].
  • 注意py_modules的使用,如果你的模块文件有点多,你可能会需要在根目录下在新建若干子目录,一般来说每个目录都会被认为是一个模块,通过modules.sub_module调用,同时也要编写合适的__init__.py文件。
    • __init__.py文件的作用是将文件夹变为一个Python模块,你可以把它当作模块的入口。

我这个setup.py的模板是经过我反复锤炼过的(参考官方文档和同行的项目),它可以最小化的减少代码结构的调整。

LICENSE

假装会有人使用这个库,我这里选择了的License是GNU General Public License (GPL)。关于LICENSE的选择可以参考choose licenses

README.md

这个就不多说了,注意如果用的是MD的话,需要在setup.py中配置,不然到时候显示的是MD的源码。

发布到PyPI

规范的发布过程在这里:Packaging Python Projects。但是它这里面的例子发布了一个没有实际代码的项目,导致少了一部分配置项,恰好这些配置项也是必要的。

PyPI会根据你在setup()中定义的配置项生成该项目在PyPI上的主页面,这是我发布的0.1版截图。

pypi-homepage.jpg

可以看到缺少项目描述,因为我的配置项的long_description写错了。改好之后,我发现我的描述是用中文写的。感觉把路走窄了。

指令

setup.py的同级目录下,执行:

# 安装twine
pip install --user --upgrade twine

# 打包
python setup.py sdist bdist_wheel

# 发布到测试服
twine upload --repository-url https://test.pypi.org/legacy/ dist/*

# 发布到正式服
twine upload dist/*

执行完安装指令后,目录结构是这样的,我是在windows下做的:

D:\ONEDRIVE\DOCUMENTS\PYTHON和数据挖掘\CODE\OHHTMLTOMARKDOWN
│  LICENSE
│  ohHtmlToMarkdown.py
│  README.md
│  setup.py
│
├─build
│  ├─bdist.win-amd64
│  └─lib
│          ohHtmlToMarkdown.py
│
├─dist
│      ohHtmlToMarkdown-0.5.0-py3-none-any.whl
│      ohHtmlToMarkdown-0.5.0.tar.gz
│
└─ohHtmlToMarkdown.egg-info
        dependency_links.txt
        not-zip-safe
        PKG-INFO
        requires.txt
        SOURCES.txt
        top_level.txt

重要的补充

强烈建议在发布正式版本之前在测试服务器上试验一下,实际上,当生成了whl文件之后,就可以直接使用pip install *.whl安装这个包到本地了, 方便测试。

安装成功后,相关的代码和egg文件会被拷贝到${PATH}/Lib/site-packages下(包括子目录),因此不管是模块名字还是子目录的名字,在世界范围内都应该是唯一的。

调用site-packages下的py模块可以直接使用import module,其他则是用.连接,比如:import module.sub_module

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,618评论 0 10
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,862评论 1 3
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,167评论 4 16
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,298评论 6 428
  • 每个人的性格中,都有某些无法让人接受的部分,再美好的人也一样。所以不要苛求别人,也不要埋怨自己。做好自己就好。
    一缕烟火0阅读 128评论 0 0