【问题标题】:Web Scrape IMDB using Beautiful Soup in Python在 Python 中使用 Beautiful Soup 抓取 IMDB
【发布时间】:2022-01-07 12:54:30
【问题描述】:

我正在开展一个项目,该项目需要我对 IMDB 进行网络抓取并构建一个 pd 数据框。

这是我目前正在处理的网址:https://www.imdb.com/list/ls031674317/

在我尝试将 HTML 页面上的 Director 放入我的 movie_director 列表之前,一切都很顺利。该网站看起来像这样,有些电影有导演,有些则没有:

html 代码如下所示:

我写了一个函数,使用 Beautiful Soup 从 html 中获取每个导演(text_muted 只是我屏幕截图上的部分代码):

def getDirector(text_muted):
    try:
        return text_muted.find("a").getText()
    except:
        return 'NA'
text_muted_stuff = movie.find_all("p", {"class": "text-muted text-small"})[1]
director = getDirector(text_muted_stuff) # Need to seperate director and actor
movie_director.append(director)

我设法将所有导演和明星都列在一个列表中,但我想更具体一点,只填写导演列表(如果任何电影没有导演,请附加 NA)。我不确定是否可以使用 Beautiful Soup 或任何硬代码来实现这一点。

谢谢

【问题讨论】:

  • 你可以试试directors=movie.select('a[href*="ttls_li_dr"]')
  • @diggusbickus 谢谢,我会调查 select()
  • 您能否添加一个列表视图的 url,请参见您的屏幕截图。这将有助于更容易地重现您的问题,因为 html 因部分而异。谢谢
  • @HedgeHog 你是对的,已添加,感谢您的建议

标签: python html web-scraping beautifulsoup


【解决方案1】:

我认为在 bs4 中分离导演和明星的最简单方法是使用它们之间的 href 差异,即使没有列出导演。

导演的href标签包含“dr_0”,这可能对所有标题都相同(应该检查这个),多导演的电影可能会遵循与明星相同的模式,并推进如“dr_1” , "dr_2", ...

所以要从 html 源中只获取导演和明星,您可以使用:

import re

# could do with a copy of the html source to test this, 
# please provide a test html string of the element if this doesn't work first time
def get_directors(text_muted) -> list:
  # find all 'a' tags in that small element, if the href contains "dr_%d" then include the name in the returned list. return empty list if no directors.
  names = text_muted.find_all("a")
  return [element.string for element in names if re.search("dr_[0-9]", element.get("href", ""))]


def get_stars(text_muted) -> list:
  # find all 'a' tags in that small element, if the href contains "st_%d" then include the name in the returned list. return empty list if no stars.
  names = text_muted.find_all("a")
  return [element.string for element in names if re.search("st_[0-9]", element.get("href", ""))]

这应该足够安全,但如果 href 不像看起来那样可预测,那么您可以转储元素字符串并将其拆分为“导演:”和“星星:”模式。

顺便说一句,我倾向于在 html-scraping 时使用手动字符串搜索/正则表达式,因为在正确拆分标签方面付出更多努力的成本比 bs4 的速度提高大约 100-200 倍,所以值得考虑。

【讨论】:

  • 那绝对不值得考虑
  • 当您有 160'000 个 htmls 文件,使用 bs4 解析需要 300 毫秒或使用正则表达式解析需要 5 毫秒时,这是唯一现实的解决方案。
  • 如果您的正则表达式确实包含 160k 页,那么在某处肯定有更强大和更快的 api。但在现实世界中,没有正则表达式能保持那么久。如果它对你有用一次也没关系,但这只是给初学者的一个糟糕的建议
  • 我的意思不是一次全部,我的意思不是使用 bs4 在 1 个 html 文件中查找标签,而是我经常使用标准搜索和正则表达式来查找标签并减少所有解析时间。一旦找到标签,逻辑基本上是相同的,而不是方便地使用 element.string 我必须使用字符串搜索/正则表达式从元素中提取文本,这对于数十万个文件来说是一个显着的节省时间。在这种情况下,如果 href 属性不可用,那么这将是区分导演和明星的唯一方法
  • @G.S 非常感谢,我会试试的
【解决方案2】:

编辑: diggusbickus 评论更加严格,因此您不必检查两次 - 很好。将完整示例更改为它的方法,结果是一样的。

