【问题标题】:How to scrape not well structured html tables with Beautifulsoup in Python?如何在 Python 中使用 Beautifulsoup 抓取结构不完善的 html 表?
【发布时间】:2019-08-19 14:39:18
【问题描述】:

这个网站 https://itportal.ogauthority.co.uk/information/well_data/lithostratigraphy_hierarchy/rptLithoStrat_1Page2.html 似乎有一个组织得不好的 html 表。表格单元格的唯一标识符是每个 tr 标签内的宽度。我想抓取所有 60 页的信息。我怎样才能找到一种方法来适当地刮掉每一行表格?我知道标题的大小是 10 列,但是因为对于某些 tr 标签,我有 5 个 td 标签,而对于其他一些标签,我或多或少有 td 标签,根据它的准确刮取数据并不容易列。

在这里,您可以看到部分代码仅提取与一行相关的数据,但不保留空单元格的空值。

soup = BeautifulSoup(page.content, 'lxml') # Parse the HTML as a string
table = soup.find_all('table')[0] # Grab the first table
new_table = pd.DataFrame(columns=range(0,10), index = [0]) # I know the size
row_marker = 0
for row in table.find_all('tr'):
     column_marker = 0
     columns = row.find_all('td')
     for column in columns:
           new_table.iat[row_marker,column_marker] = column.get_text()
           column_marker += 1

这是我从这段代码中得到的输出(将所有值放在一行中,它们之间没有任何间隙):

     0     1     2                  3     4   5    6    7    8    9  

0  62.00    PACL  Palaeocene Claystones  SWAP  NaN  NaN  NaN  NaN  NaN

但真正的输出应该是这样的:

   0        1    2   3                        4   5    6    7    8    9  

0  62.00   NaN NaN  PACL  Palaeocene Claystones  NaN  NaN  NaN  NaN  SWAP

【问题讨论】:

  • 网站是什么?
  • itportal.ogauthority.co.uk/information/well_data/… 。我也在主要问题中添加了该网站。
  • 一种可能的解决方案是创建一个包含特定列的所有可能值的列表,然后检查这些值中的任何一个是否在给定行的任何列中。这样,如果某列没有匹配项,您可以确定哪些值是空值。
  • 另一个可行的解决方案是使用每个 td 的宽度来查找空值,因为每个 td 的宽度对于每个包含数据的单元格都是恒定的。
  • 我明白了。使用基于分类的方法怎么样?我不知道抓取内容的具体细节是什么,但是如果您对每列中的数据类型有所了解,您可以使用字典列表来获取每列的所有可能结果,并比较从列表中每个字典的每一行都将每个单元格分类在相应的列下。之后,您可以用“NaN”填充其余的行数据单元格。

标签: python html web-scraping html-table beautifulsoup


【解决方案1】:

@samy 非常感谢你用很酷的方法来抓取这个网站:

