网页抓取数据( Python中正则表达式的3种抓取其中数据的方法(上))

优采云 发布时间: 2021-11-03 23:01

  网页抓取数据(

Python中正则表达式的3种抓取其中数据的方法(上))

  

  3种获取数据的方法。首先是正则表达式,然后是流行的 BeautifulSoup 模块,最后是强大的 lxml 模块。

  1 正则表达式

  如果你不熟悉正则表达式,或者需要一些提示,那么你可以查看完整的介绍。即使你已经使用过其他编程语言中的正则表达式,我仍然建议你一步一步复习 Python 中正则表达式的编写。

  由于可以在每章中构建或使用前面章节的内容,因此我建议您遵循类似于本书代码库的文件结构。所有代码都可以从代码库的代码目录中运行,以便导入正常工作。如果要创建不同的结构,请注意所有其他章节的导入操作都需要更改(例如以下代码中的chp1.advanced_link_crawler)。

  当我们使用正则表达式抓取一个国家(或地区)的面积数据时,首先需要尝试匹配``元素中的内容,如下图。

  

>>> import re

>>> from chp1.advanced_link_crawler import download

>>> url = 'http://example.python-scraping.com/view/UnitedKingdom-239'

>>> html = download(url)

>>> re.findall(r'(.*?)', html)

[&#39;<img />

&#39;,

&#39;244,820 square kilometres&#39;,

&#39;62,348,447&#39;,

&#39;GB&#39;,

&#39;United Kingdom&#39;,

&#39;London&#39;,

&#39;<a>EU</a>

&#39;,

&#39;.uk&#39;,

&#39;GBP&#39;,

&#39;Pound&#39;,

&#39;44&#39;,

&#39;@# #@@|@## #@@|@@# #@@|@@## #@@|@#@ #@@|@@#@ #@@|GIR0AA&#39;,

&#39;^(([A-Z]d{2}[A-Z]{2})|([A-Z]d{3}[A-Z]{2})|([A-Z]{2}d{2} [A-Z]{

2})|([A-Z]{2}d{3}[A-Z]{2})|([A-Z]d[A-Z]d[A-Z]{2}) |([A-Z]{2}d[A-Z]

d[A-Z]{2})|(GIR0AA))$&#39;,

&#39;en-GB,cy-GB,gd&#39;,

&#39;<a>IE </a>

&#39;]

  从上面的结果可以看出,多个国家(或地区)属性使用了``标签。如果我们只想捕获一个国家(或地区)的面积,我们可以只选择第二个匹配元素,如下图。

  

