如何在python中的字典中放入互斥方法?

How to put mutual exclusive method in dictionary in python?

我正在写一个抓取工具来提取不同站点的内容。用户输入一个 url,我的爬虫会解析 url 并找出它来自哪个来源(它只支持有限的网站)并根据网站的 dom 结构提取内容。

最简单的方法如下所示:

extract(soup, url):

  if url in siteA:
    content = soup.find_all('p')[0]
  elif url in siteB:
    content = soup.find_all('p')[3]
  elif url in siteC:
    content = soup.find_all('div', {'id':'ChapterBody'})[0]
  elif url in siteD:
    content = soup.find_all("td", {"class": "content"})[0]

然而,代码是多余的,因为越来越多的网站具有不同的规则,所以我想压缩代码并使其更容易。这是我尝试过的方式:

extract(soup, url):

  support = {
            'siteA': soup.find_all('p')[0]
            'siteB': soup.find_all('p')[3]
            'siteC': soup.find_all('div', {'id':'ChapterBody'})[0]
            'siteD': soup.find_all("td", {"class": "content"})[0]
            }

  if url in support:
    content = support[url]

这样我只需要跟踪字典而不是继续附加代码。 但是,当我 运行 代码时,每个键值对都在执行,并且显示索引错误,因为某些站点没有 'td' 或 'div' with id 'chapterbody',所以执行字典中的siteC/D会报错

我想知道有哪些可能的方法可以在保持代码紧凑的同时解决这个问题?

将字典转换为函数字典:

support = {
          'siteA': lambda: soup.find_all('p')[0],
          'siteB': lambda: soup.find_all('p')[3],
          'siteC': lambda: soup.find_all('div', {'id':'ChapterBody'})[0],
          'siteD': lambda: soup.find_all("td", {"class": "content"})[0]
          }

现在它们在您调用函数之前不会执行:

if url in support:
    content = support[url]()

或者,退出 soup.find_all() 调用并拥有元组字典(参数、索引)也是一种选择:

support = {
          'siteA': (('p'), 0),
          'siteB': (('p'), 3),
          'siteC': (('div', {'id':'ChapterBody'}), 0),
          'siteD': (("td", {"class": "content"}), 0)
          }

if url in support:
    param, index = support[url]
    content = soup.findall(*param)[index]

这里发生的事情是您编写的用于提取内容的代码,例如 soup.find_all('p')[0],在创建 support 时立即执行,这是有道理的。您在问python 将 soup.find_all('p')[0] 的 return 值分配给一个字典值,它正在这样做......对所有其他条目依此类推。

您打算做的是存储一个可以在准备就绪时执行的函数。为此,您可以使用 lambda 函数:

support = {
    'siteA': lambda s: s.find_all('p')[0],
    'siteB': lambda s: s.find_all('p')[3],
}

if url in support:
    content = support[url](soup)

但也许有一天您会拥有一个网站,其中提取内容的代码更加复杂,并且无法用 lambda 函数(仅支持一种表达式)来表达。所以在那种情况下你可以使用嵌套函数:

def site_complicated(s):
    # this is not complicated.. but it could be...
    return s.find_all('p')[0]

support = {
    'siteA': lambda s: s.find_all('p')[0],
    'siteB': lambda s: s.find_all('p')[3],
    'siteComplicated': site_complicated,
}