Python issue: TypeError: unhashable type: 'slice' during web scraping

Python issue: TypeError: unhashable type: 'slice' during web scraping

我正在尝试从网站上抓取一些信息。我能够成功地抓取我正在寻找的文本,但是当我尝试创建一个函数来将文本附加在一起时,我得到了一个不可散列类型的 TypeError。

你知道这里会发生什么吗?有人知道如何解决这个问题吗?

这里是有问题的代码:

records = []
for result in results:
    name = result.contents[0][0:-1]

这里是完整的代码,用于复制目的:

import requests
from bs4 import BeautifulSoup

r = requests.get('https://skinsalvationsf.com/2012/08/updated-comedogenic-ingredients-list/')
soup = BeautifulSoup(r.text, 'html.parser')
results = soup.find_all('td', attrs={'valign':'top'})

records = []
for result in results:
    name = result.contents[0][0:-1]

results 项样本:

<td valign="top" width="33%">Acetylated Lanolin <sup>5</sup></td>,
<td valign="top" width="33%">Coconut Butter<sup> 8</sup></td>,
...
<td valign="top" width="33%"><sup> </sup></td>

提前致谢!!

在您收集的一些结果中,contents 不包含文本,仅包含 Tag 个对象,因此当您尝试 select 切片时,您会得到 TypeError Tag 的属性字典。

您可以使用 try-except 块捕获此类错误,

for result in results:
    try:
        name = result.contents[0][0:-1]
    except TypeError:
        continue

或者您可以使用 .strings 来 select 只有 NavigableString 内容,

for result in results:
    name = list(result.strings)[0][0:-1]

但好像只有最后一项没有文字内容,所以你可以忽略它。

results = soup.find_all('td', attrs={'valign':'top'})[:-1]

for result in results:
    name = result.contents[0][:-1]

要了解您获得 TypeError: unhashable type: 'slice' 的原因,请阅读 t.m.adam's answer。简而言之,在最后一次迭代中 result 变量指向 bs4.element.Tag 对象而不是 bs4.element.NavigableString.

下面是使用 try-except 块的工作解决方案,因为列表中的最后 2 个 <td> 元素不包含 "stripped_strings" 并且会产生 ValueError: not enough values to unpack (expected 2, got 0)

代码:(Python 3.6+ 如果你想使用f-strings

from bs4 import BeautifulSoup
import requests

url = 'https://skinsalvationsf.com/2012/08/updated-comedogenic-ingredients-list/'
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'}
html = requests.get(url, headers=headers).text
soup = BeautifulSoup(r.text, 'html.parser')

tds = soup.find_all('td')
for td in tds:
    try:
        ingredient, rating = td.stripped_strings
    except ValueError:
        pass
    else:
        print(f'{ingredient} -> {rating}')

输出:

Acetylated Lanolin -> 5
Coconut Butter -> 8
...
Xylene -> 7
Octyl Palmitate -> 7

您也可以去掉整个 try-except-else 并省略最后 2 个 <td>:

tds = soup.find_all('td')[:-2]
for td in tds:
    ingredient, rating = td.stripped_strings
    ...

但是,网站的维护者可能决定添加或删除一些成分,导致代码遗漏一些成分。