【讨论】:

    【解决方案2】:

    我使用了我在 cmets 中提到的方法(使用宽度)来确定数据中的空值。这是 Python 代码:

    import requests                                                                                                                                                                                                                  
    import bs4                                                                                                                                                                                                                       
    
    URL = 'https://itportal.ogauthority.co.uk/information/well_data/lithostratigraphy_hierarchy/rptLithoStrat_1Page2.html'                                                                                                           
    
    response = requests.get(URL)                                                                                                                                                                                                     
    soup = bs4.BeautifulSoup(response.text, 'lxml')                                                                                                                                                                                  
    
    tables = soup.find_all('table')                                                                                                                                                                                                  
    count = 0                                                                                                                                                                                                                        
    cells_count = 0                                                                                                                                                                                                                  
    
    for table in tables:                                                                                                                                                                                                             
            count +=1                                                                                                                                                                                                                
            if count >2:                                                                                                                                                                                                             
                    row = table.tr                                                                                                                                                                                                   
                    cells = row.find_all('td')                                                                                                                                                                                       
                    print ''                                                                                                                                                                                                         
                    x = 0                                                                                                                                                                                                            
                    width_diff = 0                                                                                                                                                                                                   
                    cell_text = []                                                                                                                                                                                                   
                    for cell in cells:                                                                                                                                                                                               
                            width = cell.get('width')                                                                                                                                                                                
                            if int(width) < 10:                                                                                                                                                                                      
                                    continue                                                                                                                                                                                         
    
                            if width_diff > 0:                                                                                                                                                                                       
                                    cell_text.append('NaN ')                                                                                                                                                                         
                                    if width_diff > 50:                                                                                                                                                                              
                                            x += 2                                                                                                                                                                                   
                                            cell_text.append('Nan ')                                                                                                                                                                 
                                    else:
                                            x += 1
                                    width_diff = 0
    
                            if x == 0 or x == 1 or x == 2 or x == 3 or x == 4 or x == 6:
                                    width_range = [35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
                            elif x == 5:
                                    width_range = [220,221,222,223,224,225,226,227,228,229,230]
                            elif x == 7:
                                    width_range = [136]
    
    
                            if cell.text:
                                    cell_text.append(cell.text.strip() + ' ')
                            else:
                                    cell_text.append('NaN ')
    
                            if int(width) not in width_range:
                                    width_diff = int(width) - width_range[-1]
                            x += 1
                            #print x,
                    length = len(cell_text)
                    for i in range(0, length):
                            print cell_text[i],
                    diff = 9 - length
                    if diff > 0:
                            for j in range(0, diff):
                                    print 'NaN ',
    

    如您所见,我注意到每列都使用了一定的宽度范围。通过将每个单元格与其假定的宽度进行比较,我们可以确定它需要多少空间。如果宽度差异太大,则意味着它占用了接下来两个单元格的空间。

    它可能需要一些改进,您需要针对所有 URL 测试脚本以确保数据绝对干净。

    这是运行此代码的示例输出:

    61.00  SED  TERT  WBDS  NaN  Woolwich Beds  GP  NaN  WLDB                                                                                                                                                                        
    62.00  NaN  NaN  PACL  NaN  Palaeocene Claystones  NaN  Nan  SWAP                                                                                                                                                                
    63.00  NaN  NaN  SMFC  NaN  Shallow Marine Facies  NaN  Nan  SONS                                                                                                                                                                
    64.00  NaN  NaN  DMFC  NaN  Deep Marine Facies  NaN  NaN  NaN                                                                                                                                                                    
    65.00  NaN  NaN  SLSY  NaN  Selsey Member  GN  NaN  WSXB                                                                                                                                                                         
    66.00  NaN  NaN  MFM  NaN  Marsh Farm Member  NaN  NaN  NaN                                                                                                                                                                      
    67.00  NaN  NaN  ERNM  NaN  Earnley Member  NaN  NaN  NaN                                                                                                                                                                        
    68.00  NaN  NaN  WITT  NaN  Wittering Member  NaN  NaN  NaN                                                                                                                                                                      
    69.00  NaN  NaN  WHI  NaN  Whitecliff Beds  GZ  NaN  NaN                                                                                                                                                                         
    70.00  NaN  NaN  Nan  WFSM  NaN  Whitecliff Sand Member  NaN  Nan  GN                                                                                                                                                            
    71.00  NaN  WESQ  NaN  Nan  Westray Group Equivalent  NL  GW  WESH                                                                                                                                                               
    72.00  NaN  WESR  NaN  Nan  Westray Group  NM  GO  CNSB                                                                                                                                                                          
    73.00  NaN  NaN  THEF  NaN  Thet Formation  NaN  Nan  MOFI                                                                                                                                                                       
    74.00  NaN  NaN  SKAD  NaN  Skade Formation  NB  NaN  NONS                                                                                                                                                                       
    75.00  NaN  NORD  NaN  Nan  Nordland  NP  Q  CNSB                                                                                                                                                                                
    75.50  NaN  NaN  SWCH  NaN  Swatchway Formation  Q  NaN  MOFI                                                                                                                                                                    
    75.60  NaN  NaN  CLPT  NaN  Coal Pit Formation  NaN  NaN  NaN                                                                                                                                                                    
    75.70  NaN  NaN  LNGB  NaN  Ling Bank Formation  NaN  NaN  NaN                                                                                                                                                                   
    76.00  NaN  NaN  SHKL  NaN  Shackleton Formation  GO  QP  ROCK                                                                                                                                                                   
    77.00  NaN  NaN  UGNS  NaN  Upper Tertiary sands  NaN  NM  NONS                                                                                                                                                                  
    78.00  NaN  NaN  CLSD  NaN  Claret Sand  NP  NaN  SVIG                                                                                                                                                                           
    79.00  NaN  NaN  BLUE  NaN  Blue Sand  NaN  NaN  NaN                                                                                                                                                                             
    80.00  NaN  NaN  ABGF  NaN  Aberdeen Ground Formation  QH  NaN  CNSB                                                                                                                                                             
    81.00  NaN  NaN  NUGU  NaN  Upper Glauconitic Unit  NB  NA  MOFI                                                                                                                                                                 
    82.00  NaN  NaN  POWD  NaN  Powder Sand  GN  NaN  SVIG                                                                                                                                                                           
    83.00  NaN  NaN  BASD  NaN  Basin Sand  NaN  Nan  CNSB                                                                                                                                                                           
    84.00  NaN  NaN  CRND  NaN  Crenulate Sand  NaN  NaN  NaN                                                                                                                                                                        
    85.00  NaN  NaN  NORS  NaN  Nordland Sand  QP  NaN  SONS                                                                                                                                                                         
    86.00  NaN  NaN  MIOS  NaN  Miocene Sand  NM  NaN  ESHB                                                                                                                                                                          
    87.00  NaN  NaN  MIOL  NaN  Miocene Limestone  NaN  Nan  CNSB                                                                                                                                                                    
    88.00  NaN  NaN  FLSF  NaN  Fladen Sand Formation  GP  GO  WYGG                                                                                                                                                                  
    

    注意:我不知道您示例的第一个单元格中的 0 是如何创建的,因此我将其排除在答案之外。我不知道它是否也应该被刮掉,因为我在任何地方都没有找到它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-19
      • 1970-01-01
      • 2017-11-13
      • 1970-01-01
      • 2020-06-03
      • 1970-01-01
      • 2014-12-07
      相关资源
      最近更新 更多