整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          .NET 大牛之路 - 007 詳解 .NET 程序

          .NET 大牛之路 - 007 詳解 .NET 程序集

          文來自『.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 代碼、元數據和程序集清單。

          IL 代碼

          我們先來看看下面這樣一段簡單的 C# 代碼被編譯成 IL 代碼會是什么樣子。C# 代碼如下:

          class Calculator
          {
              public int Add(int num1,int num2)
              {
                  return num1 + num2;
              }
          }

          經過編譯后,在項目的 bin\Debug 目錄會生成一個與項目名稱同名的 dll 程序集文件。我們使用 ildasm.exe 工具打開這個文件,定位到 CalculatorAdd 方法,可以看到 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 代碼

          前面提到,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;

          1 預先寫好網頁的頭部文件:head.html(可以包含CSS和JS代碼)

          2 預告寫好網頁的尾部文件:tail.html(可以包含JS代碼)

          3 需要處理的文本復制到一個文本文件:original.txt,屆時放到網頁的內容部分;

          4 編寫處理文本的Python代碼

          5 代碼非常簡單,只需要注意幾個語法細節:

          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)。


          1.從字符串中讀取表

          在第一個示例中,我們將從字符串中讀取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

          2.從URL讀取表

          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]的一個例子:


          3.從文件讀取表

          Pandas read_html()接受一個文件。讓我們通過一個例子來看看這是如何工作的。

          file_path='html_string.txt'
          with open(file_path, 'r') as f:
           dfs=pd.read_html(f.read())
          
          dfs[0]

          注意:以下教程將從字符串中讀取數據,因為隨著時間的推移,網頁內容可能變更。


          4.使用parse_dates分析日期列

          日期列作為對象數據類型讀取。要正確讀取日期列,可以使用參數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

          5.使用converters顯式轉換類型

          默認情況下,數值列被轉換為數值類型,例如,我們看到的year和cost列。但并非所有的數字文本數據都必須是數字類型,例如,所有值都以零開頭的ID列。

          ID=0001

          此外,有時可能需要顯式地進行類型轉換以確保數據類型的完整性。對于這些要求,我們可以使用參數轉換器顯式地進行類型轉換:

          dfs=pd.read_html(html_string, converters={
           'ID': str,
           'year': int,
           'cost': float,
          })

          6.多索引、標題和索引列

          默認情況下,位于<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]

          7.用match匹配表

          參數匹配采用字符串或正則表達式。該值默認為.+(匹配任何非空字符串),并將返回所有表。

          我們通過一個例子來看看這是如何工作的。

          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')

          8.使用屬性篩選表

          參數attrs接受任何有效的HTML標記屬性的字典來篩選表。例如:

          dfs=pd.read_html(html_string, attrs={'id': 'report'})

          id是有效的HTML標記屬性。


          9.使用缺失值

          默認情況下,所有空字符串都被視為缺失值,并作為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。


          主站蜘蛛池模板: 亚洲熟妇AV一区二区三区宅男| 无码av免费毛片一区二区| 亚洲va乱码一区二区三区| 亚洲综合av一区二区三区| 色综合视频一区中文字幕| 亚洲av午夜精品一区二区三区| 国产亚洲一区二区在线观看| 在线观看精品视频一区二区三区 | 日韩三级一区二区三区| 丝袜人妻一区二区三区| 亚洲AV无码一区二区三区电影 | 日韩免费视频一区二区| 亚洲一区中文字幕在线电影网| 男人的天堂精品国产一区| 亚洲区精品久久一区二区三区| 亚洲国产精品一区二区第四页| 亚洲色一区二区三区四区| 无码少妇一区二区浪潮av| 无码国产精品一区二区免费式直播| 变态拳头交视频一区二区| 日本一区二区三区在线看| 亚洲香蕉久久一区二区三区四区| 久久se精品一区二区影院| 国产亚洲福利精品一区二区 | 久久久久久免费一区二区三区| 精品视频一区二区观看| 久久中文字幕无码一区二区| 一区二区视频在线播放| 国产日韩AV免费无码一区二区| 日本一区二区三区不卡视频中文字幕| 亚洲熟女www一区二区三区| 精品人妻系列无码一区二区三区| 亚洲熟妇av一区二区三区漫画| 大屁股熟女一区二区三区| 亚洲一区日韩高清中文字幕亚洲| 搜日本一区二区三区免费高清视频| 日韩美一区二区三区| 久热国产精品视频一区二区三区 | 无码人妻品一区二区三区精99 | 久久亚洲一区二区| 视频一区精品自拍|