以视频爬取实例讲解Python爬虫神器Beautiful Soup用法

发表于 5年以前  | 总阅读数:1117 次

1.安装BeautifulSoup4
easy_install安装方式,easy_install需要提前安装


    easy_install beautifulsoup4

pip安装方式,pip也需要提前安装.此外PyPi中还有一个名字是 BeautifulSoup 的包,那是 Beautiful Soup3 的发布版本.在这里不建议安装.


    pip install beautifulsoup4

Debain或ubuntu安装方式


    apt-get install Python-bs4

你也可以通过源码安装,下载BS4源码


    Python setup.py install

2.小试牛刀


    # coding=utf-8
    '''
    @通过BeautifulSoup下载百度贴吧图片
    '''
    import urllib
    from bs4 import BeautifulSoup
    url = 'http://tieba.baidu.com/p/3537654215'

    # 下载网页
    html = urllib.urlopen(url)
    content = html.read()
    html.close()

    # 使用BeautifulSoup匹配图片
    html_soup = BeautifulSoup(content)
    # 图片代码我们在[Python爬虫基础1--urllib]( http://blog.xiaolud.com/2015/01/22/spider-1st/ "Python爬虫基础1--urllib")里面已经分析过了
    # 相较通过正则表达式去匹配,BeautifulSoup提供了一个更简单灵活的方式
    all_img_links = html_soup.findAll('img', class_='BDE_Image')

    # 接下来就是老生常谈的下载图片
    img_counter = 1
    for img_link in all_img_links:
      img_name = '%s.jpg' % img_counter
      urllib.urlretrieve(img_link['src'], img_name)
      img_counter += 1

很简单,代码注释里面已经解释的很清楚了.BeautifulSoup提供了一个更简单灵活的方式,去分析网站源码,更快获取图片link.

3.爬取实例
3.1基本的抓取技术
在写一个爬虫脚本时,第一件事情就是手动观察要抓取的页面来确定数据如何定位。

首先,我们要看一看在 http://pyvideo.org/category/50/pycon-us-2014 上的 PyCon 大会视频列表。检查这个页面的 HTML 源代码我们发现视频列表的结果差不多是长这样的:


    <div id="video-summary-content">
      <div class="video-summary">  <!-- first video -->
        <div class="thumbnail-data">...</div>
        <div class="video-summary-data">
          <div>
            <strong><a href="#link to video page#">#title#</a></strong>
          </div>
        </div>
      </div>
      <div class="video-summary">  <!-- second video -->
        ...
      </div>
      ...
    </div>

那么第一个任务就是加载这个页面,然后抽取每个单独页面的链接,因为到 YouTube 视频的链接都在这些单独页面上。

使用requests来加载一个 web 页面是非常简单的:


    import requests
    response = requests.get('http://pyvideo.org/category/50/pycon-us-2014')

就是它!在这个函数返回后就能从response.text中获得这个页面的 HTML 。

下一个任务是抽取每一个单独视频页面的链接。通过 BeautifulSoup 使用 CSS 选择器语法就能完成它,如果你是客户端开发者的话你可能对这会很熟悉。

为了获得这些链接,我们要使用一个选择器,它能抓取在每一个 id 为video-summary-data的

中所有的元素。由于每个视频都有几个元素,我们将只保留那些 URL 以/video开头的元素,这些就是唯一的单独视频页面。实现上述标准的 CSS 选择器是div.video-summary-data a[href^=/video]。下面的代码片段通过 BeautifulSoup 使用这个选择器来获得指向视频页面的元素:


    import bs4
    soup = bs4.BeautifulSoup(response.text)
    links = soup.select('div.video-summary-data a[href^=/video]')

因为我们真正关心的是这个链接本身而不是包含它的元素,我们可以使用列表解析来改善上述代码。

links = [a.attrs.get('href') for a in soup.select('div.video-summary-data a[href^=/video]')]
现在,我们已经有了一个包含所有链接的数组,这些链接指向了每个单独页面。

下面这段脚本整理了目前我们提到的所有技术:


    import requests
    import bs4

    root_url = 'http://pyvideo.org'
    index_url = root_url + '/category/50/pycon-us-2014'

    def get_video_page_urls():
      response = requests.get(index_url)
      soup = bs4.BeautifulSoup(response.text)
      return [a.attrs.get('href') for a in soup.select('div.video-summary-data a[href^=/video]')]

    print(get_video_page_urls())

如果你运行上面这段脚本你将会获得一个满是 URL 的数组。现在我们需要去解析每个 URL 以获得更多关于每场 PyCon 会议的信息。

3.2抓取相连页面
下一步是加载我们的 URL 数组中每一个页面。如果你想要看看这些页面长什么样的话,这儿是个样例:http://pyvideo.org/video/2668/writing-restful-web-services-with-flask。没错,那就是我,那是我会议中的一个!

