【问题标题】:Trouble getting right values against each item无法针对每个项目获取正确的值
【发布时间】:2018-05-07 20:37:10
【问题描述】:

我正在尝试解析项目names,它对应于下面的sn-p中的valuesdt 标签包含 namesdd 包含 values。很少有dt 标签没有对应的values。所以,所有的names 都没有values。如果后者没有任何值,我希望将 values 与任何 name 保持空白。

这些是我想从中抓取数据的元素:

content="""
<div class="movie_middle">
    <dl>
        <dt>Genres:</dt> 
        <dt>Resolution:</dt> 
        <dd>1920*1080</dd> 
        <dt>Size:</dt> 
        <dd>1.60G</dd> 
        <dt>Quality:</dt> 
        <dd>1080p</dd> 
        <dt>Frame Rate:</dt> 
        <dd>23.976 fps</dd> 
        <dt>Language:</dt>
    </dl>
</div>
"""

我试过如下:

soup = BeautifulSoup(content,"lxml")
title = [item.text for item in soup.select(".movie_middle dt")]
result = [item.text for item in soup.select(".movie_middle dd")]
vault = dict(zip(title,result))
print(vault)

它给了我混乱的结果(错误的配对):

{'Genres:': '1920*1080', 'Resolution:': '1.60G', 'Size:': '1080p', 'Quality:': '23.976 fps'}

我的预期结果:

{'Genres:': '', 'Resolution:': '1920*1080', 'Size:': '1.60G', 'Quality:': '1080p','Frame Rate:':'23.976 fps','Language:':''}

我们将不胜感激任何有关解决此问题的帮助。

【问题讨论】:

  • 您将两个列表压缩在一起,因此它将匹配title 中的第一个结果和result 中的第一个结果,直到最小列表的长度。

标签: python python-3.x web-scraping beautifulsoup


【解决方案1】:

您可以遍历dl 中的元素。如果当前元素为dt,下一个元素为dd,则将该值存储为下一个元素,否则将该值设置为空字符串。

dl = soup.select('.movie_middle dl')[0]
elems = dl.find_all()  # Returns the list of dt and dd
data = {}
for i, el in enumerate(elems):
    if el.name == 'dt':
        key = el.text.replace(':', '')

        # check if the next element is a `dd`
        if i < len(elems) - 1 and elems[i+1].name == 'dd':
            data[key] = elems[i+1].text
        else:
            data[key] = ''

【讨论】:

    【解决方案2】:

    可以使用BeautifulSoup解析dl结构,然后写一个函数来创建字典:

    from bs4 import BeautifulSoup as soup 
    import re
    def parse_result(d):
      while d:
        a, *_d = d
        if _d:
          if re.findall('\<dt', a) and re.findall('\<dd', _d[0]):
            yield [a[4:-5], _d[0][4:-5]]
            d = _d[1:]
          else:
            yield [a[4:-5], '']
            d = _d
        else:
          yield [a[4:-5], '']
          d = []
    
    print(dict(parse_result(list(filter(None, str(soup(content, 'html.parser').find('dl')).split('\n')))[1:-1])))
    

    输出:

    {'Genres:': '', 'Resolution:': '1920*1080', 'Size:': '1.60G', 'Quality:': '1080p', 'Frame Rate:': '23.976 fps', 'Language:': ''}
    

    对于一个稍长但更简洁的解决方案,您可以创建一个装饰器来去除输出的 HTML 标签,从而无需在主 parse_result 函数中进行额外的字符串切片:

    def strip_tags(f):
      def wrapper(data):
         return {a[4:-5]:b[4:-5] for a, b in f(data)}
      return wrapper
    
    @strip_tags
    def parse_result(d):
      while d:
        a, *_d = d
        if _d:
          if re.findall('\<dt', a) and re.findall('\<dd', _d[0]):
            yield [a, _d[0]]
            d = _d[1:]
          else:
            yield [a, '']
            d = _d
        else:
          yield [a, '']
          d = []
    
    print(parse_result(list(filter(None, str(soup(content, 'html.parser').find('dl')).split('\n')))[1:-1]))
    

    输出:

    {'Genres:': '', 'Resolution:': '1920*1080', 'Size:': '1.60G', 'Quality:': '1080p', 'Frame Rate:': '23.976 fps', 'Language:': ''}
    

    【讨论】:

      【解决方案3】:
      from collections import defaultdict 
      test = soup.text.split('\n')   
      d = defaultdict(list)
      for i in range(len(test)):
           if (':' in test[i]) and (':' not in test[i+1]):
               d[test[i]] = test[i+1]
           elif ':' in test[i]:
               d[test[i]] = ''
      
      
      d
      defaultdict(list,
                  {'Frame Rate:': '23.976 fps',
                   'Genres:': '',
                   'Language:': '',
                   'Quality:': '1080p',
                   'Resolution:': '1920*1080',
                   'Size:': '1.60G'})
      

      这里的逻辑是你知道每个键都有一个冒号。知道了这一点,您可以编写一个if else 语句来捕获唯一的组合,无论是key 后跟key 还是key 后跟value

      编辑:

      如果你想清理你的钥匙,下面替换每个中的:

      d1 = { x.replace(':', ''): d[x] for x in d.keys() }
      d1
      {'Frame Rate': '23.976 fps',
       'Genres': '',
       'Language': '',
       'Quality': '1080p',
       'Resolution': '1920*1080',
       'Size': '1.60G'}
      

      【讨论】:

        【解决方案4】:

        问题是不存在空元素。由于&lt;dt&gt;&lt;dd&gt; 之间没有层次结构,恐怕您必须自己制作字典。

        vault = {}
        category = ""
        for item in soup.find("dl").findChildren():
            if item.name == "dt":
                if category == "":
                    category = item.text
                else:
                    vault[category] = ""
                    category = ""
            elif item.name == "dd":
                vault[category] = item.text
                category = ""
        


        基本上,这段代码会遍历&lt;dl&gt; 的子元素并用值填充vault 字典。

        【讨论】:

          猜你喜欢
          • 2020-10-22
          • 2018-07-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-05
          • 1970-01-01
          • 1970-01-01
          • 2021-05-26
          相关资源
          最近更新 更多