【问题标题】:Use beautifulsoup to scrape a table within a webpage?使用beautifulsoup 在网页中抓取表格?
【发布时间】:2022-10-17 03:56:21
【问题描述】:

我正在抓取一个发布紧急呼叫及其位置的县网站。我已经成功地抓取了基本元素,但是在抓取表格的行时遇到了麻烦。

(这是我正在使用代码的示例)

location = list.find('div', class_='listing-search-item__sub-title')

我不确定如何专门抓取表格的行。谁能解释如何深入 html 的子级别来查找这些记录?我不确定我是否需要深入研究 tr、table、tbody、td 等。可以使用一些指导来分配哪个部门或类来挖掘数据。

【问题讨论】:

  • 作为表格的旁注,您也可以使用pandas.from_html,它有时需要一些调整和过滤才能获得正确的表格 - 通常您会得到很多结果,但它通常可以节省很多手动操作 BS 的麻烦。
  • 请给我们网站的链接

标签: python web-scraping beautifulsoup


【解决方案1】:

对于提取特定的嵌套元素,我通常更喜欢使用.select,它使用css selectors(bs4 似乎不支持xpath,但你也可以查看these solutions using the lxml library),所以对于你的情况你可以使用类似的东西

soup.select_one('table[id="form1:tableEx1"]').select('tbody tr')

但结果可能是look a bit weird,因为列可能没有分开 - 要分开列/单元格,你可以get the of rows as tuples 代替

tableRows = [
    tuple([c.text.strip() for c in r.find_all(['th', 'td'])]) for r 
    in BeautifulSoup(tHtml).select_one(
        'table[id="form1:tableEx1"]'
    ).select('tbody tr')
]

(请注意,当 id 包含“:”时,您不能使用 .select(#id) 格式。)

作为提到的cmets之一,您可以使用pandas.read_html(htmlString)get a list of tables in the html;如果您想要一个特定的表,请使用 attrs 参数:

# import pandas
pandas.read_html(htmlString, attrs={'id': 'form1:tableEx1'})[0]

但你会得到所有的表 - 不仅仅是 tbody 中的内容;这将展平任何嵌套在内部的表格(请参阅results 使用来自this example 的表格)。

我最初使用select 展示的单语句方法根本不能用于嵌套表,因为输出会被打乱。相反,如果你想保留任何嵌套的内部表而不展平,并且如果你可能经常刮表,我有以下一组通常可以使用的函数:

  • 首先定义主表提取器依赖的另外两个函数:
def linkAncestor(t, a=None):
  aList = []
  while t.parent != a or a is None:
    t = t.parent 
    if t is None:
      if a is not None: aList = None
      break
    aList.append(t.name)
  return aList

def getStrings_table(xSoup): 
  # not perfect, but enough for me so far
  tableTags = ['table', 'tr', 'th', 'td']
  return "
".join([
      c.get_text(strip=True) for c in xSoup.children 
      if c.get_text(strip=True) and (c.name is None or (
          c.name not in tableTags and not c.find(tableTags)
      ))
  ])
  • 然后,您可以定义将表格提取为 python 字典的函数:
def tablesFromSoup(mSoup, mode='a', simpleOp=False):
  typeDict = {'t': 'table', 'r': 'row', 'c': 'cell'}
  finderDict = {'t': 'table', 'r': 'tr', 'c': ['th', 'td']}
  refDict = {
    'a': {'tables': 't', 'loose_rows': 'r', 'loose_cells': 'c'},
    't': {'inner_tables': 't', 'rows': 'r', 'loose_cells': 'c'},
    'r': {'inner_tables': 't', 'inner_rows': 'r', 'cells': 'c'}, 
    'c': {'inner_tables': 't', 'inner_rows': 'r', 'inner_cells': 'c'}
  }
  mode = mode if mode in refDict else 'a'

  # for when simpleOp = True
  nextModes = {'a': 't', 't': 'r', 'r': 'c', 'c': 'a'}
  mainCont = {
      'a': 'tables', 't': 'rows', 'r': 'cells', 'c': 'inner_tables'
  }

  innerContent = {} 
  for k in refDict[mode]: 
    if simpleOp and k != mainCont[mode]: 
      continue
    
    fdKey = refDict[mode][k] # also the mode for recursive call
    innerSoups = [(
        s, linkAncestor(s, mSoup)
    ) for s in mSoup.find_all(finderDict[fdKey])] 
    innerSoups = [s for s in innerSoups if not (
        'table' in s[1] or 'tr' in s[1] or 'td' in s[1] or 'th' in s[1]
    )]

    # recursive call
    kCont = [tablesFromSoup(s[0], fdKey, simpleOp) for s in innerSoups] 

    if simpleOp:
      if kCont == [] and mode == 'c':
        break
      return tuple(kCont) if mode == 'r' else kCont

    # if not empty, check if header then add to output
    if kCont: 
      if 'row' in k:
        for i in range(len(kCont)):
          if 'isHeader' in kCont[i]: continue
          kCont[i]['isHeader'] = 'thead' in innerSoups[i][1]
      if 'cell' in k:
        isH = [(c[0].name == 'th' or 'thead' in c[1]) for c in innerSoups]
        if sum(isH) > 0:
          if mode == 'r':
            innerContent['isHeader'] = True
          else: 
            innerContent[f'isHeader_{k}'] = isH
      
      innerContent[k] = kCont 
  
  if innerContent == {} and mode == 'c':
    innerContent = mSoup.text.strip()#.get_text(strip=True) 
  elif mode in typeDict:
    if innerContent == {}: 
      innerContent['innerText'] = mSoup.get_text(strip=True)
    else:
      innerStrings = getStrings_table(mSoup)
      if innerStrings:
        innerContent['stringContent'] = innerStrings
    innerContent['type'] = typeDict[mode] 
  
  return innerContent

和之前一样example,这个函数给this output;如果simpleOp 参数设置为True,则结果为simpler output,但随后不再区分标头并且还排除了一些其他外围数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2019-07-29
    • 2021-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多