文来自『.NET大牛之路』体系专栏的免费分享,追更完整专栏请私撩我……
上一篇我们介绍了 Roslyn 编译器,我们知道,我们编写的 C#/VB 代码经过 Roslyn 编译器编译后会生成程序集文件。按照之前讲的 .NET 执行模型的顺序,这一篇我具体讲讲程序集。
我们编写的 C# 代码经过编译会生成 .dll 或 .exe 文件,这些文件就是 .NET 的程序集(Assembly)。
尽管 .NET 的程序集文件与非托管的 Windows 二进制文件采用相同的文件扩展名(*.dll),但它们的内部完全不同。具体来说,.NET Core 程序集文件不包含平台(泛指操作系统和 CPU 架构的组合)特定的指令,而是平台无关的中间语言(IL)和类型元数据。
你可能在一些 .NET/.NET Core 的文档中看到过 IL 的另外两种缩写:MSIL(Microsoft Intermediate Language,微软中间语言) 和 CIL(Common Intermediate Language,通用中间语言)。IL、MSIL 和 CIL 都是一个概念,其中 MSIL 是早期的叫法,现在已经很少有人用了。
但 .NET Core 与 .NET Framework 不一样,.NET Core 始终只会生成 *.dll 格式的程序集文件,即使是像控制台应用这样的可执行项目也不再会生成 *.exe 格式的程序集文件。
那我们在 .NET Core 项目的 bin 目录中看到和项目同名的 *.exe 文件是怎么回事呢?这个文件并不是一个程序集文件,而是专门为 Windows 平台生成的一个可执行的快捷方式。在 Windows 平台双击这个文件等同于执行 dotnet <assembly name>.dll 命令。在我们安装的 .NET Core 目录中有个 dotnet.exe 命令文件(如 Windows 系统默认位置是C:\Program Files\dotnet\dotnet.exe),在编译时,该文件会被复制到构建目录,并重命名为与项目名称同名的 <assembly name>.exe 文件。
总的来说,每个程序集文件主要由 IL 代码、元数据(Metadata)、清单(Manifest) 和资源文件(如 jpg、html、txt 等)组成。其中,IL 代码和元数据会先被编译为一个或多个托管模块,然后托管模块和资源文件会被合并成程序集。
托管模块,或者叫托管资源或托管代码,顾名思义,这种资源是由 .NET Core 的 CLR 运行时来管理运行的,它包含 IL 代码和元数据。比如对象的回收是由 CLR 中垃圾回收器(GC)自动执行的,不需要手动管理。
程序集文件中占比最大的一般是 IL 代码。IL 代码和 Java 字节码相似,它不包含平台特定的指令,它只在必要的时候被 .NET Core 运行时中的 JIT 编译器编译成本机代码(机器码)。
程序集文件中的元数据详细地描述了程序集文件中每个类型的特征。比如有一个名为 Product 的类,类型元数据描述了 Product 的基类、实现的接口(如果有的话)和每个成员的完整描述等细节。元数据由语言编译器(Roslyn)自动生成。
除了托管模块,程序集文件还可以嵌入资源文件,如 jpg、gif、html 等格式的静态文件,这些文件是非托管资源。
当托管模块和资源文件合并成程序集时,会生成一份清单,它是专门用来描述程序集本身的元数据。清单包含程序集的当前版本信息、本地化信息(用于本地化字符串等),以及正确执行所需的所有外部引用程序集列表等。
在第 5 篇文章中我们讲了 .NET 的两种执行模型,其中,当基于本地运行时执行模型发布时,虽然你的应用程序可以发布为可直接执行的单一文件,但这个单一的文件其实是多个文件的包装。它包含了由 IL 代码编译成的本地代码和 Native AOT 本地运行时。你的代码仍然在一个托管的容器中运行,运行时它的资源的管理和它作为多个文件发布是一样的。
下面让我们更详细地了解一下 IL 代码、元数据和程序集清单。
我们先来看看下面这样一段简单的 C# 代码被编译成 IL 代码会是什么样子。C# 代码如下:
class Calculator
{
public int Add(int num1,int num2)
{
return num1 + num2;
}
}
经过编译后,在项目的 bin\Debug 目录会生成一个与项目名称同名的 dll 程序集文件。我们使用 ildasm.exe 工具打开这个文件,定位到 Calculator 的 Add 方法,可以看到 Add 方法的 IL 代码如下:
.method public hidebysig
instance int32 Add (
int32 num1,
int32 num2
) cil managed
{
// Code size 9 (0x9)
.maxstack 2
.locals init (
[0] int32
)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add
IL_0004: stloc.0
IL_0005: br.s IL_0007
IL_0007: ldloc.0
IL_0008: ret
}
以我的安装环境为例,你可以在这个位置找到 ildasm.exe 工具:C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe。为了使用方便,你可以把该工具配置到 Visual Studio 的外部工具中。
这就是 IL 代码的样子,如果使用 VB 或 F# 编写相同的 Add 方法,它生成的 IL 代码会是一样的。关于 IL 代码语法后面有机会再讲,这里我们暂且不关心。
由于程序集中的 IL 代码不是平台特定的指令,所以 IL 代码必须在使用前调用 JIT 编译器进行即时编译,将其编译成特定平台(特定的操作系统和 CUP 架构,如 Linux x64)的本地代码,才能在该平台运行起来。
.NET Core 运行时会在 JIT 编译过程中针对特定平台再次进行底层优化。比如将 IL 代码编译成特定于某平台的本地代码时,它会把平台无关的代码剔除。并且,它会以适合目标操作系统的方式将编译好的本地代码缓存在内存中,供以后使用,下次不需要重新编译 IL 代码。
除了 CIL 代码外,.NET Core 程序集还包含完整、全面、细致的元数据,它描述了程序集中定义的每个类型(如类、结构、枚举),以及每个类型的成员(如属性、方法、事件),这些信息生成都由编译器自动完成的。
我们继续使用 ildasm.exe 来看看 .NET Core 元数据具体长什么样。以前面的代码为例,选择该程序集,依次点击“视图->元信息->显示”,可以看到当前程序集的所有元数据信息。我们可以在元数据信息中找到 Calculator 类的 Add 方法,它的元数据是这样的:
TypeDef #2 (02000003)
-------------------------------------------------------
TypDefName: ConsoleApp.Calculator (02000003)
Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100000)
Extends : 0100000C [TypeRef] System.Object
Method #1 (06000003)
-------------------------------------------------------
MethodName: Add (06000003)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x00002090
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: I4
2 Arguments
Argument #1: I4
Argument #2: I4
2 Parameters
(1) ParamToken : (08000002) Name : num1 flags: [none] (00000000)
(2) ParamToken : (08000003) Name : num2 flags: [none] (00000000)
元数据会被 .NET Core 运行时以及各种开发工具所使用。例如,Visual Studio 等工具所提供的智能提示功能就是通过读取程序集的元数据而实现的。元数据也被各种对象浏览工具、调试工具和 C# 编译器本身所使用。元数据是众多 .NET Core 技术的支柱,比如反射、对象序列化等。
.NET Core 程序集还包含描述程序集本身的元数据,我们称之为清单。清单记录了当前程序集正常运行所需的所有外部程序集、程序集的版本号、版权信息等等。与类型元数据一样,生成程序集清单也是由编译器的工作。
同样地,还是以上面 Calculator 类所在项目为例,我们也来看看程序集清单长什么样子。在 ildasm.exe 工具打开的程序集的目录树中,双击 MAINFEST 即可查看程序集的清单内容:
.assembly extern System.Runtime
{
.publickeytoken=(B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 5:0:0:0
}
.assembly extern System.Console
{
.publickeytoken=(B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 5:0:0:0
}
.assembly ConsoleApp
{
...
.custom instance void ... TargetFrameworkAttribute ...
.custom instance void ... AssemblyCompanyAttribute ...
...
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module ConsoleApp.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
可以看到,程序集清单首先通过 .assembly extern 指令记录了它所引用的外部程序集。接着是当前程序集本身的信息,记录了程序集本身的各种特征,如版本号、模块名称等。
前面提到,IL 代码需要先通过 JIT 编译器编译成特定平台的本地代码,才能在该平台运行。你可能会问,.NET 为什么要将源代码编译成 IL 代码,而不直接编译成特定平台的本地代码呢?
这样做主要有两个好处:一是语言整合,一套运行时环境可以运行多种语言编写的程序,.NET 团队不用开发和维护多套运行时;二是平台无关,方便程序和库的移植,编译后的程序集可以发布到多个平台,而不用为不同的平台发布特定的程序文件。虽然 IL 代码带来了可移植性等的好处,但需要以牺牲一点点启动时的性能作为代价。
一般我们的 Web 应用程序最终只会部署在一种平台(如 Linux x64),为了更快的启动性能,在启动时,我们确实可以不需要中间语言编译这个环节,省去启动时的 JIT 编译的时间。.NET Core 为我们提供了两种方式把 IL 代码提前编译成特定平台的本地代码。
一种方式是使用 ReadyToRun 功能。.NET Core 运行时(CoreCLR)中的一个叫做 CrossGen 的工具,它可以预先将 IL 代码编译成本地代码。要使用这个功能,只需在程序发布的时候,选择特定平台,在发布选项中勾选 Enable ReadyToRun compilation 即可。不过 ReadyToRun 功能目前只适用于 Windows 系统。
另一种方式是使用 .NET 5 新增加的 AOT 编译功能。发布时选择 Self-Contained 模式,发布后生成单个文件。AOT 编译也是提前将 IL 代码编译成本地代码,不同的是,它在发布时生成的单个文件还包含一个精简版的本地运行时。这点在第 5 篇文章讲过,不再累述了。
这两种方式都有弊端,第一种目前只适用于 Windows 系统,第二种 Self-Contained 单个文件发布要比多文件发布大几十 M。不过对于第一次启动慢那么一点点(可能甚至不到一秒的时间),大部分的 Web 应用程序都是完全可以接受的。如果实在对启动时性能有严格的要求,也可以使用预热的方案。
本文介绍了程序集以及它的内部组成:IL 代码、元数据、资源文件和程序集清单。总的来说,程序集就是 .NET Core 在编译后生成的 *.dll 文件,它包含托管模块、资源文件和程序集清单,其中托管模块由 IL 代码和元数据组成。
需要强调的是,IL 代码不包含特定平台的指令,它只在需要的时候才会被 CoreCLR 运行时中的 JIT 编译器编译成特定于平台的本地代码。
通过本文,相信大家对 .NET Core 程序集和它的内部组成已经有了一个整体的认识。
于网络上的文本,有的换行分段不是很规范,如果想做较规范的处理,并写成自己定义的CSS和JS的网页,可以按下面思路处理:
1 预先写好网页的头部文件:head.html(可以包含CSS和JS代码)
2 预告写好网页的尾部文件:tail.html(可以包含JS代码)
3 需要处理的文本复制到一个文本文件:original.txt,到时放到网页的内容部分;
4 编写处理文本的Python代码
4.1 将head.html写入new.html;
4.2 将original.txt内容处理后写入new.html;
4.2 将tail.html内容处理后写入new.html;
2 预告写好网页的尾部文件:tail.html(可以包含JS代码)
5.1 文本读入方法的区别
5.1.1 read([size]):读取整个文件做为一个字符串或指定size大小的内容
从文件读取size数量或全部字符,返回一个string类型。
read()的利端:
方便、简单;
一次性独读出文件放在一个大字符串中,速度最快;
read()的弊端:
文件过大的时候,占用内存会过大,1GB的文件,需要占用1GB的内存。
5.1.2 readline():
readline()逐行(段)读取文本,返回结果是一个字符串,当通过循环全部读完后,最后会读到一个空字符串,用来当做循环处理结束的标记;
readline()的利端:
占用内存小,逐行读取;
readline()的弊端:
由于是逐行读取,速度比较慢;
5.1.3 readlines():
readlines()一次性读取文本的所有内容,返回结果是一个list;
上面使用with语句会自动关闭打开的文件;
这种方法读取的文本内容,每行(段)文本末尾都会带一个'\n'换行符 (可以使用L.rstrip('\n')去掉换行符)
readlines()的利端:
一次性读取文本内容,速度比较快;
readlines()的弊端:
随着文本的增大,占用内存会越来越多,同样的,1GB的文件,需要1GB的内存;
5.1.4 直接for循环文件对象
调用read()会一次性读取文件的全部内容,文件全部内容做为一个字符串返回。如果文件太大,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。
调用readlines()一次读取所有内容并按行返回一个list。
调用readline()可以每次读取一行内容;
因此,要根据需要决定怎么调用。如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;调用readlines()最方便。
5.2 python中'\r'、'\n'及'、'\r\n'
如果文本文件是用atom编辑器写的,发现换行符是'\r'。这样一来去逐行读取数据就失效了,因为用open函数去打开该文件readline()时默认使用的换行符是'\n'。
但是,当这个txt文件是用pycharm编辑器写的话,其换行符就是'\n'了。一切正常。
在打字机时代就有了这几个符号的设计,其实'\r'的本意是回到行首,'\n'的本意是换行。所以回车相当于做的是'\r\n'或者'\n\r'。到了计算机时代沿用了这个符号设计,后来估计换行并回行首需要一起处理,所以出现了'\r'、'\n'、'\r\n'都有可能表示换行并回行首。(Windows是'\r\n',Linux是'\n')
如果需要明文内容,请用'rU'来读取(强烈推荐),即U通用换行模式(Universal new line mode)。该模式会把所有的换行符(\r \n \r\n)替换为\n。只支持读入,但是也足够了。这是Python 提供给我们的最好的选择,没有之一。
open函数加上参数'rU'后,读取结果跟txt文件表现就一致了。
另外,文件在使用write()方法写入时是不提供\n,由用户根据需要自行选择添加,这样在分行时更加灵活。
5.3 字符的strip()方法,全删除掉\n
strip()方法用于删除字符串首尾的空格,也可以用来删除首尾的指定指定串,格式为strip([str])。strip()的功能可以分解为lstrip()和rstrip()。
5.4 open()方法打开文件时的编码问题
如果涉及编码的报错,需要进行编码处理,如:
f=open('init.txt','rU',encoding='UTF-8')
附 Python代码:
import sys
import os
import re
arg1='' # 脚本没有参数:不合并段落;否则用空行分段,不是空行的合并
if len(sys.argv) > 1 :
....arg1=sys.argv[1]
# 将init.txt的文本适当处理,写到temp.txt文件中
f=open('init.txt','rU',encoding='UTF-8')
fTemp=open('temp.txt','w',encoding="UTF-8")
s=f.read() # 整个文本文件作为一个字符串返回
s=s.replace(' ','') # 处理全角空格
s=re.sub(r'\(淫色淫色\S+\)', '', s)
#s=s.replace(' ' , '\n\n')
#处理文本中的空格,只要含有“英文+空格+英文”就不处理
pattern=re.compile(u"[a-zA-Z]+\s+[a-zA-Z]+")
entxt=re.findall(pattern,s)
if (not entxt):
'''
....s=s.replace('.' , '。')
....s=s.replace(',' , ',')
....s=s.replace('!' , '!')
....s=s.replace('?' , '?')
'''
....s=s.replace(' ' , '') # 处理半角空格(全中文可以使用)
if (arg1==''):
....for i in range(4):
........s=s.replace('\n\n','\n') # 将多余的空行处理掉
else:
....# 处理用空行分段的文本(不是空格的段落合并)
....s=s.replace('\n\n','a1b2c3z0')# 用'a1b2c3z0'作段落标记
....s=s.replace('\n','')
....s=s.replace('a1b2c3z0','\n')
fTemp.write(s)
f.close()
fTemp.close()
# 'w'覆盖写website.html,'a'为追加写
fNew=open('website.html','w',encoding="UTF-8")
# 将head.html文件写到new.txt文件中
with open('head.html','rU',encoding="UTF-8") as head:
....line=head.readline() # 返回单行(段落)string
....while line: # 全部读完后,会返回一个空字符串
........fNew.write(line)
........line=head.readline() # 第n次执行则返回第n行
....#fNew.write(head.read())
# 将temp.txt的每段加<p></p>\n\n,写到website.html文件中
with open('temp.txt','rU',encoding="UTF-8") as fTemp2:
....countLines=countChars=0
....for lines in fTemp2.readlines(): # 返回一个列表list,元素为行
........countLines +=1
........countChars +=len(lines)
........if countLines==1:
............s='<h4>' + lines.strip() + '</h4>\n\n'
............fNew.write(s)
........else:
............s=lines.replace('\n','')
............s=s.strip() # 会删除掉\n
............s='<p>' + s + '</p>\n\n'
............fNew.write(s)
....fNew.write("\n\n本页共")
....fNew.write(str(countLines))
....fNew.write("段,")
....fNew.write(str(countChars))
....fNew.write("个字符。")
# 将head.html文件写到new.txt文件中
ffooter=open('footer.html','r',encoding="UTF-8")
fNew.write(ffooter.read())
fNew.close()
ffooter.close()
with open('temp.txt','rU',encoding="UTF-8") as fTemp3: #重命名website.html
....nfn=fTemp3.readline()
nfn=nfn.strip()
nfn +='.html'
if os.path.exists(nfn):
....os.remove(nfn)
os.rename('website.html',nfn)
print(countLines,"lines and",countChars,"chars.")
-End-
eb爬取是从Web上收集和解析数据的过程。Python社区已经开发出一些非常强大的web爬取工具。其中,Pandas read_html()是从html表中爬取数据的一种快速方便的方法。
在本文中,你将学习Pandas read_html()来处理以下常见问题,并帮助你进行web爬取。
1.从字符串读取表
2.从URL读取表
3.从文件读取表
4.使用parse_dates分析日期列
5.使用converters显式转换类型
6.多索引、标题和索引列
7.用match匹配表
8.使用属性筛选表
9.使用缺失值
请检查Notebook的源代码(https://github.com/BindiChen/machine-learning/blob/master/data-analysis/024-pandas-read_html/pandas-read_html.ipynb)。
在第一个示例中,我们将从字符串中读取HTML表。
html_string="""
<table>
<thead>
<tr>
<th>date</th>
<th>name</th>
<th>year</th>
<th>cost</th>
<th>region</th>
</tr>
</thead>
<tbody>
<tr>
<td>2020-01-01</td>
<td>Jenny</td>
<td>1998</td>
<td>0.2</td>
<td>South</td>
</tr>
<tr>
<td>2020-01-02</td>
<td>Alice</td>
<td>1992</td>
<td>-1.34</td>
<td>East</td>
</tr>
<tr>
<td>2020-01-03</td>
<td>Tomas</td>
<td>1982</td>
<td>1.00023</td>
<td>South</td>
</tr>
</tbody>
</table>
"""
要从字符串中读取表,请执行以下操作:
dfs=pd.read_html(html_string)
现在,我们得到的结果不是Pandas数据帧而是Python列表。如果使用type()函数,可以看到:
>>> type(dfs)
list
如果要获取表,可以使用索引访问它:
dfs[0]
结果看起来很棒。让我们看看dfs[0].info()的数据类型。默认情况下,数值列被转换为数值类型,例如,year和cost列分别被转换为int64和float64。
>>> df[0].info()
RangeIndex: 3 entries, 0 to 2
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 3 non-null object
1 name 3 non-null object
2 year 3 non-null int64
3 cost 3 non-null float64
4 region 3 non-null object
dtypes: float64(1), int64(1), object(3)
memory usage: 248.0+ bytes
Pandas read_html()接受URL。让我们通过一个例子来看看这是如何工作的。
URL='https://en.wikipedia.org/wiki/London'
dfs=pd.read_html(URL)
与从字符串读取相同,它返回一个数据帧列表。如果我们运行len(dfs),我们可以从给定的URL得到31个表。
>>> print(f'Total tables: {len(dfs)}')
31
下面是dfs[6]的一个例子:
Pandas read_html()接受一个文件。让我们通过一个例子来看看这是如何工作的。
file_path='html_string.txt'
with open(file_path, 'r') as f:
dfs=pd.read_html(f.read())
dfs[0]
注意:以下教程将从字符串中读取数据,因为随着时间的推移,网页内容可能变更。
日期列作为对象数据类型读取。要正确读取日期列,可以使用参数parse_dates指定日期列的列表。
>>> dfs=pd.read_html(html_string, parse_dates=['date'])
>>> dfs[0].info()
RangeIndex: 3 entries, 0 to 2
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 3 non-null datetime64[ns]
1 name 3 non-null object
2 year 3 non-null int64
3 cost 3 non-null float64
4 region 3 non-null object
dtypes: datetime64[ns](1), float64(1), int64(1), object(2)
memory usage: 248.0+ bytes
默认情况下,数值列被转换为数值类型,例如,我们看到的year和cost列。但并非所有的数字文本数据都必须是数字类型,例如,所有值都以零开头的ID列。
ID=0001
此外,有时可能需要显式地进行类型转换以确保数据类型的完整性。对于这些要求,我们可以使用参数转换器显式地进行类型转换:
dfs=pd.read_html(html_string, converters={
'ID': str,
'year': int,
'cost': float,
})
默认情况下,位于<thead>中的<th>或<td>元素用于形成列索引,如果<thead>中包含多行,则创建一个多索引。
下面是一个在<thead>中包含多行的HTML表的示例。
html_string="""
<table>
<thead>
<tr>
<th colspan="5">Year 2020</th>
</tr>
<tr>
<th>date</th>
<th>name</th>
<th>year</th>
<th>cost</th>
<th>region</th>
</tr>
</thead>
<tbody>
<tr>
<td>2020-01-01</td>
<td>Jenny</td>
<td>1998</td>
<td>1.2</td>
<td>South</td>
</tr>
<tr>
<td>2020-01-02</td>
<td>Alice</td>
<td>1992</td>
<td>-1.34</td>
<td>East</td>
</tr>
</tbody>
</table>
"""
它创建多重索引,因为<thead>中有多行。
dfs=pd.read_html(html_string)
dfs[0]
指定标题行:
dfs=pd.read_html(html_string, header=1)
dfs[0]
指定索引列:
dfs=pd.read_html(html_string, header=1, index_col=0)
dfs[0]
参数匹配采用字符串或正则表达式。该值默认为.+(匹配任何非空字符串),并将返回所有表。
我们通过一个例子来看看这是如何工作的。
html_string="""
<table id="report">
<caption>2020 report</caption>
<thead>
<tr>
<th>date</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr>
<td>2020-01-01</td>
<td>Jenny</td>
</tr>
<tr>
<td>2020-01-02</td>
<td>Alice</td>
</tr>
</tbody>
</table>
<table>
<caption>Average income</caption>
<thead>
<tr>
<th>name</th>
<th>income</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>200</td>
</tr>
<tr>
<td>James</td>
<td>300</td>
</tr>
</tbody>
</table>
"""
要读取包含特定文本的表:
# 标题中的文本
dfs=pd.read_html(html_string, match='2020 report')
# 表格单元格中的文本
dfs=pd.read_html(html_string, match='James')
参数attrs接受任何有效的HTML标记属性的字典来筛选表。例如:
dfs=pd.read_html(html_string, attrs={'id': 'report'})
id是有效的HTML标记属性。
默认情况下,所有空字符串都被视为缺失值,并作为NaN读取。
下面是一个HTML表格的示例,其中的< td >单元格中有一些空字符串。
html_string="""
<table>
<tr>
<th>date</th>
<th>name</th>
<th>year</th>
<th>cost</th>
<th>region</th>
</tr>
<tr>
<td>2020-01-01</td>
<td>Jenny</td>
<td>1998</td>
<td>1.2</td>
<td>South</td>
</tr>
<tr>
<td>2020-01-02</td>
<td>Alice</td>
<td>1992</td>
<td></td>
<td>East</td>
</tr>
<tr>
<td>2020-01-03</td>
<td>Tomas</td>
<td>1982</td>
<td></td>
<td>South</td>
</tr>
</table>
"""
以默认设置读取。
dfs=pd.read_html(html_string)
dfs[0]
为了保留这些空字符串,我们可以将参数keep_default_na设置为False。
dfs=pd.read_html(html_string, keep_default_na=False)
有时,对于缺少的值,你可能有其他字符表示法。如果我们知道什么类型的字符用作表中的缺失值,我们可以使用na_values参数处理它们:
dfs=pd.read_html(html_string, na_values=['?', '&'])
当数据帧已经创建好后,我们可以使用pandas replace()函数来处理这些值:
df_clean=dfs[0].replace({ "?": np.nan, "&": np.nan })
Pandas read_html()函数是一种快速方便地从html表中获取数据的方法。
我希望本文能帮助你节省从HTML表中删除数据的时间。建议你查看read_html()API的文档,并了解可以做的其他事情。
谢谢你的阅读。请查看Notebook的源代码,如果你对机器学习的实际方面感兴趣,请继续关注:https://github.com/BindiChen/machine-learning/blob/master/data-analysis/024-pandas-read_html/pandas-read_html.ipynb。
*请认真填写需求信息,我们会在24小时内与您取得联系。