使用 BeautifulSoup 和 Selenium 进行网页爬取

时间: 2018-10-17阅读: 793标签: 网页
来自:开源中国 翻译频道,英文原文

概述

HTML几乎是平铺直叙的。CSS是一个伟大的进步,它清晰地区分了页面的结构和外观。JavaScript添加一些魅力。道理上讲是这样的。现实世界还是有点不一样。

在本教程中,您将了解在浏览器中看到的内容是如何实际呈现的,以及如何在必要时进行抓取。特别是,您将学习如何计算Disqus评论。我们的工具是Python和这门语言的很棒的包,比如request、BeautifulSoup和Selenium。


什么时候应该使用网页爬取?

网页爬取是一种自动获取被设计于实现人工用户交互式网页的内容、解析它们并提取一些信息(可能是导航到其他页面的链接)的实践。如果没有其他方法来提取必要的网页信息时,网页爬取是很必要有效的技术方法。理想情况下,应用程序依靠提供好的专用API来编程自动获得网页的数据。可在下面几种场所景之下你最好就别用网页抓取技术了:

  • 被爬取的网页是脆弱的(您正在爬取的网页可能会被频繁更改)。

  • 爬取被禁止(一些web应用程序有禁止爬取的策略)。

  • 爬取速度可能会很慢和爬取内容过于繁杂的(如果你需要在很多无用信息中寻找和涉猎你想要的东东)。


了解真实的网页