>>> re.findall(&#39;(.*?)&#39;, html)[1]

&#39;244,820 square kilometres&#39;

  虽然现在可以使用这个方案,但是如果网页发生变化,该方案很可能会失败。例如,表发生了变化,删除了第二个匹配元素中的区域数据。如果我们现在只抓取数据,我们可以忽略这种可能的未来变化。但是,如果我们希望能够在未来的某个时刻再次捕获数据,我们需要提供更健壮的解决方案,以尽可能避免这种布局更改的影响。为了使正则表达式更加明确,我们还可以添加其父元素。因为这个元素有一个 ID 属性,它应该是唯一的。

  

>>> re.findall(&#39;Area:

(.*?)&#39;, html)

[&#39;244,820 square kilometres&#39;]

  这个迭代版本看起来更好,但是还有很多其他的更新网页的方式,也会让正则表达式不尽人意。例如,将双引号更改为单引号,在`labels 之间添加额外的空格,或者更改area_label` 等。下面是一个尝试支持这些可能性的改进版本。

  

>>> re.findall(&#39;&#39;&#39;.*?(.*?)&#39;&#39;&#39;, html)

[&#39;244,820 square kilometres&#39;]

  这种正则表达式虽然更容易适应未来的变化,但存在构建困难、可读性差的问题。此外,还有许多其他细微的布局更改会使正则表达式不令人满意,例如在`tag 中添加标题属性,或者为tr 和td` 元素修改它们的CSS 类或ID。

  从这个例子可以看出,正则表达式为我们提供了抓取数据的捷径,但是这种方式过于脆弱,在网页更新后容易出现问题。幸运的是,有更好的数据提取解决方案,例如我们将在本章中介绍的其他爬虫库。

  2美汤

  美汤

  它是一个非常流行的 Python 库,可以解析网页并提供方便的界面来定位内容。如果您还没有安装该模块,您可以使用以下命令安装最新版本。

  

pip install beautifulsoup4

  使用 Beautiful Soup 的第一步是将下载的 HTML 内容解析成一个 Soup 文档。由于很多网页没有好的HTML格式,Beautiful Soup需要修改其标签打开和关闭状态。例如,在下面的简单网页列表中,存在属性值周围缺少引号和未关闭标签的问题。

  

Area

Population

  如果将 Population 列表项解析为 Area 列表项的子元素,而不是两个并排的列表项,我们在爬行时会得到错误的结果。我们来看看Beautiful Soup是如何处理的。

  

>>> from bs4 import BeautifulSoup

>>> from pprint import pprint

>>> broken_html = &#39;AreaPopulation

&#39;

>>> # parse the HTML

>>> soup = BeautifulSoup(broken_html, &#39;html.parser&#39;)

>>> fixed_html = soup.prettify()

>>> pprint(fixed_html)

Area

Population

  我们可以看到使用默认的 html.parser 无法正确解析 HTML。从前面的代码片段可以看出,由于使用了嵌套的li元素,可能会造成定位困难。幸运的是,我们还有其他解析器可供选择。我们可以安装LXML(2.2.将在第3节详细介绍),或者使用html5lib。要安装 html5lib,只需使用 pip。

  

pip install html5lib

  现在,我们可以重复这段代码,只对解析器进行以下更改。

  

>>> soup = BeautifulSoup(broken_html, &#39;html5lib&#39;)

>>> fixed_html = soup.prettify()

>>> pprint(fixed_html)

Area

Population

  至此,BeautifulSoup 使用 html5lib 已经能够正确解析缺失的属性引号和结束标记,并添加了 &amp; 标记,使其成为一个完整的 HTML 文档。当您使用 lxml 时,您可以看到类似的结果。

  现在,我们可以使用 find() 和 find_all() 方法来定位我们需要的元素。

  

>>> ul = soup.find(&#39;ul&#39;, attrs={&#39;class&#39;:&#39;country_or_district&#39;})

>>> ul.find(&#39;li&#39;) # returns just the first match

Area

>>> ul.find_all(&#39;li&#39;) # returns all matches

[Area

, Population

  有关可用方法和参数的完整列表,请访问 Beautiful Soup 的官方文档。

  以下是示例网站中使用该方法提取国家(或地区)面积数据的完整代码。

  

>>> from bs4 import BeautifulSoup

>>> url = &#39;http://example.python-scraping.com/places/view/United-Kingdom-239&#39;

>>> html = download(url)

>>> soup = BeautifulSoup(html)

>>> # locate the area row

>>> tr = soup.find(attrs={&#39;id&#39;:&#39;places_area__row&#39;})

>>> td = tr.find(attrs={&#39;class&#39;:&#39;w2p_fw&#39;}) # locate the data element

>>> area = td.text # extract the text from the data element

>>> print(area)

244,820 square kilometres

  这段代码虽然比正则表达式代码复杂,但更容易构建和理解。此外,我们不需要担心布局的微小变化,例如额外的空间和标签属性。我们也知道,即使页面收录不完整的 HTML,Beautiful Soup 也可以帮助我们组织页面,以便我们从非常不完整的 网站 代码中提取数据。

  3Lxml

  xml文件

  它是在 libxml2 的基础上构建的 Python 库,是一个 XML 解析库。它是用C语言编写的,解析速度比Beautiful Soup更快,但安装过程比较复杂,尤其是在Windows下。您可以参考最新的安装说明。如果自己安装库有困难,也可以使用Anaconda来实现。

  你可能不熟悉 Anaconda,它是一个由员工创建的包和环境管理器,专注于开源数据科学包。您可以根据其安装说明下载并安装 Anaconda。需要注意的是,使用 Anaconda 的快速安装会将你的 PYTHON_PATH 设置为 Conda 的 Python 安装位置。

  与 Beautiful Soup 一样,使用 lxml 模块的第一步是将潜在的非法 HTML 解析为统一格式。以下是使用该模块解析相同不完整 HTML 的示例。

  

>>> from lxml.html import fromstring, tostring

>>> broken_html = &#39;AreaPopulation

&#39;

>>> tree = fromstring(broken_html) # parse the HTML

>>> fixed_html = tostring(tree, pretty_print=True)

>>> print(fixed_html)

Area

Population

  同理,lxml 可以正确解析属性两边缺失的引号并关闭标签,但模块不会添加额外的和标签。这些不是标准 XML 的要求,因此对于 lxml,插入它们是没有必要的。

  解析输入内容后,进入选择元素的步骤。这时候lxml有几种不同的方法,比如XPath选择器和类似于Beautiful Soup的find()方法。但是,在这个例子中,我们将使用 CSS 选择器,因为它更简洁,可以在第 5 章解析动态内容时重复使用。 一些读者可能已经熟悉了它们,因为他们有过 jQuery 选择器的经验,或者它们在前面的使用——结束 Web 应用程序开发。在本章的其余部分,我们将比较这些选择器与 XPath 的性能。要使用 CSS 选择器,您可能需要先安装 cssselect 库,如下所示。

  

pip install cssselect

  现在,我们可以使用 lxml 的 CSS 选择器来提取示例页面中的区域数据。

  

>>> tree = fromstring(html)

>>> td = tree.cssselect(&#39;tr#places_area__row > td.w2p_fw&#39;)[0]

>>> area = td.text_content()

>>> print(area)

244,820 square kilometres

  通过在代码树上使用cssselect方法,我们可以使用CSS语法来选择表中ID为places_area__row的行元素,然后是w2p_fw类的子表数据标签。由于cssselect返回的是一个列表,我们需要获取第一个结果并调用text_content方法迭代所有子元素并返回每个元素的相关文本。在这个例子中,虽然我们只有一个元素,但这个特征对于更复杂的提取例子非常有用。

  标签:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线