从这些页面我们可以抓取到会议的标题,在页面的顶部能看到它。我们也可以从侧边栏获得演讲者的姓名和 YouTube 的链接,侧边栏在嵌入视频的右下方。获取这些元素的代码展示在下方:


    def get_video_data(video_page_url):
      video_data = {}
      response = requests.get(root_url + video_page_url)
      soup = bs4.BeautifulSoup(response.text)
      video_data['title'] = soup.select('div#videobox h3')[0].get_text()
      video_data['speakers'] = [a.get_text() for a in soup.select('div#sidebar a[href^=/speaker]')]
      video_data['youtube_url'] = soup.select('div#sidebar a[href^=http://www.youtube.com]')[0].get_text()

关于这个函数需要注意的一些事情:

从首页抓取的 URL 是相对路径,所以root_url需要加到前面。
大会标题是从 id 为videobox的

里的

元素中获得的。注意[0]是必须的,因为调用select()返回的是一个数组,即使只有一个匹配。
演讲者的姓名和 YouTube 链接的获取方式与首页上的链接获取方式类似。
现在就剩下从每个视频的 YouTube 页面抓取观看数了。接着上面的函数写下去其实是非常简单的。同样,我们也可以抓取 like 数和 dislike 数。


    def get_video_data(video_page_url):
      # ...
      response = requests.get(video_data['youtube_url'])
      soup = bs4.BeautifulSoup(response.text)
      video_data['views'] = int(re.sub('[^0-9]', '',
                       soup.select('.watch-view-count')[0].get_text().split()[0]))
      video_data['likes'] = int(re.sub('[^0-9]', '',
                       soup.select('.likes-count')[0].get_text().split()[0]))
      video_data['dislikes'] = int(re.sub('[^0-9]', '', 
                        soup.select('.dislikes-count')[0].get_text().split()[0]))
      return video_data

上述调用soup.select()函数,使用指定了 id 名字的选择器,采集到了视频的统计数据。但是元素的文本需要被处理一下才能变成数字。考虑观看数的例子,在 YouTube 上显示的是"1,344 views"。用一个空格分开(split)数字和文本后,只有第一部分是有用的。由于数字里有逗号,可以用正则表达式过滤掉任何不是数字的字符。

为了完成爬虫,下面的函数调用了之前提到的所有代码:


    def show_video_stats():
      video_page_urls = get_video_page_urls()
      for video_page_url in video_page_urls:
        print get_video_data(video_page_url)

3.3并行处理
上面到目前为止的脚本工作地很好,但是有一百多个视频它就要跑个一会儿了。事实上我们没做什么工作,大部分时间都浪费在了下载页面上,在这段时间脚本时被阻塞的。如果脚本能同时跑多个下载任务,可能就会更高效了,是吗?

回顾当时写一篇使用 Node.js 的爬虫文章的时候,并发性是伴随 JavaScript 的异步特性自带来的。使用 Python 也能做到,不过需要显示地指定一下。像这个例子,我将开启一个拥有8个可并行化进程的进程池。代码出人意料的简洁:


    from multiprocessing import Pool

    def show_video_stats(options):
      pool = Pool(8)
      video_page_urls = get_video_page_urls()
      results = pool.map(get_video_data, video_page_urls)

multiprocessing.Pool 类开启了8个工作进程等待分配任务运行。为什么是8个?这是我电脑上核数的两倍。当时实验不同大小的进程池时,我发现这是最佳的大小。小于8个使脚本跑的太慢,多于8个也不会让它更快。

调用pool.map()类似于调用常规的map(),它将会对第二个参数指定的迭代变量中的每个元素调用一次第一个参数指定的函数。最大的不同是,它将发送这些给进程池所拥有的进程运行,所以在这个例子中八个任务将会并行运行。

节省下来的时间是相当大的。在我的电脑上,第一个版本的脚本用了75秒完成,然而进程池的版本做了同样的工作只用了16秒!

3.4完成爬虫脚本
我最终版本的爬虫脚本在获得数据后还做了更多的事情。

我添加了一个--sort命令行参数去指定一个排序标准,可以指定views,likes或者dislikes。脚本将会根据指定属性对结果数组进行递减排序。另一个参数,--max代表了要显示的结果数的个数,万一你只想看排名靠前的几条而已。最后,我还添加了一个--csv选项,为了可以轻松地将数据导到电子制表软件中,可以指定数据以 CSV 格式打印出来,而不是表对齐格式。