让我们通过查看一些常见web应用程序代码的实现情况,来了解我们面临的问题。例如在“Vagrant技术入门”(链接:https://code.tutsplus.com/tutorials/introduction-to-vagrant--cms-25917)这篇帖子的页面底部有一些Disqus的评论,为了爬取这些评论,我们需要首先在页面上找到它们。


查看页面代码

自20世纪90年代以来,每个浏览器都支持查看当前页面的HTML代码。下面是在源码视图下观看到的是“Vagrant技术入门”这篇帖子对应的源码内容的一个片段,这篇源码以大量与本文本身内容无关的被压缩过的和丑陋的JavaScript代码开始。下面是其中的一”小“部分:


这是页面中的一些实际HTML代码:


代码看起来乱糟糟,你竟然在页面的源代码中找不到Disqus评论,这让你有些吃惊。



强大的内联框架

原来页面是一个”混搭“, Disqus评论被嵌入到iframe(内联框架)元素中。你可以通过右键点击评论区域找到它,你会看到那里有框架信息和源码。这是有意义的。将第三方内容嵌入iframe是使用iframe的主要应用场景之一。让我们在主页源中找到iframe标记。完蛋了!主页源中没有iframe标记。  


JavaScript-Generated标记

这个遗漏的原因是view page source显示了从服务器获取的内容。但是,由浏览器呈现的最终DOM(文档对象模型)可能非常不同。JavaScript开始工作,可以随意操纵DOM。无法找到iframe,因为从服务器检索页面时,它就是不存在。


静态抓取 vs. 动态抓取

静态抓取会忽略 JavaScript, 它可以不依靠浏览器而直接从服务器端获取网页代码. 这就是你通过"查看源码"所看到的东西, 然后你就可以进行信息提取了. 如果你要查找的内容已经存在于源码中, 那就不需要进一步的动作了. 可是, 如果你要查找的内容像上文的 Disqus 评论一样被嵌入iframe 中, 你就必须使用动态爬取来获取内容.

动态爬取使用一个真实的浏览器(或无界面浏览器), 它先让页面内的 JavaScript 运行起来, 完成动态内容处理加载. 之后, 它再通过查询 DOM 来获取所要寻找的内容. 有时候, 你还需要让浏览器自动模拟人的操作来得到你所需要的内容.


使用 Requests 和 BeautifulSoup 进行静态抓取

让我们来看看如何使用 Python 的两个经典包来进行静态抓取: requests 用来抓取网页内容. BeautifulSoup用来解析 HTML.


安装 Requests 和 BeautifulSoup

首先安装 pipenv, 然后运行命令: pipenv install requests beautifulsoup4

它首先为你创建一个虚拟环境, 然后安装这两个包在虚拟环境里. 如果你的代码在gitlab上, 你可以使用命令 pipenv install 来安装.


获取网页内容

用 requests 抓取网页内容只需要一行代码: 

r = requests.get(url).

代码返回一个 response 对象, 它包含大量有用的属性. 其中最重要的属性是 ok 和 content. 如果请求失败, r.ok 为 False 并且 r.content 包含该错误信息. content 代表一个字节流, 做文本处理时, 你最好将它解码成 utf-8.

>>> r = requests.get('http://www.c2.com/no-such-page')
>>> r.ok
False
>>> print(r.content.decode('utf-8'))
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /ggg was not found on this server.</p>
<hr>
<address>
Apache/2.0.52 (CentOS) Server at www.c2.com Port 80
</address>
</body></html>

如果代码正常返回没有报错, 那 r.content 会包含请求的网页源码(就是"查看源码"所看到的内容).


用 BeautifulSoup 查找元素

下面的 get_page() 函数会获取给定 URL 的网页源码, 然后解码成 utf-8, 最后再将 content 传递给 BeautifulSoup 对象并返回, BeautifulSoup 使用 HTML 解析器进行解析.

def get_page(url):
    r = requests.get(url)
    content = r.content.decode('utf-8')
    return BeautifulSoup(content, 'html.parser')

我们获取到 BeautifulSoup 对象后, 就可以开始解析所需要的信息了.

BeautifulSoup 提供了很多查找方法来定位网页中的元素, 并可以深入挖掘出嵌套的元素.

Tuts+ 网站包含了很多培训教程, 这里(https://tutsplus.com/authors/gigi-sayfan)是我的主页. 在每一个页面包含最多12篇教程, 如果你已经获取了12篇的教程, 你就可以进入下一页面了. 每一篇文章都被 <article> 标签包围着. 下面的函数就是发现页面里的所有 article 元素, 然后找到对应的链接, 最后提取出教程的 URL.

page = get_page('https://tutsplus.com/authors/gigi-sayfan')
articles = get_page_articles(page)
prefix = 'https://code.tutsplus.com/tutorials'
for a in articles:
    print(a[len(prefix):])  
    
     
Output:
building-games-with-python-3-and-pygame-part-5--cms-30085
building-games-with-python-3-and-pygame-part-4--cms-30084
building-games-with-python-3-and-pygame-part-3--cms-30083
building-games-with-python-3-and-pygame-part-2--cms-30082
building-games-with-python-3-and-pygame-part-1--cms-30081
mastering-the-react-lifecycle-methods--cms-29849
testing-data-intensive-code-with-go-part-5--cms-29852
testing-data-intensive-code-with-go-part-4--cms-29851
testing-data-intensive-code-with-go-part-3--cms-29850
testing-data-intensive-code-with-go-part-2--cms-29848
testing-data-intensive-code-with-go-part-1--cms-29847
make-your-go-programs-lightning-fast-with-profiling--cms-29809


使用 Selenium 动态爬取

静态爬取很适合一系列的文章,但正如我们前面看到的,Disqus 的评论是由 JavaScript 写在一个 iframe 中的。为了获取这些评论,我们需要让浏览器自动与DOM 交互。做这种事情最好的工具之一就是 Selenium。

Selenium 主要用于 Web 应用自动化测试,但它也是一个不错的通用浏览器自动化工具。


安装 Selenium

用这个命令安装 Selenium:

pipenv install selenium


选择你的 Web 驱动

Selenium 需要一个 Web 驱动(自动化用的浏览器)。对于网页爬取来说,一般不需要在意选用哪个驱动。我建议使用 Chrome 驱动。Selenium 手册中有相关的介绍。


对比 Chrome 和 PhantomJS

某些情况下你可能想用没有用户界面的(headless)浏览器。理论上来说,PhantomJS 正好就是那款 Web 驱动。但是实际上有人报告一些只会在 PhantomJS 中出现的问题,这些问题在 Selenium 使用 Chrome 或 Firefox 时并不会出现。我喜欢从等式中删除这一变量,使用实际的 Web 浏览器驱动。


统计 Disqus 评论数量

我们来搞点动态抓取,使用 Selenium 统计 Tuts+ 手机的 Disqus 评论数量。下面需要导入的内容。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.expected_conditions import (
    presence_of_element_located)
from selenium.webdriver.support.wait import WebDriverWait

get_comment_count() 函数需要传入 Selenium 驱动和 URL 作为参数。它使用驱动的 get() 方法从 URL 获取内容。这和requests.get()相似,其不同之处在于使用驱动对象管理 DOM 的实时呈现。

然后,它获取教程的标题,并使用 iframe 的父级 id,disqus_thread,和 iframe 标签来定位 iframe:

def get_comment_count(driver, url):
    driver.get(url)
    class_name = 'content-banner__title'
    name = driver.find_element_by_class_name(class_name).text
    e = driver.find_element_by_id('disqus_thread')
    disqus_iframe = e.find_element_by_tag_name('iframe')
    iframe_url = disqus_iframe.get_attribute('src')

接下来获取 iframe 的内容。注意我们要等到 comment-count 元素出现,因为评论是动态加载的,不一定可用。

driver.get(iframe_url)
wait = WebDriverWait(driver, 5)
commentCountPresent = presence_of_element_located(
    (By.CLASS_NAME, 'comment-count'))
wait.until(commentCountPresent)

comment_count_span = driver.find_element_by_class_name(
    'comment-count')
comment_count = int(comment_count_span.text.split()[0])

最后部分是返回最新的评论, 当然不包括我自己的评论. 方法是检查我还没有回复的评论.

last_comment = {}
if comment_count > 0:
    e = driver.find_elements_by_class_name('author')[-1]
    last_author = e.find_element_by_tag_name('a')
    last_author = e.get_attribute('data-username')
    if last_author != 'the_gigi':
        e = driver.find_elements_by_class_name('post-meta')
        meta = e[-1].find_element_by_tag_name('a')
        last_comment = dict(
            author=last_author,
            title=meta.get_attribute('title'),
            when=meta.text)
return name, comment_count, last_comment


结论

网页爬取是一个非常实用的技术, 尤其当你需要处理的信息浏览器并不提供有用的API支持的时候. 它通常需要一些技巧来从现代web应用中提取信息, 不过一些成熟的、设计良好的工具, 比如: requests、BeautifulSoup、Selenium 都会减轻你的工作并提高效率.

最后, 你可以试一下我写的一些工具, 它们在 Envato Market 有售, 欢迎提问和反馈.


链接: http://www.fly63.com/article/detial/1156

网页设计十大流行趋势

最近看到越来越多的网络设计尝试个性化的风格,其中比较突出的一点是个性化字体的增多:用自己独特设计的字体代替标准印刷体,让设计更加独特,配色改变反应了人们审美需求的改变。同色系网站设计将成为主流

静态和动态网页的区别?

在静态web程序中,客户端使用web浏览器经过网络连接到服务器上,使用HTTP协议发起一个请求(Request),告诉服务区我现在需要得到哪个页面,所有的请求交给web服务器

FreeMarker网页静态化

网页静态化解决方案在实际开发中运用比较多,例如新闻网站,门户网站中的新闻频道或者是文章类的频道。网页静态化技术和缓存技术的共同点都是为了减轻数据库的访问压力,但是具体的应用场景不同

使用 CSS 追踪用户

除了使用 JS 追踪用户,现在有人提出了还可以使用 CSS 进行网页追踪和分析,译者认为,这种方式更为 优雅,更为 简洁,且 不好屏蔽,值得尝试一波

h5网页水印SDK的实现代码示例

在网站浏览中,常常需要网页水印,以便防止用户截图或录屏暴露敏感信息后,追踪用户来源。如我们常用的钉钉软件,聊天背景就会有你的名字。那么如何实现网页水印效果呢?

静态网页与动态网页的区别?

静态网页是标准的HTML文件,它的文件扩展名是.htm、.html,静态网页是网站建设的基础。从网站浏览者的角度来看,无论是动态网页还是静态网页,都可以展示基本的文字和图片信息,但从网站开发、管理、维护的角度来看就有很大的差别。

实现主机访问虚拟机网页的方法总结

VMware Workstation提供了两种虚拟机上网方式,一种bridge,一种NAT,bridge可以获得公网地址,而NAT只能是内网地址了。例1:在虚拟机内搭建http服务器,使用公网地址访问,例2: ssh端口映射

html页面锚点链接对SEO的作用,以及实现描点的三种方式

锚链链接是一个非常重要的概念,在网页中增加恰当的锚链接,会让所在网页和所指向网页的重要程度有所提升,从而影响到关键词排名。锚链接对SEO的作用主要体现在以下几个方面

JS判断网页广告是否被浏览器拦截过滤的代码

js地址包含ads.php、ad.js甚至是gg.js都可能会被过滤,360浏览器甚至会把包含ad.gif、960x90.jpg带有广告字眼的图片地址都会屏蔽掉。如果我们自己投放自定义的广告,一定要尽量避免这些广告字眼。其实这个js文件的内容就是一个函数判断语句var killads = true;

html网页自动跳转方法_整理网页自动跳转的5种方法

网页自动跳转,是指当用户访问某个网页时,被自动跳转到另一个网页中去。网页自动跳转的主要作用是,当域名变更后,或者网站里的一个或多个网页被删除后,可以使用这种方式将用户引导到其它正常的网页中去,从而留住用户。

fly63.com版权所有,内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权,请与小编联系!情况属实本人将予以删除!

广告赞助文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全