[x.text for x in d] if (d := item.select('a[href*="_dr_"]')) else None

属性选择器

[href*="_dr_"] - 表示具有名为href 的属性的元素,其值包含子字符串_dr_


第一种方法

您可以使用css selector 和同时检查href 的条件来选择导演。

[x.text for x in d if '_dr_' in x['href']] if (d := item.select('p:-soup-contains("Director:") a')) else None
  1. 选择包含 Director:

    <p> 中的所有 <a>
    item.select('p:-soup-contains("Director:") a')
    
  2. 检查元素是否不是None,否则将director的值设置为None

  3. 检查href中是否有_dr_,确定是导演

    if '_dr_' in x['href']
    

示例 (https://www.imdb.com/search/title/?genres=action)

import requests
from bs4 import BeautifulSoup
url = 'https://www.imdb.com/search/title/?genres=action'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'}
page = requests.get(url,headers=headers)
soup = BeautifulSoup(page.text, 'html.parser')

data = []
for item in soup.select('.lister-item'):
    data.append({
        'title':item.h3.a.text,
        'url':'https://www.imdb.com'+item.a['href'],
        # if you do not like lists in your data frame, simply join ','.join([x.text for x in d])
        'director': [x.text for x in d] if (d := item.select('a[href*="_dr_"]')) else None,
        'stars':[x.text for x in s] if (d := item.select('a[href*="_st_"]')) else None
    })


pd.DataFrame(data)

输出

title url director stars
Das Rad der Zeit https://www.imdb.com/title/tt7462410/?ref_=adv_li_i ['Rosamund Pike', 'Daniel Henney', 'Madeleine Madden', 'Zoë Robins']
Arcane https://www.imdb.com/title/tt11126994/?ref_=adv_li_i ['Kevin Alejandro', 'Jason Spisak', 'Hailee Steinfeld', 'Harry Lloyd']
Hawkeye https://www.imdb.com/title/tt10160804/?ref_=adv_li_i ['Jeremy Renner', 'Hailee Steinfeld', 'Florence Pugh', 'Tony Dalton']
Cowboy Bebop https://www.imdb.com/title/tt1267295/?ref_=adv_li_i ['John Cho', 'Mustafa Shakir', 'Daniella Pineda', 'Elena Satine']
Red Notice https://www.imdb.com/title/tt7991608/?ref_=adv_li_i ['Rawson Marshall Thurber'] ['Dwayne Johnson', 'Ryan Reynolds', 'Gal Gadot', 'Ritu Arya']
Spider-Man: No Way Home https://www.imdb.com/title/tt10872600/?ref_=adv_li_i ['Jon Watts'] ['Zendaya', 'Benedict Cumberbatch', 'Tom Holland', 'Marisa Tomei']
Dune https://www.imdb.com/title/tt1160419/?ref_=adv_li_i ['Denis Villeneuve'] ['Timothée Chalamet', 'Rebecca Ferguson', 'Zendaya', 'Oscar Isaac']
Shang-Chi and the Legend of the Ten Rings https://www.imdb.com/title/tt9376612/?ref_=adv_li_i ['Destin Daniel Cretton'] ['Simu Liu', 'Awkwafina', 'Tony Chiu-Wai Leung', 'Ben Kingsley']
James Bond 007: Keine Zeit zu sterben https://www.imdb.com/title/tt2382320/?ref_=adv_li_i ['Cary Joji Fukunaga'] ['Daniel Craig', 'Ana de Armas', 'Rami Malek', 'Léa Seydoux']
Eternals https://www.imdb.com/title/tt9032400/?ref_=adv_li_i ['Chloé Zhao'] ['Gemma Chan', 'Richard Madden', 'Angelina Jolie', 'Salma Hayek']
Venom: Let There Be Carnage https://www.imdb.com/title/tt7097896/?ref_=adv_li_i ['Andy Serkis'] ['Tom Hardy', 'Woody Harrelson', 'Michelle Williams', 'Naomie Harris']

【讨论】:

  • 天哪,太棒了,非常感谢
  • 很高兴提供帮助,也感谢 diggusbickus - 使用属性选择器提供树木的森林:D
猜你喜欢
  • 2022-08-22
  • 2023-03-20
  • 1970-01-01
  • 2020-09-04
  • 1970-01-01
  • 2013-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多