完整脚本显示在下方:


    import argparse
    import re
    from multiprocessing import Pool
    import requests
    import bs4

    root_url = 'http://pyvideo.org'
    index_url = root_url + '/category/50/pycon-us-2014'

    def get_video_page_urls():
      response = requests.get(index_url)
      soup = bs4.BeautifulSoup(response.text)
      return [a.attrs.get('href') for a in soup.select('div.video-summary-data a[href^=/video]')]

    def get_video_data(video_page_url):
      video_data = {}
      response = requests.get(root_url + video_page_url)
      soup = bs4.BeautifulSoup(response.text)
      video_data['title'] = soup.select('div#videobox h3')[0].get_text()
      video_data['speakers'] = [a.get_text() for a in soup.select('div#sidebar a[href^=/speaker]')]
      video_data['youtube_url'] = soup.select('div#sidebar a[href^=http://www.youtube.com]')[0].get_text()
      response = requests.get(video_data['youtube_url'])
      soup = bs4.BeautifulSoup(response.text)
      video_data['views'] = int(re.sub('[^0-9]', '',
                       soup.select('.watch-view-count')[0].get_text().split()[0]))
      video_data['likes'] = int(re.sub('[^0-9]', '',
                       soup.select('.likes-count')[0].get_text().split()[0]))
      video_data['dislikes'] = int(re.sub('[^0-9]', '',
                        soup.select('.dislikes-count')[0].get_text().split()[0]))
      return video_data

    def parse_args():
      parser = argparse.ArgumentParser(description='Show PyCon 2014 video statistics.')
      parser.add_argument('--sort', metavar='FIELD', choices=['views', 'likes', 'dislikes'],
                default='views',
                help='sort by the specified field. Options are views, likes and dislikes.')
      parser.add_argument('--max', metavar='MAX', type=int, help='show the top MAX entries only.')
      parser.add_argument('--csv', action='store_true', default=False,
                help='output the data in CSV format.')
      parser.add_argument('--workers', type=int, default=8,
                help='number of workers to use, 8 by default.')
      return parser.parse_args()

    def show_video_stats(options):
      pool = Pool(options.workers)
      video_page_urls = get_video_page_urls()
      results = sorted(pool.map(get_video_data, video_page_urls), key=lambda video: video[options.sort],
               reverse=True)
      max = options.max
      if max is None or max > len(results):
        max = len(results)
      if options.csv:
        print(u'"title","speakers", "views","likes","dislikes"')
      else:
        print(u'Views +1 -1 Title (Speakers)')
      for i in range(max):
        if options.csv:
          print(u'"{0}","{1}",{2},{3},{4}'.format(
            results[i]['title'], ', '.join(results[i]['speakers']), results[i]['views'],
            results[i]['likes'], results[i]['dislikes']))
        else:
          print(u'{0:5d} {1:3d} {2:3d} {3} ({4})'.format(
            results[i]['views'], results[i]['likes'], results[i]['dislikes'], results[i]['title'],
            ', '.join(results[i]['speakers'])))

    if __name__ == '__main__':
      show_video_stats(parse_args())

下方输出的是在我写完代码时前25个观看数最多的会议:


    (venv) $ python pycon-scraper.py --sort views --max 25 --workers 8
    Views +1 -1 Title (Speakers)
     3002 27  0 Keynote - Guido Van Rossum (Guido Van Rossum)
     2564 21  0 Computer science fundamentals for self-taught programmers (Justin Abrahms)
     2369 17  0 Ansible - Python-Powered Radically Simple IT Automation (Michael Dehaan)
     2165 27  6 Analyzing Rap Lyrics with Python (Julie Lavoie)
     2158 24  3 Exploring Machine Learning with Scikit-learn (Jake Vanderplas, Olivier Grisel)
     2065 13  0 Fast Python, Slow Python (Alex Gaynor)
     2024 24  0 Getting Started with Django, a crash course (Kenneth Love)
     1986 47  0 It's Dangerous to Go Alone: Battling the Invisible Monsters in Tech (Julie Pagano)
     1843 24  0 Discovering Python (David Beazley)
     1672 22  0 All Your Ducks In A Row: Data Structures in the Standard Library and Beyond (Brandon Rhodes)
     1558 17  1 Keynote - Fernando Perez (Fernando Perez)
     1449  6  0 Descriptors and Metaclasses - Understanding and Using Python's More Advanced Features (Mike Muller)
     1402 12  0 Flask by Example (Miguel Grinberg)
     1342  6  0 Python Epiphanies (Stuart Williams)
     1219  5  0 0 to 00111100 with web2py (G. Clifford Williams)
     1169 18  0 Cheap Helicopters In My Living Room (Ned Jackson Lovely)
     1146 11  0 IPython in depth: high productivity interactive and parallel python (Fernando Perez)
     1127  5  0 2D/3D graphics with Python on mobile platforms (Niko Skrypnik)
     1081  8  0 Generators: The Final Frontier (David Beazley)
     1067 12  0 Designing Poetic APIs (Erik Rose)
     1064  6  0 Keynote - John Perry Barlow (John Perry Barlow)
     1029 10  0 What Is Async, How Does It Work, And When Should I Use It? (A. Jesse Jiryu Davis)
     981 11  0 The Sorry State of SSL (Hynek Schlawack)
     961 12  2 Farewell and Welcome Home: Python in Two Genders (Naomi Ceder)
     958  6  0 Getting Started Testing (Ned Batchelder)

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237229次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录