无法针对每个项目获得正确的值

时间:2018-05-07 20:37:11

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

我尝试从下面的代码段解析项names及其对应的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:':''}

任何有关解决问题的帮助都将受到高度赞赏。

4 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

您可以使用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函数中进行额外的字符串切片:< / p>

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:': ''}

答案 2 :(得分: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] = ''

答案 3 :(得分:0)

问题是没有空元素。由于<dt><dd>之间没有等级制度,我担心您必须自己制作字典。

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 = ""


基本上,此代码会迭代<dl>的子元素,并使用值填充vault字典。