用美汤刮去冠状病毒死亡人口统计
网页抓取教程
马库斯·斯皮斯克在 Unsplash 上的照片
随着冠状病毒在全球蔓延,详细的数据对于人们了解这种病毒变得至关重要。许多优秀的数据源已经发布了可供研究使用的数据(本文中的综述,以及本文中的中与 GIS 相关的综述)。但是,大部分数据来源集中在不同地点的确诊/死亡/康复计数和检测计数。如果您想要其他信息,例如性别和年龄等死亡人口统计信息,该怎么办?
在本教程中,我们将浏览伊利诺伊州卫生部每日新闻网页,使用 Python 和 Beautiful Soup library 获取冠状病毒死亡人口统计数据。
1.调查网页的逻辑结构
首先,我们知道伊利诺伊州卫生部(IDPH)有每日简报,其中包含死亡人口统计数据。让我们以 2020 年 4 月为例,访问以下网页
快速浏览可以发现以下信息:
- 该网址以的格式按年月组织。
- 这个网页包含每月的所有每日报告,最新的报告在最上面。每条每日新闻都可以用相应的日期串来标识,格式为。
- 每日新闻只有一个部分显示在这个网页上。完整的每日报告实际上在右下角的“阅读更多”链接中。
2.准备汤品
因为我们知道 HTTP 地址,所以我们可以很容易地用漂亮的 soup 构造一个 Soup 对象。
3.查找相关信息
3.1 确定切入点
大多数现代网络浏览器都提供了查看网页源代码的基本工具。以 Chrome 为例,可以使用键调出侧面的 DevTools 控制台,如下图:
乍一看,右边的内容让人望而生畏,尤其是对于不熟悉 HTML 的人来说。怎么才能在这一大堆乱七八糟的东西中找到相关的信息呢?
好消息是,在大多数情况下,我们不必知道网页的完整结构。例如,由于在步骤#1 中我们已经确定日期字符串指定了新闻的位置,我们可以使用 Ctrl-F 搜索日期字符串“15th Apr ”,以快速定位感兴趣的部分:
如果您将鼠标悬停在“4 月 15 日”上,左侧网页上的相应部分将会高亮显示。这确认了代码位置。
在确认了逻辑位置之后,我们可以使用find_all
函数在 soup 对象中复制它,以找到所有的 div ,并使用其 span 具有正确字符串的那个。
运行上面的代码后,我们可以打印 target_elm 来确认它是否是包含“2020 年 4 月 15 日”的元素:
3.2 找到从入口点到兴趣点的相对路径
正如我们在#1 中讨论的,详细的每日报告实际上在另一个位置,在“阅读更多”链接下。我们可以使用相同的技巧找到相对路径,即在 DevTools 控制台中搜索“Read more ”:
方便的是,包含“Read more”链接的元素和包含“15th Apr”字符串的元素属于同一个parent
。因此,我们可以使用 soup 元素的parent
属性来导航结构,然后使用.a.get('href')
来获得超链接。
在获得日期的详细报告的超链接之后,我们可以请求新的网页,并相应地创建新的 soup 对象:
然后我们可以打开实际的每日报告页面,并使用相同的技巧,搜索“Cook County”来定位代码的相关部分。正如我们所看到的,详细的分解是在一个无序列表中(
标签),在一个 div 类“field-item even”下。
4.提取信息
找到列表后,我们可以通过使用find_all
函数搜索< li >标签来迭代列表,然后使用.text
属性获取相应的文本。
一旦我们得到诸如“1 男 30”这样的字符串,我们就可以从字符串中提取计数/性别/年龄信息。然后我们可以将它们放入熊猫数据框中进行进一步分析。
上面代码中的核心分析是我们必须实现的extract()
函数。基本思想很简单——我们可以将字符串分成三个子字符串,然后相应地提取计数、性别和年龄。
然而,现实要复杂得多,也容易出错,就像任何网页抓取一样。因为新闻文本是由人类生成的,尽管格式相对稳定,但仍有许多细微差别可能会发生变化。仅举几个例子:
- 在 2020 年 3 月 22 日之前,死亡人口统计日报的格式不太结构化。
- 有时“年龄”部分不以“s”结尾。比如“1 男 100+”。更糟糕的是,有时它使用“青少年”或“婴儿”这样的词来指定年龄。
- 有“未知”和“不完整”的条目。
- 有时死亡计数“1”会被忽略。
- ……
毕竟,完整的实现可以在这个 GitHub repo 中的utils.parse_IL_death_demographic()
中找到。作为一条支持信息,我在输出数据框中添加了新闻页面的链接,以便在必要时可以快速验证结果。最终的输出数据帧如下所示:
5.绘制死亡人口统计图
经过以上努力,我们从《IDPH 日报》中获得了死亡人口统计数据框架。这样网页抓取就完成了。但是在宣布胜利之前检查结果总是一个好主意。Pandas pivot_table()
提供了聚集数据的灵活方式。例如,我们可以看看每日死亡人数时间序列:
上述情节很容易被新闻来源所证实,这使我们对我们的结果有了一些信心。
最后,我们可以看看死亡人口统计。按年龄和性别划分的堆积条形图是一个很好的表示。这可以通过将数据框旋转到另一个方向来轻松实现:
最后注意:
一般来说,网页抓取并不健壮,因为网页会在你不知道的情况下改变。因此,我们总是更喜欢更健壮的 API。但是当这不可用时,网络搜集可以作为另一个获取数据的途径。
承认
我要感谢我的朋友 David Tian,他是一名机器学习工程师,他对本文提出了宝贵的建议。看看他有趣的自驾* DeepPiCar *博客。
使用 Python 抓取 COVID19 数据
乔希·希尔德在 Unsplash 上的照片
通过各种尝试来抑制 COVID19 对世界的影响,各种研究工作和创新措施依赖于从正确的数据中获得的洞察力。帮助创新所需的大部分数据可能无法通过应用编程接口(API) 或类似于“的文件格式获得。CSV’等待下载,但只能作为网页的一部分访问。所有代码片段都可以在这里找到。
Web scraping 是一个术语,用来描述使用程序或算法从 Web 中提取和处理大量数据。无论您是数据科学家、工程师,还是分析大量数据集的任何人,从 web 上抓取数据的能力都是一项有用的技能。
Worldometers 在全球拥有可靠的 COVID19 数据来源。在本文中,我们将学习如何使用 python 将下面描述的 COVID19 数据从网页抓取到站点的 Dask dataframe 。
来自 Worldometer 的 COVID19 病例报告
为什么是 Dask dataframe?
Pandas 已经成为 Python 编程语言中用于数据争论和分析的最流行和最受欢迎的数据科学工具之一。由于算法和本地内存限制,熊猫在大数据方面有自己的局限性。
然而, Dask 是一个开源的免费的 Python 库。Dask 提供了在性能和可伸缩性方面扩展 Pandas、 Scikit-Learn 和 Numpy 的方法。在本文的上下文中,数据集必然会不断增加,这使得 Dask 成为理想的工具。
网页的元素
照片由 Pankaj Patel 在 Unsplash 上拍摄
在我们深入研究网络抓取之前,让我们先弄清楚网页和网站的区别。一个网页可以被认为是一个单独的实体,而一个网站是多个网页的组合。在网站 HTTP 中,通过浏览器访问网页,并使用 DNS 协议来访问它。网站中的内容根据网页而变化,而网页包含更具体的信息。
网页有四(4)个基本元素,它们是:
- 结构
- 功能
- 内容
- 美学
上面列出的元素,属于但不限于这些可编程组件,如HTML—包含页面的主要内容, CSS —添加样式使页面看起来更好,最后JS—JavaScript 文件为网页添加交互性。****
当我们执行 web 抓取时,我们感兴趣的是从 web 页面的主要内容中提取信息,这使得很好地理解 HTML 变得很重要。
超文本标记语言(HTML)
来自 Logolynx 的 HTML 徽标
超文本标记语言(HTML)是一种创建网页的语言。HTML 不像 Python 那样是一种编程语言,相反,它是一种告诉浏览器如何布局内容的标记语言。
让我们快速浏览一下 HTML,这样我们就有足够的知识来有效地抓取。HTML 由称为标签的元素组成。
HTML 代码的外观
这是 HTML 网页的基本语法。每一个<tag>
在网页里面服务一个区块。head
标签包含了关于页面标题的数据,而 HTML 文档的可见部分在body
标签之间。p
标签定义了一个段落,标签内的任何文本都显示为一个单独的段落。最后,从我们的代码片段来看。我们添加了两个a
标签,允许输入链接,并告诉浏览器呈现另一个网页的链接。标签的href
属性决定了链接的位置。如需标签的完整列表,请点击 查看 。
另外,HTML 标签有时会带有id
和class
。属性为一个 HTML 标签指定了一个唯一的 id,这个值对于 HTML 文档来说必须是唯一的。class
属性用于为同一个类的 HTML 标签定义相等的样式(CSS 操作)。
下载用于网络抓取的网页
要抓取一个网页,我们需要做的第一件事是下载页面。我们可以使用 Python 请求库下载页面。在我们使用它之前,我们必须安装它。在终端上,下面的代码将安装这个库。
pip install requests
请求库将向 web 服务器发出一个GET
请求,它将为我们下载给定网页的 HTML 内容。
运行我们的请求后,我们得到一个响应对象。这个对象有一个status_code
属性,指示页面是否下载成功。200
中的status_code
表示页面下载成功。以2
开头的状态码通常表示成功,要了解更多关于状态码的信息,请点击链接。
HTML 解析
解析简单地说就是在语法的指导下把句子结构分解成成分。因此,“HTML 解析”意味着接受 HTML 代码,并从其各种标签中提取相关信息。解析内容的计算机程序称为解析器。在本文中,我们将使用’ BeautifulSoup '库来解析我们下载的 HTML 文档并帮助正确提取。在我们使用它之前,我们必须安装它。在终端上,下面的代码将安装这个库。
pip install beautifulsoup4
一旦安装了“BeautifulSoup”包,您就可以开始通过 BeautifulSoup 对象解析 HTML 文档了。
这里使用了“lxml”解析器,因为它可以处理不完整的 html,并且被广泛使用。
检查 HTML 网页
为了从网页中充分提取内容,我们必须检查网页以识别其属性和标签。通过右键单击网页上的任意位置并选择“检查”来检查网页在本文的上下文中,我们正在寻找与“已报告冠状病毒病例”更新表相关的属性和标签。这就是结果的样子。
HTML 检查
提取表格
<table id="main_table_countries_today" class="table table-bordered table-hover main_table_countries dataTable no-footer" style="width: 100%; margin-top: 0px !important;">
检查之后,属性“id”被识别出来,并将用于过滤 HTML 文档以获得所需的表格元素。
从提取的表格中获取文本
可以看出,提取的表格中嵌入了必不可少的 HTML 标签。本文的目标是从网页中取出一个表格,并将其转换成数据帧,以便使用 Python 进行操作。为此,我们首先以列表形式逐行获取所需的数据(文本),然后将该列表转换为 dataframe。
值得注意的是,标签td
、tr
、th
分别代表表格的列、行和表头。
转换成 Dask 数据帧
下一步是将该列表转换成 Dask 数据帧,以便进行数据操作和清理。如前所述。由于数据每天都在增加,建议使用 Dask 数据框架,它可以更有效地处理大数据。我们将创建一个 pandas 数据帧,并将其转换为 dask 数据帧以实现可伸缩性。生成的表格需要一些清晰的格式。
导出到 csv 以供将来使用
逗号分隔值( CSV )是一种广泛使用的文件格式,它将表格数据(数字和文本)存储为纯文本。它的流行和可行性是由于大量的程序和应用程序支持 csv 文件。
当视图显示从 Worldometers conoravirus 病例报告网页中成功提取的表格时,生成“csv”
我希望这篇文章有助于进一步的研究工作和创新,收集数据来抑制疫情。感谢阅读,并保持安全!
抓取网络流量中的数据
在这一点上,我已经看到了许多关于如何使用 BeautifulSoup 或类似工具抓取数据的教程,但您可能没有考虑过从您感兴趣的网站抓取网络流量来获取数据。
为什么要刮网络流量?
您可能已经知道 http 请求为 web 提供了动力,您的浏览器发出了对某个网页的请求,并返回了该网页的数据。这不是对一个页面的单独请求,而是对不同数据的许多请求,这些数据被放在一起,形成您在浏览器中看到的内容。假设你在一个社交网站上发了一个帖子,然后你的朋友评论了这个帖子。您的浏览器只请求与您的帖子相关的数据,而不是请求重新加载整个页面来显示您朋友最近的评论。这减少了必须请求的数据量,最终用户不必经历整个页面被重新加载。这对于抓取数据来说是幸运的,因为您经常需要的数据可能在您可以捕获的请求中。
检查网络流量
为了从请求中抓取数据,在浏览器中检查网络流量是很有用的。比方说,你想获得北达科他州冠状病毒的数据,并决定删除北达科他州卫生部的网站。你很难抓取它,因为数据包含在一些交互式小部件(如下图)中,而不是像 HTML 表那样容易抓取的东西。
首先,您可以在页面上的任意位置单击鼠标右键,然后在 Mac 上单击“检查”或 Cmd+Option+i,在 Windows 上单击 Cmd+Shift+C。这将在你的浏览器中打开开发者工具,我正在使用谷歌浏览器,所以你的开发者工具可能看起来不同。单击网络选项卡将其打开。然后选择 XHR (XMLHttpRequests)。现在重新加载页面。
现在您可以看到,有一些数据通过网络传输。在本例中,包含北达科他州各县数据的是 4 个 CSV。这比用 BeautifulSoup 刮页面方便多了。
访问数据
在这里,您可以单击其中一个 CSV 来查看数据(如下所示),但是您可能希望采用一种更加程序化的方法,不需要任何手动步骤。
通过单击 Headers 选项卡,您可以看到获取这些数据是通过一个特定 url 上的 GET 请求完成的。我们可以获取这个 url,并在程序中发出这个请求。
使用 python 的 request、io 和 pandas 包,您可以从代码中请求有问题的 CSV。
这种方法可以使您拥有北达科他州新冠肺炎的数据可视化,显示与北达科他州卫生部一样最新的数据,而不必运行可能因页面布局的变化而频繁中断的刮刀。
发布请求中的数据
北达科他州冠状病毒数据相当容易访问,因为它保存在一个相对简单的 GET 请求中。让我们考虑一下南达科他州卫生部网站上的南达科他州冠状病毒数据。让我们从导航到开发者工具的网络流量窗格的相同过程开始。
如您所见,我们无法通过简单的 GET 请求轻松提取 CSV。我们需要的数据包含在名为 querydata 的第一个请求中。同步=真。这里,您需要用请求中正确的头和数据发出 POST 请求。右键单击它并导航到选项复制为卷曲。
现在,您可以将复制的内容粘贴到命令行中并运行它。您将得到一些很长的 JSON,可以通过解析找到您想要的数据。这是查找您可能感兴趣的数据的有用工具。
把 JSON 放在一个容易看到的地方可能会对你有用,你可以试着把这些都复制并粘贴到像 jsonformatter 或【jsonprettyprint.net】T4 这样的网站上,它们可以帮助你直观地解析数据。
将 cURL 转换为 python
即使你很擅长使用 cURL,阅读起来也会很棘手,很容易出错。有很多在线资源可以帮助你使用 cURL 并用你选择的编程语言重写它,比如trillworks.com,但是我建议你使用 Postman 。Postman 是一个很棒的免费应用程序,可以帮助你使用(和构建)API。由于 API 在处理数据中起着如此重要的作用,如果你真的想从事数据方面的职业,这是一个很好的学习工具。
下面,我画了 cURL 到 python 的转换,这是我选择的语言,Postman 已经帮我处理好了。
使用硒
Selenium 是一个流行的框架,用于在浏览器中运行自动化测试,实际上它可以用来抓取数据。从 Selenium Desired Capabilities 实现中,您可以提取您编写的自动化软件所访问的站点的请求。
希望这能让你看到一种更有效的收集数据的方法!
在 R 中抓取、下载和存储 pdf
网页抓取 pdf
嵌套擦伤,以避免按钮点击
来源:https://rstudio.com/resources/cheatsheets/
“汇流”一词的意思是事物的汇合或融合,通常指河流。我在西弗吉尼亚州长大,靠近 Gauley 河(为你椽子准备的世界级白水)和 Meadow 河(小嘴鲈鱼的天堂)。汇流这个词也很恰当地描述了我作品中的思想潮流是如何汇聚的。我的工作融合了统计学、数据科学和公共政策。如果是这些河流,数据科学无疑是最小的。虽然现在称自己为“数据科学家”很时髦,但我不是数据科学家。然而,我确实使用了数据科学家的许多工具、技术和流程。
我研究公共政策,因此,我没有形式和度量一致的数据(例如,对你们经济学家来说是美元和美分)。因此,有一天我正在使用机器学习技术对文本进行分类,第二天我正在为这些分类建立测量模型,之后我可能会为与数据相关的一些方面建立一个极端事件模型。所有这些意味着我做了很多数据科学家,特别是那些 Tidyverse 说服者,称之为“数据争论”的事情——清理、组织和转换数据以供分析。
我经常忘记需要处理数据的各种命令。RStudio 为数据科学和统计的各种包和过程提供了一套很好的备忘单。它们以 pdf 海报的形式出现,就像你在会议上看到的那样。我的问题是,我经常在西弗吉尼亚州的旅馆工作,那里的互联网连接是一个挑战,背景是高利河峡谷的轰鸣声。这意味着我经常搜索和下载我认为可能需要的备忘单。因为备忘单会不时更新,所以我冒着在本地存储过期备忘单的风险。这一切促使我考虑编写一个简短的脚本来访问所有 RStudio 备忘单并将其下载到本地。关于代码…
加载有用的库
至少对于那些使用 r 的人来说,tidyverse
是这些类型操作的主力,特别是,rvest
是读取和解析 HTML 的主要工具。最后,stringr
提供了一致的处理文本字符串的方法——在抓取 URL 列表时非常有用。
library(tidyverse)
library(rvest)
library(stringr)
抓取链接和下载文件
下面的代码行通过识别和读取包含 RStudio Cheatsheets 的 pdf 的 Github 存储库的 HTML 来开始抓取。
page <- read_html("https://www.github.com/rstudio/cheatsheets")
如果您访问该存储库,您会发现单击表格中的一个 pdf 文件并不会单独生成相关的备忘单。它将我们带到存储库的第二级页面,并在代码编辑器中打开文件。获取文件以便在工作中引用意味着单击代码编辑器顶部菜单栏上的下载按钮。当然,没有什么刮擦练习是真正简单的。要获取文件,我们需要找到一种绕过“点击”下载按钮的方法。明确地说,一些脚本允许我们“点击”按钮,但是我在jsonlite
中找到的那些比我在这里做的更复杂(虽然更有效)。
raw_list <- page %>% *# takes the page above for which we've read the html*
html_nodes("a") %>% *# find all links in the page* html_attr("href") %>% *# get the url for these links* str_subset("\\.pdf") %>% *# find those that end in pdf only* str_c("https://www.github.com", .) %>% *# prepend the website to the url* **map**(read_html) %>% *# take previously generated list of urls and read them*
**map**(html_node, "#raw-url") %>% *# parse out the 'raw' url - the link for the download button*
**map**(html_attr, "href") %>% *# return the set of raw urls for the download buttons*
str_c("https://www.github.com", .) %>% *# prepend the website again to get a full url*
walk2(., basename(.), download.file, mode = "wb") *# use purrr to download the pdf associated with each url to the current working directory*
我利用存储库主页上的链接来访问、记录和获取与每个备忘单的下载按钮相关联的链接列表。这种解决方法不需要为每个按钮编写一个“点击”按钮的脚本。这个脚本不是最高效的,但是您最终会在本地工作目录中找到所有优秀的 RStudio 备忘单。
前置很重要,因为每个对read_html
的调用都需要完整的 URL。反过来,html_nodes
和html_attr
中的解析依赖于已经读取了 HTML。来自stringr
的字符串折叠函数str_c
使这变得容易。对于解析,我们使用 Tidyverse 包purrr
中的map
函数。
重要花絮
- 在最初的抓取中,
str_subset("\\.pdf")
告诉 R 返回所有带有 pdf 的链接。否则,您将获得整个存储库的链接,包括开发文件。 map(html_node, "#raw-url")
告诉 R 查找与每个备忘单的下载按钮相关联的 URL。您可以通过使用 Google 的选择器小工具来识别这个标签——在上面搜索例子以及如何识别标签。purrr::walk2
将download.file
函数应用于每个生成的原始链接。的“.”告诉它使用前一个命令中的数据(原始 URL)。basename(.)
告诉它给下载的文件一个 URL 的基本名称(例如,“purrr.pdf”)。- 根据您的 pdf 阅读器,您可能需要添加
mode = "wb"
。否则,文件可能会显示为空白、损坏或无法正确呈现。更多信息参见download.file()
命令的文档。
老派
请注意,Tidyverse 的walk2
和许多其他伟大的功能本身并不必要。下面的 for 循环是上面的walk2
的老式实现。这里,download.file
应用于raw_list
中的每个 URL(URL 在这里只是对象raw_list
中每一行的标签)。
for (url in raw_list){ download.file(url, destfile = basename(url), mode = "wb") }
包扎
因此,对于需要“点击按钮”的地方来说,这不是最有效的代码,但是它用最少的包和对json
或其他语言的了解就完成了工作。现在我们已经完成了,我还可以说在 RStudio 的资源页面上可以找到备忘单。在那里,它们会更容易抓取,当然你可以点击下载。我经常向学生提及它们,因为它们是数据分析和统计教学的重要资源。
原载于 2020 年 4 月 10 日 https://www.samuelworkman.org。
从维基百科上搜集信息
如何自动从多页中抓取
莎伦·麦卡琴在 Unsplash 上的照片
上周,我写了一篇关于如何从维基百科的一个表格中收集数据的文章(这里有一个链接让你了解)。在这篇文章中,我从这一页的上的一个表格中搜集了数据,上面有参赛者的姓名、年龄、职业,以及他们来自英国烘焙大赛第一季的地方。最终的结果是下面的字典:
{'Annetha Mills': [30, '/wiki/Essex'],
'David Chambers': [31, '/wiki/Milton_Keynes'],
'Edward "Edd" Kimber': [24, '/wiki/Bradford'],
'Jasminder Randhawa': [45, '/wiki/Birmingham'],
'Jonathan Shepherd': [25, '/wiki/St_Albans'],
'Lea Harris': [51, '/wiki/Midlothian'],
'Louise Brimelow': [44, '/wiki/Manchester'],
'Mark Whithers': [48, '/wiki/Wales'],
'Miranda Gore Browne': [37, '/wiki/Midhurst'],
'Ruth Clemens': [31, '/wiki/Poynton,_Cheshire']}
如您所见,参赛者的全名是关键字,值是包含他们年龄的列表和包含他们家乡的维基百科页面 url 的 url 片段。现在,我可以很容易地得到这个城镇的名字,然后就到此为止。然而,如果我打算在收集的数据上运行某种模型,那就没什么用了。当您收集用于模型的数据时,可以收集的实际数字数据越多越好。因此,与其只收集一个地方的名称,不如收集一些关于这个地方的统计数据,比如人口或密度,会更有用。
当然,这些信息不会直接出现在 GBBO 的维基上,因为那与那个特定的页面无关,这也是为什么他们会链接到这个城镇的页面上。因此,这意味着如果我们想收集这些信息,我们必须自己去每一页搜集这些信息。你可以浏览每一个城镇页面,然后手动操作,但这很快就会变得乏味。这就是为什么我开发了一个脚本,可以自动进入每个页面并抓取信息。这样,实际的执行速度会更快,效率也会更高。
首先,下面是我编写的收集城镇人口和密度的完整函数:
现在我们要回到那里,解释这里到底发生了什么。
第二行我有下面的字典:
stats_dict = {'density':None, 'pop':None}
如果你看到最后,列表中返回的第二项是 stats_dict。通过使用空值字典,我们可以确保在页面上出现异常的情况下,我们仍然可以得到一些信息,并且可以从那里对有问题的单个项目进行故障诊断。
第四行如下:
if type(url_snippet) == str:
这确保了被传递的值是一个字符串,因为如果不是,函数的其余部分显然不能工作。第 5–8 行如下
area_url = 'https://en.wikipedia.org{}'.format(url_snippet) area_page = requests.get(area_url)
area_soup = BeautifulSoup(area_page.content)
它们创建完整的 url,请求 url,并制作我们将用于抓取信息的汤。
下一行是 for 循环,所有人口和密度的发现都在它下面
for item in area_soup.find_all('th'):
这实质上是说查看所有标记为“th”的项目,这是人口和密度信息通常放在下面的地方。第 11–17 行是关于查找密度信息,第 19–26 行是关于查找人口信息。以下是我如何找到密度信息的解释:
第 11 行和第 12 行如下:
if 'Density' in item.text:
if item.find_next_sibling().text:
这是在最初的 for 循环之后,查找所有标记为“th”的项目,所以这表示如果标记为标题的内容的文本是“density”,这意味着查找标记为 Density 的标题。下一行是说如果标记为 density 的东西的兄弟有文本,那么执行以下操作。
注意:通过这个过程,你会注意到有很多 if 语句。这是因为不同页面的格式偶尔会有细微的差异。对于页面浏览者来说,这并没有真正减少他们的体验。然而,当你处理一个脚本时,一个稍微不同的安排对你来说可能意味着一个错误。所以,你要做很多,“如果这个东西存在,就这么做”而不是只告诉函数做某件事。
以下几行是:
density_str = item.find_next_sibling().text
for elem in density_str.split():
if "/km" in elem:
回到上一个 if 语句,这里的第一行是说,“现在我们知道这个东西存在,这个东西就是包含密度信息的字符串。”接下来的两行将文本分割成所有单独的部分,然后取包含每公里的人的信息的部分,因为我们希望确保我们的单位与我们得到的数字一致。下面两行实际上提取了数据(第一行溢出了一点):
d = int([item for item in re.split("(\d*,?\d*)",elem)
if len(item)>1][0].replace(',','')) stats_dict['density'] = d
第一行是实际访问数字的内容。简而言之,它使用 regex 来拆分文本,如果该项可以拆分,则获取列表中的第一项,即数字本身,然后将文本中的逗号替换为空,然后将最后一个数字从字符串转换为整数。下一行返回到最初为空的字典,并使用前一行中收集的数字更新密度条目。
瞧,我们有了我们的密度!现在来看人口,我发现这个问题有点棘手,因为格式的变化比密度的变化要多一些。
if 'Population' in item.text:
像密度一样,我们开始寻找被标记为标题、文本为“人口”的项目然而,在这之后,我们开始偏离获得城镇密度的道路。在我们进入代码本身之前,我想确切地说明为什么它看起来比您最初认为的要复杂一些:
这是安娜莎·米尔斯的家乡埃塞克斯的维基百科页面:
https://en.wikipedia.org/wiki/Essex
在这里,我们基本上看到了我们在密度中看到的,我们正在搜索的数字在兄弟姐妹的文本中。然而,这并不是它一贯的格式。如果我们看看布拉德福德,爱德华“艾德”金伯的家乡:
https://en.wikipedia.org/wiki/Bradford
母体的数目在父代的兄弟文本中。因此,我们的下一行有点不同:
if item.find_next_sibling() is None:
stats_dict['pop'] = re.split('(\d*,\d*)',item.parent.find_next_sibling().text)[1]
实质上,如果该项没有兄弟,就找到父项,找到父项的兄弟,然后从父项的兄弟中获取文本,将其拆分,并找到编号。这个数字现在是 stats_dict 中的 population(pop)条目的值。那么下一个条件如下:
elif item.find_next_sibling().text.startswith('%'):
pass
在这种情况下,如果该项目有一个兄弟,但兄弟的文本以百分号开头,只需忽略它,因为它不会有您要寻找的信息。最后,如果该项有一个同级,并且该同级的文本不以百分号开头,则我们得到以下内容:
else:
pop_str = item.find_next_sibling().text
stats_dict['pop']= int(pop_str.split()[0].replace(',',''))
这远没有以前复杂。基本上,获取该项的兄弟,获取它的文本(pop_str),拆分该文本,获取拆分列表中的第一项(包含数字的项),去掉数字中的逗号,然后将其转换为整数。现在在 stats_dict 中被指定为“pop”值。我们终于有了回报声明:
return [url_snippet.split('/')[-1], stats_dict]
这将返回一个列表,其中第一项只是初始 url 片段中的地名。我在事件密度中保留这个名称,或者弹出一个 None 值。如果真的发生这种情况,我可以自己去看页面,然后手动抓取。列表中的第二项是包含密度和人口信息的 stats_dict。
现在我们已经有了一个函数,是时候使用它了。
我上一篇文章的最终产品,即本文开头所示的词典,将被命名为“参赛者姓名年龄城镇”
前三行表示对于字典值中的每个列表,城镇是该列表中的最后一项,现在应该使用 area_stats 函数扩展该列表,该函数将返回一个关于该地点的地名和统计信息的列表。最后,现在我们有了所有的区域统计数据,我们可以取出 url 片段。所有这些让这本字典:
{'Annetha Mills': [30, '/wiki/Essex'],
'David Chambers': [31, '/wiki/Milton_Keynes'],
'Edward "Edd" Kimber': [24, '/wiki/Bradford'],
'Jasminder Randhawa': [45, '/wiki/Birmingham'],
'Jonathan Shepherd': [25, '/wiki/St_Albans'],
'Lea Harris': [51, '/wiki/Midlothian'],
'Louise Brimelow': [44, '/wiki/Manchester'],
'Mark Whithers': [48, '/wiki/Wales'],
'Miranda Gore Browne': [37, '/wiki/Midhurst'],
'Ruth Clemens': [31, '/wiki/Poynton,_Cheshire']}
到这本字典里:
{'Annetha Mills': [30, 'Essex', {'density': 427, 'pop': 1477764}],
'David Chambers': [31, 'Milton_Keynes', {'density': 2584, 'pop': 229941}],
'Edward "Edd" Kimber': [24, 'Bradford', {'density': 1290, 'pop': '539,776'}],
'Jasminder Randhawa': [45, 'Birmingham', {'density': 4262, 'pop': '1,141'}],
'Jonathan Shepherd': [25, 'St_Albans', {'density': 8140, 'pop': 147373}],
'Lea Harris': [51, 'Midlothian', {'density': 260, 'pop': '91,340'}],
'Louise Brimelow': [44, 'Manchester', {'density': 4735, 'pop': '552,858'}],
'Mark Whithers': [48, 'Wales', {'density': 148, 'pop': '278,000'}],
'Miranda Gore Browne': [37, 'Midhurst', {'density': 1467, 'pop': 4914}],
'Ruth Clemens': [31, 'Poynton,_Cheshire', {'density': 1089, 'pop': 14260}]}
现在,我们实际上有了一些有用的区域统计数据,我们不必逐个梳理每个页面来查找这些信息!
用 Python 抓取谷歌地图评论
使用 BeautifulSoup 和 Selenium 抓取最新评论
在这篇文章中,我想和你分享一些关于如何使用 Python Selenium 和 BeautifulSoup 库来应用数据抓取的知识:这两个工具以正确的方式结合起来允许定义一组 API 来从几乎任何网站收集数据。
**注意:**从网站收集的任何数据都可能受到版权保护,这意味着未经所有者同意不得重复使用,并且不得明确用于商业目的。
本文的主要目标是展示如何作为编码练习收集数据,以及如何为研究和/或个人项目构建数据集。
在这个简短的免责声明之后,我们可以开始了吗?
先从工具说起。
蟒蛇
Anaconda 是一个框架,它使用虚拟环境的概念,以透明的方式帮助维护每个项目的 Python 库依赖关系:您创建一个环境,在其中安装每个库,并根据您的需要激活/停用该环境,而不会干扰其他项目的依赖关系。你可以在这里下载。
这个工具不是强制性的,它可以由任何虚拟环境库(例如:virtualenv)代替,但如果您想在这里介绍的管道末端添加进一步的步骤,如数据清理、数据分析和机器学习,它可能是有用的。此外,您也可以通过 pip 安装所描述的库。
无论如何,要创建我们的抓取环境,请运行以下代码:
conda create --name scraping python=3.6
conda activate scraping
硒
Selenium 是一个为 web 应用程序自动测试而构建的框架:它的 API 允许我们模拟点击、滚动和网站上发生的任何其他交互。因此,它对于抓取网站也非常有用:点击和滚动会触发页面的变化,从而加载更多的数据(或其他类型的数据)。
该库是用 Java、Python、C#、JavaScript 和许多其他语言编写的:在本文中,我们将使用 Python 实现来加载目标 web 页面,并生成检索附加信息所需的所有交互。
要安装 Selenium,请运行命令:
conda install -c conda-forge selenium
我们还需要安装我们想要使用的浏览器的 web 驱动程序。webdriver 是自动运行浏览器实例的软件,Selenium 将在其上工作。
我决定使用的谷歌 Chromedriver ,你可以从这里下载,但是任何驱动都可以正常工作。
**注意:**我们将在后面看到的手动测试必须使用我们在这一步选择的浏览器来运行。
美丽的声音
BeautifulSoup 是一个解析 HTML 和 XML 文件的原生 Python 库:它帮助导航树的节点,以非常直观的方式访问属性和特性。
对我们来说,主要用途是解析经过 Selenium 处理的 HTML 页面,将信息提取为原始文本,并将其发送给进一步的处理。
要在我们的 conda 环境中安装库,运行命令:
conda install -c anaconda beautifulsoup4
好了,现在我们应该准备好开始定义我们的抓取模块了!
目标示例将显示如何收集最新的 Google Maps 评论:我们将定义一个导航到特定兴趣点(从现在开始是 POI)并检索其相关最新评论的 scraper。
1。初始化
作为第一步,我们需要初始化我们的网络驱动。该驱动程序可以配置多个选项,但目前我们仅将英语设置为浏览器语言:
options = Options()
options.add_argument("--lang=en")
driver = webdriver.Chrome(chrome_options=options)
2.URL 输入
然后,我们需要提供我们的目标页面:当我们想要抓取 Google Maps 评论时,我们选择一个 POI 并获得直接指向评论的 url。在这一步中,驱动程序只需打开页面。
总经理评估的目标页面示例
这种类型的 URL 相当复杂:我们需要手动将其从浏览器复制到一个变量中,并将其传递给驱动程序。
url = [https://www.google.it/maps/place/Pantheon/@41.8986108,12.4746842,17z/data=!3m1!4b1!4m7!3m6!1s0x132f604f678640a9:0xcad165fa2036ce2c!8m2!3d41.8986108!4d12.4768729!9m1!1b1](https://www.google.it/maps/place/Pantheon/@41.8986108,12.4746842,17z/data=!3m1!4b1!4m7!3m6!1s0x132f604f678640a9:0xcad165fa2036ce2c!8m2!3d41.8986108!4d12.4768729!9m1!1b1)driver.get(url)
3.单击菜单按钮
现在,我们要提取的 最新的评论,而页面默认设置呈现最相关的评论。我们需要点击“排序”菜单,然后点击“最新”标签。
这就是 Selenium(和编码技能)真正发挥作用的地方:我们需要找到按钮,单击它,然后单击第二个按钮。为此,我使用了 Selenium 提供的 XPath 搜索方法:这比 CSS 搜索更容易,这也要感谢 Chropath ,这是一个浏览器扩展,它将 XPath 解释器添加到浏览器开发工具中。
这样,我们检查页面以测试表达式,直到我们突出显示所需的元素:
第一个按钮的 XPath 表达式测试
第二个按钮的 XPath 表达式测试
不幸的是,这还不够。Google Maps 网站(和许多其他现代网站一样)主要是使用 AJAX 实现的:网站的许多部分是异步加载的**,这意味着如果 Selenium 在加载页面后立即查找按钮,它们可能不会被加载。**
但是在那种情况下我们也有解决办法。Selenium 实现了等待功能:在某些条件被验证之后或者在最大超时时间过去之后执行点击。等到元素在页面上出现并可点击,就解决了前面的问题。
该部分的代码是:
wait = WebDriverWait(driver, 10)
menu_bt = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//button[[@data](http://twitter.com/data)-value=\'Sort\']'))
)
menu_bt.click()
recent_rating_bt = driver.find_elements_by_xpath(
'//div[[@role](http://twitter.com/role)=\'menuitem\']')[1]
recent_rating_bt.click()
time.sleep(5)
Sleep 函数被添加到这个块的末尾,因为点击触发了一个 AJAX 调用来重新加载评论,因此我们需要在进入下一步之前等待…
4.审查数据提取
现在,我们终于达到了目标,评论数据。我们将页面发送到 BeautifulSoup 解析器,它帮助找到正确的 HTML 标签、div 和属性。
首先,我们确定了 review 的包装器 div:find _ all 方法创建了一个 div 元素列表,这些元素具有特定的属性。在我们的例子中,列表包含页面上的评论的 div。
response = BeautifulSoup(driver.page_source, 'html.parser')
rlist = response.find_all('div', class_='section-review-content')
对于每个评论,我们解析它的信息:评级星、文本内容(如果有的话)、评论日期、评论者姓名、评论 id。
id_r = r.find('button',
class_='section-review-action-menu')['data-review-id']
username = r.find('div',
class_='section-review-title').find('span').texttry:
review_text = r.find('span', class_='section-review-text').text
except Exception:
review_text = Nonerating = r.find('span', class_='section-review-stars')['aria-label']
rel_date = r.find('span', class_='section-review-publish-date').text
5.卷动
最后但同样重要的是:该页面已经加载了 20 篇评论,但是如果不向下滚动页面,其他评论就不可用。
Selenium API 中没有直接实现滚动,但是它们允许执行 JavaScript 代码并将其应用于页面的特定元素:我们标识可以滚动的 div 对象,并运行简单的 JS 代码行来滚动到页面底部。
scrollable_div = driver.find_element_by_css_selector(
'div.section-layout.section-scrollbox.scrollable-y.scrollable-show'
)
driver.execute_script(
'arguments[0].scrollTop = arguments[0].scrollHeight',
scrollable_div
)
正如你所看到的,在这个简短的教程中,我试图解释从谷歌地图上抓取评论的核心要素,但同样的概念也适用于许多现代网站。复杂性很高,我在这里没有考虑所有的细节:如果你想有一个完整的例子,检查我在 Github 这里的知识库。
感谢您的阅读。让我知道你对它的想法,如果你有任何问题!
用 Python 抓取交互式折线图
带着硒穿过生产线
【www.freepik.com free pik 制作的 T2 商业照片
假设你想知道今天美元对欧元的表现。快速的谷歌搜索就能搞定,只需输入:“美元欧元”。你会看到这样一个交互式图表:
为了查看整个周期的值,您需要沿线移动光标,同时包含值和日期的标签会立即更新。
现在假设您想从这个图表中获取所有数据,并为自己创建一个数据库。如果标签中的值一直变化,如何从整行中刮出数据?既然你能看见它,你能刮它,就一定有办法。
如果你要手动完成,你要做的是沿着这条线移动光标,同时在 Excel 表格中输入每个值,或者甚至在笔记本上手写。在这篇文章中,我们将看到如何让 selenium 做同样的事情。我们将让它像人一样工作,遍历行并记录值。
虽然我们使用的是 Google 的图表,但是这里实现的逻辑可以适用于任何类似的图表。
我们基本上使用的是硒、熊猫、和日期时间模块。好了,在我们导入所有我们要用的东西后,让我们直接打开连接并抓取页面。
然后我们实例化 action 对象,我们将使用它来移动屏幕上的光标,以及 search_bar 和 search_button 对象,我们将使用它们来执行我们的搜索。完成后,只需使用 send_keys 方法编写搜索,并使用 click 方法点击搜索按钮。我们将使用 WebDriverWait ,以便它等待元素被完全加载到页面中:
现在让我们找到图表元素,并获取它在页面上的大小和位置。这很重要,这样我们就知道将光标移动到哪里。为此,我们还将使用 WebDriverWait 。
运行我们到目前为止所做的一切,这样您就可以看到以下输出:
{'x': 514, 'y': 262}
{'height': 134, 'width': 301}
现在我们有了这个信息,我们可以用它来移动光标到对象的最右边,使用move _ to _ element _ with _ offset。此方法将鼠标移动指定元素的偏移量。偏移量是相对于元素的左上角的,所以我们需要将对象的整个长度作为 xoffset 来传递。由于不需要在 y 轴上移动,yooffset可以设置为零:
如果我们使用 move_to_element 来代替,光标将被移动到元素的中心。
现在我们的光标在这里:
请注意图表最右边的光标。
然后我们得到第一个日期值对:
我们现在只需要光标继续向左移动。为此,我们需要设置:
- 光标移动的速度;
- 光标移动的截止日期。
我们将使用动作对象的 move_by_offset 方法将光标从其最后一个已知位置移动到下一个位置。无限循环将使光标移动到我们设置的截止日期。当日期超过极限时,我们称之为突破。
这是我们预期的行为:
很酷吧。!每次光标向左移动,它都会从标签中抓取一个新的值和日期。就像人类移动鼠标做笔记一样!
如果你在谷歌的一个图表中这样做,并且你改变了图表的时间范围,比如说,到年,一些日期可能会被跳过。这些日期即使手动也无法访问。可能是因为 x 轴对于这么小的图表来说变得太大了。
此外,重要的是要明确节奏是由反复试验决定的。如果速度太小,光标将需要很长时间才能移动到图表中的下一个日期。如果太大,它可能会跳过日期。
然而,不可避免的是,有时光标仍然会在同一个日期,即使它刚刚移动。因此,最好将数据存储在字典中,并用它来丢弃重复的数据。当我们完成抓取时,我们可以将这个字典转换成一个数据帧。这是完整的代码:
这是我们刚刚收集的数据:
最后一个警告:如果你正在观察光标在这条线上的移动,你会注意到它在每一次循环中都回到了起点。即使 selenium 保持最后的位置,也会发生这种情况,但是您也可以注意到,光标在每个新循环中以我们建立的速度继续向左移动。所以,一切都按计划进行。
嗯,就是这样。我希望你喜欢它,这可能是有用的。如果你有问题,有建议,或者只是想保持联系,请随时通过 Twitter 、 GitHub 或 Linkedin 联系。
用 3 行代码一步一步地抓取实时交通数据
了解如何用最少的代码在 python 中 web 抓取实时更新的图表。
来源:TomTom。
鉴于最近的疫情,决策者实施的封锁措施已经停止了大部分经济活动。由于经济数据的滞后性,在数据中衡量消费者的实时行为并不总是容易的。事实上,数据往往至少有一个月的时滞,因此很难评估人们是否真的在四处走动。因此,政府、科学家和经济学家等一直在关注高频数据,这些数据提供了自限制实施以来全球经济运动如何变化的实时视图。TomTom 提供的流量数据是衡量活动进展的一个流行指标。该数据涵盖 57 个国家的 416 个城市,并计算整个道路网络上所有车辆的行驶时间。在 TomTom 的网站上,平均拥堵程度以实时交通图的形式显示。我选择了米兰:
米兰 TomTom 交通指数。来源:汤姆汤姆。
我们可以将鼠标悬停在图表上以获得每个单独的数据点,但无法从网站上下载数据。所以有两个选择:
- 花 个小时悬停在每个点上,记录每个数值…
- 刮吧!
检查数据
在任何网站上,网站在加载页面时都会加载一系列不同的信息,这是很常见的。这可能包括 cookies、数据文件、图像等等。我们可以通过检查页面来了解内幕。在 Chrome 中,按下’ **Ctrl+shift+I '就可以轻松做到这一点。**将出现一个窗口,显示已加载到页面中的所有数据。我们想要的选项卡是弹出窗口顶部显示的网络选项卡。
检查 TomTom 米兰交通网页。
如果你看不到所有的数据,点击’ Ctrl+R’ ,重新加载页面,观察所有数据的加载。在名称选项卡上,你会看到许多不同的文件类型:。js,。json,。svg,。otf 等等。那么我们如何知道哪一个包含我们图表的底层数据呢?首先,我们需要通过 XHR 进行过滤。这是 XML HTTP 请求的简称。使用这种格式载入网页上的数据是很常见的。然后我们可以按大小过滤**——底层数据通常是页面中包含的最大文件之一(可能以千字节而不是字节为单位)。最后,通常情况下,源数据会以的形式出现。json 文件。这代表了 Javascript 对象符号,这是存储大量数据的常用方式。**
然而,查看我们的数据集,并不能立即看出 json 文件在哪里。下一步,让我们看看型柱。因为我们已经通过 xhr 进行了过滤,所以我们应该在这个列中只看到 xhr 类型。说到这里,我们顺着栏目往下看,可以看到既 xhr 又 fetch 。获取类型意味着该数据从 API 或自动处理接口加载。用获取类型查看数据,这个特定对象的文件名是*“ITA _ Milan”。* **看来我们已经得到了我们的数据!**我们可以通过在新标签中打开它来检查它是否正确。当我们打开新选项卡时,我们看到这确实是我们想要的数据,在链接下面:【https://api.midway.tomtom.com/ranking/live/ITA_milan】*,*这是从 API *中检索的。*太好了——现在让我们将这些数据加载到 python 中,并开始清理它们以复制实时图表。
检索和清理数据
首先,请求是我们将用来加载数据的模块。在我们之前找到的 url 上使用 requests.get(),我们可以将该文件显示为一个 json ,并使用 python 中的 json 模块对其进行解包(是的,只使用了三行代码):
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import json
import requests# retrieve json file
url_ = "[https://api.midway.tomtom.com/ranking/live/ITA_milan](https://api.midway.tomtom.com/ranking/live/ITA_milan)"
italy_req = requests.get(url)
italy_json = italy_req.json()
这为我们提供了以下输出:
{'data': [{'JamsDelay': 13.5,'TrafficIndexLive': 6,'TrafficIndexHistoric': 17,'UpdateTime': 1586608290000,'JamsLength': 1.4,'JamsCount': 4},{'JamsDelay': 4.2,'TrafficIndexLive': 6,'TrafficIndexHistoric': 17,'UpdateTime': 1586609190000,'JamsLength': 0.4,'JamsCount': 2},{'JamsDelay': 5.8,'TrafficIndexLive': 6,'TrafficIndexHistoric': 18,'UpdateTime': 1586610090000,'JamsLength': 0.4,'JamsCount': 2},
这个文件可能看起来有点混乱,有很多不同的标点和括号。它比看起来简单——JSON文件实际上是一个名为*‘数据’*的字典,其中包含一个字典列表。所以为了得到我们想要的信息。(“Time”和“TrafficIndexLive”),我们要调用每个想要检索的点的键。例如,要获得实时流量指数的第一个值,我们需要输入以下内容:
italy_json["data"][0]["TrafficIndexLive"]
然后,我们可以将字典中的每个条目添加到一个列表中,将每个列表转换为熊猫数据帧中的一列:
实时交通数据帧。
使用字典和一点点数据清理,我们已经做到了!让我们通过绘制图表来验证数据是否与实时图表相匹配:
米兰实时交通。
我们做到了!在几行代码中,我们能够从一个图表中检索数据,否则我们无法访问这些数据。原来他们说的是真的:看得见就刮!
感谢阅读! 如果您有任何见解,请随时发表评论。包含我用来做这个项目的源代码的完整 Jupyter 笔记本可以在我的 Github 资源库中找到。
参考资料:
[1] TomTom,2020。《米兰交通报道*》*。可在:https://www.tomtom.com/en_gb/traffic-index/milan-traffic/
免责声明:本文表达的所有观点均为本人观点,与 和 先锋或任何其他金融实体没有任何关联。我不是一个交易者,也没有用本文中的方法赚钱。这不是财务建议。
抓取亚马逊商店以生成价格警报
只需几行 Python 代码,您就可以构建自己的 web 抓取工具来监控多个商店,这样您就不会错过很多东西了!
对那些亚马逊交易保持警惕!(照片由加里·本迪格拍摄)
我怎么会在这里?
我认为有一点是公平的,我们都有一个来自亚马逊的书签产品页面,我们疯狂地刷新它,希望价格能下降。
好吧,也许不是疯狂地,但肯定是一天几次。
我将向您展示如何编写一个简单的 Python 脚本,它可以从他们的任何商店抓取亚马逊产品页面,并检查价格,等等。我努力使它保持简单,如果你已经知道基本的东西,你应该能够顺利地跟上。以下是我们将在这个项目中做的事情:
- 创建一个 csv 文件,其中包含我们想要的产品的链接,以及我们愿意购买的价格
- 用 Beautiful Soup 编写一个函数,该函数将循环遍历 csv 文件中的链接并检索关于它们的信息
- 将所有东西存储在一个“数据库”中,并随时跟踪产品价格,这样我们就可以看到历史趋势
- 安排脚本在一天中的特定时间运行
- Extra —当价格低于你的限额时,创建电子邮件提醒
我还在编写一个脚本,它采用搜索词而不是产品链接,并返回所有商店中最相关的产品。如果你觉得这可能有用,请在评论中告诉我,我会写另一篇关于它的文章!
更新:我刚刚制作了一个全程视频!检查一下,让我知道你节省了多少:)
永恒的愿望清单…
将你的爱好扩展到更专业的领域,最悲哀的事情通常是需要购买更好的设备。以摄影为例。购买一台新相机是一个可以显著影响你睡眠时间的决定。众所周知,购买新镜头也会产生类似的效果——至少我是这么认为的!不仅技术方面非常相关,需要一些研究,而且你也希望得到尽可能好的价格。
研究完成后,就该在我们知道的每一个网店里搜索选中的型号了。
将你知道的商店数量乘以选择的型号数量,你就会得到我的浏览器上打开的标签的大概数量。大多数情况下,我最终会再次访问亚马逊……就我而言,因为我在欧洲,这通常涉及到搜索意大利、法国、英国、西班牙和德国等国家的亚马逊商店。我发现同样的产品有非常不同的价格,但更重要的是,每个市场都有特定的偶尔交易。我们不想错过这个机会…
这就是了解一点 Python 可以为您省钱的地方。字面意思!
我开始测试一些网页抓取来帮助我自动完成这项任务,结果发现每个商店的 HTML 结构都差不多。这样我们就可以在所有的中使用我们的脚本来快速获得所有商店的所有价格。
如果你想访问我的其他网络抓取文章——以及媒体上的几乎所有内容——你考虑过订阅吗?你会大力支持我的工作!
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@fneves/membership)
入门指南
我以前写过几篇关于网络抓取的文章,解释了漂亮的汤是如何工作的。这个 python 包非常容易使用,你可以查看 我写的这篇文章 是为了从房屋列表中收集价格。简而言之,Beautiful Soup 是一个工具,您可以使用它从 HTML 页面中访问特定的标签。即使你以前没有听说过它,我打赌你一看到代码就能明白它在做什么。
您需要一个名为 TRACKER_PRODUCTS.csv 的文件,其中包含您想要跟踪的产品的链接。本 Github 库中提供了一个模板。每次运行后,scraper 将结果保存在一个名为“search _ history _[随便什么日期]”的不同文件中。xlsx”。这些是 search_history 文件夹中的文件。您也可以在存储库中找到它。
这是我们需要的文件夹和文件结构
由于我不想让事情变得过于复杂,我将首先使用 Jupyter 笔记本向您展示代码输出。但是最后,我们会给这段代码一些类固醇,并在一个 Amazon_Scraper.py 文件中把它变成一个不错的函数。最重要的是,我们将创建一个计划任务来不时地运行它!
我们需要的就是汤
让我们从笔记本视图开始。在你的 Jupyter 笔记本上使用下面的代码片段,这样你就可以看到代码的每一部分是做什么的。
这里最不为人知的包大概就是 glob 了。这是一个很好的包,它允许你在一个文件夹中获得文件名列表之类的东西。请求将负责获取我们在跟踪列表中设置的 URL。美丽的汤是我们这次挑战的网络抓取工具。如果你需要安装它们中的任何一个,简单的 pip/conda 安装就可以了。有很多资源可以帮助你,但通常情况下, Python 包索引页面会有。
需要 HEADERS 变量来传递 get 方法。我在上面提到的其他文章中也讨论了它,但是当使用请求包访问 URL 时,您可以将它视为您的身份证。它告诉服务器你正在使用哪种浏览器。你可以在这里阅读更多关于这个的内容。
现在可以从 存储库 中获取名为 TRACKER_PRODUCTS.csv 的 csv 文件,并将其放在文件夹“trackers”中。
然后我们把它放进美味的汤里。这将使 HTML 变得更加“容易理解”,(巧妙地)命名为 汤 。
新鲜的王子回归让你开心!
应该挑哪些食材?
我们从简单的东西开始,比如产品的名称。虽然这几乎是多余的,但是一旦我们检查了 excel 文件,给出一些关于产品的细节是一件好事。
我添加了其他字段,如评论分数/计数和可用性,不是因为它们与购买决策相关,而是因为我对这些东西有点强迫症,我更喜欢有“有一天会有用”的变量……看到特定产品的评论分数的变化可能会很有趣。也许不会,但无论如何我们会保留它!
每当你看到 soup.find 的时候,就意味着我们正在试图使用页面的 HTML 标签(比如 div,或者 span 等等)来寻找页面的元素。)和/或属性(名称、id、类别等。).与 soup.select 一样,我们使用的是 CSS 选择器。你可以在浏览器上使用 Inspect 功能并导航页面代码,但我最近发现了一个非常方便的 Chrome 扩展,名为 SelectorGadget ,我强烈推荐它。这使得找到正确的代码更加容易。
在这一点上,如果你觉得很难理解选择器是如何工作的,我鼓励你阅读这篇 文章 ,在那里我会更详细地解释一下。
下面你可以看到每个部分返回的内容。如果您刚开始使用 web 抓取,但仍然不完全理解它是如何工作的,我建议您将代码分成几部分,慢慢弄清楚每个变量在做什么。
我总是在网络抓取文章中写这个免责声明,但是如果你几个月后阅读这篇文章,这里的代码可能不再正确工作了——例如,如果亚马逊改变页面的 HTML 结构,这可能会发生。
如果发生这种情况,我会鼓励你去解决它!一旦你分解代码,使用我的例子这真的没什么大不了的。只需要修复你的代码正在获取的选择器/标签。你可以在下面留言,我会尽力帮你。
(反)构汤
获得我们产品的名称应该是小菜一碟。价格部分更具挑战性,最后,我不得不添加几行代码来纠正它,但是在这个例子中,这部分就足够了。
您将在脚本的最终版本中看到,我还为在美国商店购物的读者添加了获取美元价格的功能!
这导致了 try/except 控件的增加,以确保我每次都得到正确的字段。在编写和测试 web scraper 时,我们迟早会面临同样的选择。
我们可能会浪费 2 个小时来获取每次都能得到页面正确部分的 HTML 片段,而不能保证这是可能的!或者我们可以简单地即兴创作一些错误处理条件,这将让我们更快地使用工作工具。
我并不总是这样或那样做,但是当我写代码的时候,我确实学到了更多的注释。这真的有助于提高代码的质量,甚至当你想回去重新开始以前的项目时。
这是运行中的脚本
奇迹发生的地方
正如你所看到的,获得单独的成分是非常容易的。测试完成后,是时候编写一个合适的脚本了,它将:
- 从 csv 文件中获取 URL
- 使用 while 循环抓取每个产品并存储信息
- 在 excel 文件中保存所有结果,包括以前的搜索
要编写这个,您需要您最喜欢的代码编辑器(我使用 Spyder,它随 Anaconda 安装一起提供— 旁注:版本 4 非常好)并创建一个新文件。我们就叫它 Amazon_Scraper.py 。
对我们在上面看到的零碎内容有一些补充,但我希望评论有助于使它变得清楚。这个文件也在 资源库 中。
跟踪产品
关于文件 TRACKER_PRODUCTS.csv 的几点说明,这是一个非常简单的文件,有三列(“url”、“code”、“buy_below”)。您可以在这里添加想要监控的产品 URLs】。
你甚至可以把这个文件放在你同步的 Dropbox 文件夹的某个部分(之后用新的文件路径更新脚本),这样你就可以用你的手机随时更新它。如果您将脚本设置为在服务器或您家中的笔记本电脑上运行,它将在下次运行时从文件中选择新产品链接。
搜索历史
SEARCH_HISTORY 文件也是如此。第一次运行时,您需要将一个空文件(在 存储库 中找到)添加到文件夹“search_history”中。在上面脚本的第 116 行中,当定义 last_search 变量时,我们试图在搜索历史文件夹中找到最后一个文件。这就是为什么你也需要在这里映射你自己的文件夹。只需将文本替换为运行这个项目的文件夹(在我的例子中是“Amazon Scraper”)。
价格预警
在上面的脚本中,第 97 行有一个区域,如果价格低于你的限制,你可以用它来发送一封带有某种警告的电子邮件。
每当你的价格达到目标时,就会发生这种情况。
它出现在 try 命令中的原因是,并不是每次我们从产品页面获得实际价格时,逻辑比较都会返回一个错误——例如,如果产品不可用。
我不想让脚本变得太复杂,所以我把它从最终代码中去掉了。然而,你可以在 这篇文章 中拥有来自一个非常相似项目的代码。
我在脚本中留下了一个带有购买警告的打印指令,所以您只需要用电子邮件部分替换它。就当是你的作业吧!
设置运行脚本的计划任务
亲爱的 Mac/Linux 读者,这部分将只讨论 Windows 中的调度程序,因为这是我使用的系统。对不起 Mac/Linux 用户!我 100%肯定这些系统也有替代方案。如果你能在评论里让我知道,我会在这里加上。
设置一个自动化任务来执行我们的小脚本并不困难:
1 —首先打开“任务计划程序”(只需按下 windows 键并输入)。然后选择“创建任务”并选择“触发器”选项卡。我每天 10 点和 19 点 30 分跑步。
2 —接下来你移动到动作选项卡。在这里,您将添加一个操作,并为“程序/脚本”框选择 Python 文件夹位置。我的位于程序文件目录中,正如你在图片中看到的。
3 —在 arguments 框中,您要键入我们的函数文件名。
4 —我们将告诉系统在我们的文件 Amazon_Scraper.py 所在的文件夹中启动这个命令**。**
从这里开始,任务就可以运行了。您可以探索更多的选项,并进行测试以确保它的工作。这是用 Windows 任务计划程序安排脚本自动运行的基本方法!
我认为我们涵盖了许多有趣的特性你现在可以用它们来探索其他网站或者构建更复杂的东西。感谢您的阅读,如果您有任何问题或建议,我会尽量回复所有邮件!如果我在下面看到一些请求,我可能会考虑做一个视频教程,所以让我知道你是否愿意把它和文章放在一起。
如果你想看我的其他网页抓取例子,这里有两个不同的项目:
[## 我在找房子,所以我用 Python 做了一个 web scraper!
几个月后,我将不得不离开我租的公寓,去找一个新的。尽管这种经历很痛苦…
towardsdatascience.com](/looking-for-a-house-build-a-web-scraper-to-help-you-5ab25badc83e) [## 用一个简单的 Python 机器人增加你的 Instagram 粉丝
我在 4 天内有了 500 个真正的追随者!
towardsdatascience.com](/increase-your-instagram-followers-with-a-simple-python-bot-fde048dce20d)
如果你读到这里,你可能已经意识到我喜欢摄影,所以为了表达我的谢意,我将留给你一张我的照片!
夜晚的卢浮宫金字塔,还有一个不要脸的塞给展柜我的一个爱好!
感谢您的阅读!一如既往,我欢迎反馈和建设性的批评。如果你想取得联系,可以在这里联系我或者直接回复下面的文章。
如何用 Python 抓取多个 URL:教程
因为您想要的数据并不总是您拥有的数据
在本文中,我将向您展示三种从多个 URL 获取数据的方法。更具体地说,我将展示如何遍历页码,遍历手动创建的 URL 列表,最后遍历一个粗略的 URL 列表。
我假设在本教程中,你有一些网页抓取的超级基础知识。如果你需要快速复习如何检查和抓取一个网站,看看这个。
[## 一个 Kaggle 不会教你的教程:网页抓取,数据清理等等
因为数据科学不仅仅是 EDA 和培训模型
towardsdatascience.com](/a-tutorial-of-what-kaggle-wont-teach-you-web-scraping-data-cleaning-and-more-16d402a206e8)
我将从 Hostelworld 收集旅馆数据,Hostelworld 是世界上寻找旅馆最好的网站。好了,现在我们开始吧!
在页码上循环
这是抓取多个页面的最简单、最直接的方式。让我们先来看看我们收集旅舍的 URL 的末尾(文章末尾有完整的 URL):
我们看到对于第一页,我们有 page=1。对于第二页,我们将有 page=2,依此类推。
因此,我们需要做的就是创建一个“for”循环,在这里我们改变最后一个数字。对于每个页面,循环将收集我们指定的信息。
这里是收集从市中心的距离的代码,宿舍床的价格,一个私人房间的价格,以及以前的客户对网站前 2 页找到的所有旅馆的平均评级。
- 我在这里使用 selenium 是因为 hostelworld 页面是 JavaScript 渲染的,BeautifulSoup 无法处理。
- 我去掉了" price-title 5" 元素,因为该元素允许我们知道价格是针对宿舍还是私人房间。
- 睡眠功能有助于控制我们向网站服务器发出请求的速度(避免降低服务器速度),但也有助于确保 selenium 在继续运行之前找到我们想要的信息。
通常,我们会继续清理数据以使其可用,但我将在最后用最后一种方法来做这件事。让我们继续下一个方法。
遍历手动创建的 URL 列表
那太好了,但是如果你要抓取的不同网址没有你可以循环通过的页码怎么办?此外,如果我想要的具体信息只在旅舍的实际页面上提供,该怎么办?
第一种方法是手动创建一个 URL 列表,并遍历这个列表。下面是为前两个旅馆创建 URL 列表的代码:
url = ['[https://www.hostelworld.com/pwa/hosteldetails.php/Casa-Gracia-Barcelona-Hostel/Barcelona/45620?from=2020-07-03&to=2020-07-05&guests=1'](https://www.hostelworld.com/pwa/hosteldetails.php/Casa-Gracia-Barcelona-Hostel/Barcelona/45620?from=2020-07-03&to=2020-07-05&guests=1') ,
'[https://www.hostelworld.com/pwa/hosteldetails.php/Itaca-Hostel/Barcelona/1279?from=2020-07-03&to=2020-07-05&guests=1'](https://www.hostelworld.com/pwa/hosteldetails.php/Itaca-Hostel/Barcelona/1279?from=2020-07-03&to=2020-07-05&guests=1',\)]
然后,您可以创建一个新的“for”循环,遍历列表中的每个元素并收集您想要的信息,其方式与第一种方法完全相同。
如果你只有几个 URL,那也可以,但是想象一下如果你有 100,1000 甚至 10000 个 URL!当然,手动创建列表不是你想要做的(除非你有大量的空闲时间)!
谢天谢地,有更好/更聪明的方法来做事。
循环浏览抓取的 URL 列表
这是本教程的最后一个方法。让我们仔细看看我们正在抓取的 Hostelworld 页面。
我们看到每个旅舍列表都有一个 href 属性,它指定了到单个旅舍页面的链接。这就是我们想要的信息。
方法如下:
- 创建一个“for”循环,抓取我们需要的所有页面的所有 href 属性(以及 URL)。
- 清理数据并创建一个包含所有收集的 URL 的列表。
- 创建一个新的循环,遍历 URL 列表,收集所有需要的信息。
- 清理数据并创建最终的数据帧。
需要指出的是,如果抓取的每一页都有不同的结构,这种方法将无法正常工作。网址需要来自同一个网站!
对于每一个旅舍页面,我都刮掉了旅舍的名称、一张床最便宜的价格、评论数量以及 8 个类别(位置、氛围、安全、清洁度等)的评论分数。).
这使得我们看到的第一种方法毫无用处,因为使用这种方法,我们可以获得所有相同的信息,甚至更多!
下面是获得干净的 URL 列表的代码。
- 不需要的链接很可能会出现在你的 URL 列表中,就像这里的情况一样。一般来说,几乎总是有一个非常独特的模式来区分你想要的网址和其他网址(宣传等)。).在这种情况下,所有指向旅馆的链接都以/pwa/开头。
- 我将字符串*‘https://www.hostelworld.com’*添加到列表的每个元素中。在接下来的循环中,URL 需要用到这一部分。
现在我们有了干净网址的列表,我们可以通过循环列表来抓取我们想要的所有信息。
由于每次迭代大约需要 15-20 秒,所以我将只对这里的前 10 个旅舍这样做。你可以很容易地通过改变范围来改变它。
- 当我收集评论数量时,由于该信息在每页上出现两次,所以我使用[-1]只获得最后一次发现的评论数量。
- 通常有许多价格选择(取决于宿舍的类型)。最后给的价格永远是最便宜的,这也是我想保留的。如果找到多个,try/except 循环基本上保持最后的价格,如果只找到一个,则保持价格不变。
这种类型的循环是处理潜在错误的好方法!
收集完所有数据后,下面是清理数据并将其放入 dataframe 的代码:
这是最终数据帧的头部:
现在你有了,三种不同的抓取多个页面/URL 的方法。我真的希望这有所帮助,不要忘记负责任地刮。
非常感谢你的阅读!
参考
巴塞罗那拥有欧洲最好的夜生活,吸引了来自世界各地的背包客和派对爱好者…
www.hostelworld.com](https://www.hostelworld.com/s?q=Barcelona,%20Catalonia,%20Spain&country=Spain&city=Barcelona&type=city&id=83&from=2020-07-03&to=2020-07-05&guests=1&page=1)
用 Python 从公共 API 抓取新闻和文章
让我们探索纽约时报、卫报、黑客新闻和其他 API,并为您的下一个项目收集一些新闻数据!
无论你是数据科学家、程序员还是人工智能专家,你都可以很好地利用大量的新闻文章。尽管获得这些文章可能具有挑战性,因为你必须经过相当多的环节才能获得实际数据——找到正确的新闻来源,探索它们的 API,找出如何根据它们进行认证,最后收集数据。那是很多工作而且没有乐趣。
因此,为了节省您的时间并帮助您入门,这里列出了我能够找到的公共新闻 API,并解释了如何验证、查询它们,最重要的是如何从它们那里获取您需要的所有数据的示例!
纽约时报
在我看来,第一个也是最好的数据来源是《纽约时报》。要开始使用它的 API,你需要在https://developer.nytimes.com/accounts/create创建一个账户,在https://developer.nytimes.com/my-apps/new-app创建一个应用程序。创建应用程序时,您可以选择激活哪些 API——我建议至少激活最受欢迎的、文章搜索、头条新闻和归档 API。当您的应用程序被创建时,您将看到一个密钥,您将使用它来与所有选定的 API 进行交互,所以复制它,让我们开始查询吧!
我们可以用《T21 时报》API 做的最简单的查询就是查找当前的头条新闻:
上面的片段非常简单。我们针对提供了section
名称和我们的 API 密钥的topstories/v2
端点运行一个GET
请求。在这种情况下,Section 是 science ,但是 NY Times 在这里提供了许多其他选项,例如时尚、健康、体育或戏剧。完整列表可在这里找到。这个特定的请求将产生类似如下的响应:
接下来,当你试图获得一些特定的数据集时,可能是最有用的端点是文章搜索端点:
这个端点具有许多过滤选项。唯一的必填字段是q
(查询),这是搜索词。除此之外,您还可以混合匹配过滤查询、日期范围(begin_date
、end_date
)、页码、排序顺序和方面字段。过滤器查询(fq
)是一个有趣的查询,因为它允许使用 Lucene 查询语法,可以使用逻辑操作符(AND
、OR
)、否定或通配符来创建复杂的过滤器。好看的教程可以在这里找到。
上述查询的示例响应可能是这样的(为了清楚起见,删除了一些字段):
我将在这里展示的纽约时报的最后一个端点是他们的归档 API,它返回了从 1851 年开始的给定月份的文章列表。如果您需要大量数据,并且不需要搜索特定术语,这将非常有用。
上面的查询搜索了 1852 年 6 月以来的所有文章,从下面的结果中我们可以看到,即使我们搜索了非常旧的文章,我们仍然得到了 1888 个命中结果。也就是说,其中大多数缺乏有用的数据,如关键词、字数、作者等。所以你最好还是搜索一些最近的文章。
这些只是纽约时报提供的一些(在我看来)更有用的 API。除了这些,在 https://developer.nytimes.com/apis 还有更多。为了探索每一个 API,我还推荐使用查询构建器,比如用于文章搜索的的,它让你不用任何编码就可以在网站上构建并执行你的测试查询。
《卫报》
接下来是另一个重要的新闻和文章来源——卫报。与纽约时报一样,我们首先需要注册一个 API 密钥。你可以在 https://bonobo.capi.gutools.co.uk/register/developer 办理,你会收到一封电子邮件,里面有你的钥匙。这样一来,我们可以导航到 API 文档并开始查询 API。
让我们简单地从查询卫报的内容部分开始:
这些部分将内容按主题分组,如果您正在寻找特定类型的内容,例如科学或技术,这将非常有用。如果我们省略 query ( q
)参数,我们将收到完整的部分列表,大约 75 条记录。
继续一些更有趣的事情——通过标签搜索:
这个查询看起来与前一个非常相似,也返回相似类型的数据。标签也将内容分成不同的类别,但是标签的数量(大约 50000 个)要比章节多得多。这些标签中的每一个都具有类似于例如world/extreme-weather
的结构。这些在搜索实际文章时非常有用,这是我们接下来要做的。
你来这里的真正目的是文章搜索,为此我们将使用https://open-platform.theguardian.com/documentation/search:
我首先向您展示部分和标签搜索的原因是,它们可以用于文章搜索。从上面可以看到,我们使用了section
和tag
参数来缩小搜索范围,这些值可以使用前面显示的查询找到。除了这些参数,我们还为我们的搜索查询包括了明显的q
参数,但也使用了from-date
和show-fields
参数的开始日期,这允许我们请求与内容相关的额外字段——在这种情况下,这些将是标题、署名、评级和缩短的 URL。这里有更多的完整列表
与之前的所有问题一样,以下是示例响应:
黑客新闻
对于更多面向技术的新闻来源,人们可能会转向 HackerNews ,它也有自己的公共 REST API。https://github.com/HackerNews/API 上有记载。正如你将看到的,这个 API 是在版本v0
中,目前非常简单,这意味着它并没有真正提供特定的端点,例如查询文章、评论或用户。
但是,即使它非常基础,它仍然提供了所有必要的东西,例如,获取头条新闻:
上面的代码片段没有前面的那么明显,所以让我们更仔细地看一下。我们首先向 API 端点(v0/topstories
)发送请求,它不会像您所期望的那样返回热门故事,而实际上只是它们的 id。为了获得实际的故事,我们获取这些 id(前 10 个)并向v0/item/<ID>
端点发送请求,端点返回每个单独项目的数据,在本例中恰好是一个故事。
您肯定注意到查询 URL 是用query_type
参数化的。这是因为, HackerNews API 也为网站的所有顶部部分提供了类似的端点,即提问、展示、工作或新闻。
这个 API 的一个优点是它不需要认证,所以你不需要请求 API 密钥,也不需要像其他 API 那样担心速率限制。
运行这段代码将得到类似如下的响应:
如果你发现了一篇有趣的文章,想更深入地挖掘一下,那么 HackerNews API 也可以帮你。您可以通过遍历所述故事的kids
字段找到每个提交的评论。这样做的代码如下所示:
首先,我们按照 ID 查找 story ( item
),就像我们在前面的例子中做的那样。然后,我们迭代它的kids
,用各自的 id 运行相同的查询,检索条目,在本例中是指故事评论。如果我们想要构建特定故事的评论的整个树/线程,我们也可以递归地遍历这些。
和往常一样,以下是回答示例:
电流
找到受欢迎和高质量的新闻 API 是相当困难的,因为大多数经典报纸没有免费的公共 API。然而,总的新闻数据来源可以用来从报纸上获取文章和新闻,例如金融时报和彭博只提供付费 API 服务,或者 CNN 根本不公开任何 API。
其中一个聚合器叫做 Currents API 。它汇集了来自数千个来源、18 种语言和 70 多个国家的数据,而且还是免费的。
它类似于之前显示的 API。我们再次需要首先获得 API 密钥。为此,你需要在 https://currentsapi.services/en/register 注册。之后,在https://currentsapi.services/en/profile访问您的个人资料并取回您的 API 令牌。
准备好密钥(令牌)后,我们可以请求一些数据。只有一个有趣的终点,那就是 https://api.currentsapi.services/v1/search 的:
这个端点包括许多过滤选项,包括语言、类别、国家等等,如上面的代码片段所示。所有这些都是不言自明的,但是对于我提到的前三个,您将需要一些额外的信息,因为它们可能的值并不是很明显。这些值来自可用的 API 端点这里是,在语言和区域的情况下,实际上只是值到代码的映射(例如"English": "en"
),在类别的情况下,只是可能值的列表。上面的代码中省略了它,以使它更短一些,但是我只是将这些映射复制到 Python dict
中,以避免每次都调用 API。
对上述要求的回应如下:
如果您不搜索特定主题或历史数据,那么还有一个选项是 Currents API 提供的,即最新新闻端点:
它非常类似于search
端点,但是这个端点只提供language
参数并产生如下结果:
结论
互联网上有许多很棒的新闻网站和在线报纸,但在大多数情况下,你无法抓取它们的数据或以编程方式访问它们。本文中展示的是极少数具有良好 API 和免费访问的应用程序,您可以将其用于您的下一个项目,无论是一些数据科学、机器学习还是简单的新闻聚合器。如果你不介意为新闻 API 付费,你也可以考虑使用金融时报或彭博。除了 API 之外,你还可以尝试用类似于 BeautifulSoup 的东西抓取 HTML 并自己解析内容。如果你碰巧发现任何其他好的新闻数据来源,请告诉我,以便我可以将其添加到这个列表中。🙂
本文最初发布于martinheinz . dev
通过 SQLAlchemy 及其混合属性、嵌套查询、表元数据,在 Python 中使用 SQL 很容易
towardsdatascience.com](/advanced-sqlalchemy-features-you-need-to-start-using-e6fc1ddafbdb) [## 你可以用 GitHub API 和 Python 做的所有事情
GitHub REST API 允许您管理问题、分支、回购、提交等等,所以让我们看看您如何使用…
towardsdatascience.com](/all-the-things-you-can-do-with-github-api-and-python-f01790fca131) [## 自动化 Python 项目的各个方面
每个 Python 项目都可以从使用 Makefile、优化的 Docker 映像、配置良好的 CI/CD、代码…
towardsdatascience.com](/automating-every-aspect-of-your-python-project-6517336af9da)
使用 Python 收集 NFL 数据并比较四分卫效率
使用 BeautifulSoup 收集 NFL 数据并绘制四分卫的雷达图
Riley McCullough 在 Unsplash 上拍摄的照片
我总是担心试图收集自己的数据,事实上,像 Kaggle 这样的网站聚集了如此高质量的数据集,使得学习这种技能变得不那么必要。然而,这个平台上大量关于数据科学的教育文章帮助我在收集自己的数据集方面取得了进展。很多搜集数据的灵感和方法都来自这里的。
在本文中,我将从 Pro Football Reference 中提取 2019–20 NFL 赛季的四分卫统计数据,并使用它们创建雷达图来评估 QB 效率。
加载包
为了打开网页并抓取数据,我们将使用两个模块,urllib.request
打开 URL,BeautifulSoup
解析 HTML。
# Import scraping modules
from urllib.request import urlopen
from bs4 import BeautifulSoup
除了这些包,我们还需要一些包来操作数据,numpy
和pandas
,并绘制我们的数据,matplotlib
。
# Import data manipulation modules
import pandas as pd
import numpy as np# Import data visualization modules
import matplotlib as mpl
import matplotlib.pyplot as plt
刮数据
我们要导入的数据是 2019 赛季的 NFL 传球数据,可以在这里找到。我们打开站点,并通过以下内容将其传递给BeautifulSoup
:
# URL of page
url = 'https://www.pro-football-reference.com/years/2019/passing.htm'# Open URL and pass to BeautifulSoup
html = urlopen(url)
stats_page = BeautifulSoup(html)
请注意,通过将 URL 中的2019
更改为您选择的年份,我们可以很容易地将所有这些分析调整到以前的年份。
我们将用来抓取页面的两个BeautifulSoup
函数是findAll()
和getText()
,它们基于我们正在抓取的页面的 HTML 返回值。我在下面给出了简化的用例——对于所有的可能性,你应该参考文档。
findAll(name)Parameters
name -- HTML tags to use to parse webpageReturns array of all matches to name tag getText()Returns text from HTML
为了使这些有效,我们必须确定页面源中的模式。在我们的例子中,数据在表格中被很好地格式化,所以我们可以找到所有的表格行(tr
)和列(td
),并直接从单元格中提取文本。
首先,我们需要收集列标题,以便稍后在数据框架中使用它们。为此,我们找到页面中的第一个tr
元素,并从所有表格标题中收集文本(th
):
# Collect table headerscolumn_headers = stats_page.findAll('tr')[0]
column_headers = [i.getText() for i in column_headers.findAll('th')]
我们索引第一个元素,因为这是包含列标题的行。我们可以查看我们的结果:
print(column_headers)>>> ['Rk', 'Player', 'Tm', 'Age', 'Pos', 'G', 'GS', 'QBrec', 'Cmp', 'Att', 'Cmp%', 'Yds', 'TD', 'TD%', 'Int', 'Int%', '1D', 'Lng', 'Y/A', 'AY/A', 'Y/C', 'Y/G', 'Rate', 'QBR', 'Sk', 'Yds', 'NY/A', 'ANY/A', 'Sk%', '4QC', 'GWD']
为了收集实际的数据,我们将首先收集所有的表行(tr
)并将它们存储在一个数组中。然后,我们遍历每一行,用getText()
收集每一列中的文本(td
):
# Collect table rows
rows = stats_page.findAll('tr')[1:]# Get stats from each row
qb_stats = []
for i in range(len(rows)):
qb_stats.append([col.getText() for col in rows[i].findAll('td')])
我们跳过第一行,因为这些是我们之前刚刚收集的列标题。当我们检查qb_stats
列表的第一行时:
print(qb_stats[0])>>> ['Jared Goff', 'LAR', '25', 'QB', '16', '16', '9-7-0', '394', '626', '62.9', '4638', '22', '3.5', '16', '2.6', '220', '66', '7.4', '7.0', '11.8', '289.9', '86.5', '', '22', '170', '6.90', '6.46', '3.4', '1', '2']
现在,我们可以将标题和统计数据合并到一个pandas
数据帧中。如果您以前没有使用过pandas
,它本质上是一个 Excel 电子表格,您可以通过编程来操作,因此它非常适合处理大型数据集。我们注意到上面的Rk
列在我们的qb_stats
列表中没有相应的数据——这是因为那些值都是表头,所以我们的findAll()
函数没有提取它们的数据。无论如何,这些数据对于我们的分析来说都是无关紧要的,因此我们在创建数据帧时会将其忽略。
# Create DataFrame from our scraped data
data = pd.DataFrame(qb_stats, columns=column_headers[1:])
columns
—数据框架的列标题(我们省略了Rk
)
现在,让我们看看数据帧的前五行:
# Examine first five rows of data
data.head()
成功!
操作和清理数据
现在,我们可以处理这个数据框中的数据,得到我们需要的东西来制作我们的雷达图。首先,让我们看看集合中的所有列:
# View columns in data
data.columns>>> Index(['Player', 'Tm', 'Age', 'Pos', 'G', 'GS', 'QBrec', 'Cmp', 'Att', 'Cmp%', 'Yds', 'TD', 'TD%', 'Int', 'Int%', '1D', 'Lng', 'Y/A', 'AY/A', 'Y/C', 'Y/G', 'Rate', 'QBR', 'Sk', 'Yds', 'NY/A', 'ANY/A', 'Sk%', '4QC', 'GWD'], dtype='object')
我们立即看到一个问题—有两个标题为Yds
的列;一个是传球码,另一个是因为被抢而失去的码。我们可以通过重命名后者来轻松解决这个问题:
# Rename sack yards column to `Yds_Sack`
new_columns = data.columns.values
new_columns[-6] = 'Yds_Sack'
data.columns = new_columns
现在,让我们再次查看我们的专栏:
# View columns in data
data.columns>>> Index(['Player', 'Tm', 'Age', 'Pos', 'G', 'GS', 'QBrec', 'Cmp', 'Att', 'Cmp%', 'Yds', 'TD', 'TD%', 'Int', 'Int%', '1D', 'Lng', 'Y/A', 'AY/A', 'Y/C', 'Y/G', 'Rate', 'QBR', 'Sk', 'Yds_Sack', 'NY/A', 'ANY/A', 'Sk%', '4QC', 'GWD'], dtype='object')
我们已经成功地将该列重命名为Yds_Sack
,并且不再有列名冲突!接下来,让我们确定我们对可视化感兴趣的统计类别。我们将选择:
(1)完成百分比— Cmp%
(2)传球码— Yds
(3)传球达阵— TD
(4)拦截— Int
每次试跳 5 码— Y/A
(6)及格分数— Rate
# Select stat categories
categories = ['Cmp%', 'Yds', 'TD', 'Int', 'Y/A', 'Rate']
现在,让我们创建一个新的数据框架,作为原始数据的子集,只使用我们选择的类别中的数据。此外,我们将添加球员的名字和球队。
# Create data subset for radar chart
data_radar = data[['Player', 'Tm'] + categories]
data_radar.head()
现在我们有了数据子集,让我们检查数据类型,因为我们从 URL 中提取了文本形式的值:
# Check data types
data_radar.dtypes>>> Player object
Tm object
Cmp% object
Yds object
TD object
Int object
Y/A object
Rate object
dtype: object
我们所有的数字数据都被存储为对象,所以我们不能操作它们。因此,在我们继续之前,我们必须将所有这些数据转换成数值。为此,我们使用了一个名为pandas.to_numeric
的函数。
# Convert data to numerical values
for i in categories:
data_radar[i] = pd.to_numeric(data[i])
现在,让我们再次检查我们的数据类型:
# Check data types
data_radar.dtypes>>> Player object
Tm object
Cmp% float64
Yds float64
TD float64
Int float64
Y/A float64
Rate float64
dtype: object
我们还有最后一项数据清理工作要做。在最初的网站上,他们把装饰性的人物放在有季末成就的球员旁边,比如职业碗(*)或全职业(+)选择。我们将使用str.replace()
移除这些:
# Remove ornamental characters for achievements
data_radar['Player'] = data_radar['Player'].str.replace('*', '')
data_radar['Player'] = data_radar['Player'].str.replace('+', '')
让我们把数据过滤到投球距离超过 1500 码的四分卫:
# Filter by passing yards
data_radar_filtered = data_radar[data_radar['Yds'] > 1500]
现在,对于我们的雷达图,我们想通过百分位数计算每个四分卫的统计排名,这在pandas
中用DataFrame.rank(pct=True)
很容易完成。rank()
函数可以采用其他参数来根据其他参数进行排序,您可以查阅在线文档。
此外,我们想翻转我们的拦截排名,因为我们不希望有最多拦截的 QB 有最高的百分位数!
# Create columns with percentile rank
for i in categories:
data_radar_filtered[i + '_Rank'] =
data_radar_filtered[i].rank(pct=True)# We need to flip the rank for interceptions
data_radar_filtered['Int_Rank'] =
1 - data_radar_filtered['Int_Rank']
现在我们检查我们的新数据:
# Examine data
data_radar_filtered.head()
太好了!我们现在可以进行可视化了!
生成雷达图
我们将从编辑一些通用的绘图参数开始。我们将生成一个极坐标图,因此 x 刻度对应于围绕圆的角度,我们将增加轴和刻度标签之间的填充:
# General plot parameters
mpl.rcParams['font.family'] = 'Avenir'
mpl.rcParams['font.size'] = 16
mpl.rcParams['axes.linewidth'] = 0
mpl.rcParams['xtick.major.pad'] = 15
对于图表中的颜色,我们将使用 NFL 球队颜色的十六进制代码,这些代码是从链接中收集的。
team_colors = {'ARI':'#97233f', 'ATL':'#a71930', 'BAL':'#241773', 'BUF':'#00338d', 'CAR':'#0085ca', 'CHI':'#0b162a', 'CIN':'#fb4f14', 'CLE':'#311d00', 'DAL':'#041e42', 'DEN':'#002244', 'DET':'#0076b6', 'GNB':'#203731', 'HOU':'#03202f', 'IND':'#002c5f', 'JAX':'#006778', 'KAN':'#e31837', 'LAC':'#002a5e', 'LAR':'#003594', 'MIA':'#008e97', 'MIN':'#4f2683', 'NWE':'#002244', 'NOR':'#d3bc8d', 'NYG':'#0b2265', 'NYJ':'#125740', 'OAK':'#000000', 'PHI':'#004c54', 'PIT':'#ffb612', 'SFO':'#aa0000', 'SEA':'#002244', 'TAM':'#d50a0a', 'TEN':'#0c2340', 'WAS':'#773141'}
我们绘制所有观点的角度将取决于我们所拥有的统计类别的数量。在我们的例子中,我们有 6 个类别,所以我们将每隔 2π/6 弧度或 60 度绘制我们的点。我们可以使用numpy.linspace()
计算如下:
# Calculate angles for radar chart
offset = np.pi/6
angles = np.linspace(0, 2*np.pi, len(categories) + 1) + offset
我们添加了offset
项来调整标签出现在圆上的位置(第一个标签不是 0 弧度,而是出现在π/6)。
当我们绘制数据时,我们需要实际复制第一个类别,这样形状就会自动闭合。如果你注意到当我们计算角度时,我们有 7 个数据点,第一个和最后一个角度对应于圆上的同一点。因此,当我们绘制数据时,我们使用以下内容(我们将把它变成一个函数,所以player_data
表示我们将传递的一行特定于玩家的数据):
# Plot data and fill with team color
ax.plot(angles, np.append(player_data[-(len(angles)-1):], player_data[-(len(angles)-1)]), color=color, linewidth=2)ax.fill(angles, np.append(player_data[-(len(angles)-1):], player_data[-(len(angles)-1)]), color=color, alpha=0.2)
上面的代码是有效的,因为我们的类别是数据帧的最后 6 列,所以len(angles) — 1
对应于这些类别中的第一列(因为 angles 有一个额外的元素)。然后,我们将第一个类别的值附加到该数组的末尾,这样我们就可以关闭该形状。
现在我们可以设置类别名称的标签(因为我们的类别比角度少一个,所以我们省略了最后一个元素):
# Set category labels
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories)
最后,我们将在雷达图的顶部添加玩家的名字——我们将文本对象放置在绝对绘图坐标中的(π/2,1.7)处,以便它出现在轴的上方:
# Add player name
ax.text(np.pi/2, 1.7, player_data[0], ha='center', va='center', size=18, color=color)
我们可以将所有这些放在一起,创建一个辅助函数来生成我们的雷达图,如下所示:
让我们创建另一个助手函数,当给定一个团队输入时,返回 QB 数据的numpy
数组:
# Function to get QB data
def get_qb_data(data, team):
return np.asarray(data[data['Tm'] == team])[0]
可视化数据
NFC 西
因为我是 49 人队的超级粉丝,所以我们先来看看 NFC 西区 QBs 的雷达图:
# Create figure
fig = plt.figure(figsize=(8, 8), facecolor='white')# Add subplots
ax1 = fig.add_subplot(221, projection='polar', facecolor='#ededed')
ax2 = fig.add_subplot(222, projection='polar', facecolor='#ededed')
ax3 = fig.add_subplot(223, projection='polar', facecolor='#ededed')
ax4 = fig.add_subplot(224, projection='polar', facecolor='#ededed')# Adjust space between subplots
plt.subplots_adjust(hspace=0.8, wspace=0.5)# Get QB data
sf_data = get_qb_data(data_radar_filtered, 'SFO')
sea_data = get_qb_data(data_radar_filtered, 'SEA')
ari_data = get_qb_data(data_radar_filtered, 'ARI')
lar_data = get_qb_data(data_radar_filtered, 'LAR')# Plot QB data
ax1 = create_radar_chart(ax1, angles, lar_data, team_colors['LAR'])
ax2 = create_radar_chart(ax2, angles, ari_data, team_colors['ARI'])
ax3 = create_radar_chart(ax3, angles, sea_data, team_colors['SEA'])
ax4 = create_radar_chart(ax4, angles, sf_data, team_colors['SFO'])plt.show()
数据似乎支持我们从视力测试中看到的:
- Russell Wilson 处于精英水平,在所有统计类别中至少达到 75 个百分点。这个分析也没有考虑到他的扰流能力带来了什么。
- 吉米·加罗波洛通常是一个可靠的四分卫,但不时会投出一些愚蠢的拦截。
- 贾里德·戈夫(Jared Goff)获得了很多码,但这似乎并没有转化为大量生产(即“空码”)。
- 凯勒穆雷很难判断,因为他是一个新秀,但他的效率看起来是四个人中最低的。像威尔逊一样,他的机动性在这个情节中没有被考虑。
MVP 赛
最有价值球员的争夺(在 QB)在拉塞尔·威尔逊和拉马尔·杰克逊(谁最终获胜)之间展开:
# MVP Race
# Create figure
fig = plt.figure(figsize=(8, 4), facecolor='white')# Add subplots
ax1 = fig.add_subplot(121, projection='polar', facecolor='#ededed')
ax2 = fig.add_subplot(122, projection='polar', facecolor='#ededed')# Adjust space between subplots
plt.subplots_adjust(hspace=0.8, wspace=0.5)# Get QB data
bal_data = get_qb_data(data_radar_filtered, 'BAL')
sea_data = get_qb_data(data_radar_filtered, 'SEA')# Plot QB data
ax1 = create_radar_chart(ax1, angles, sea_data, team_colors['SEA'])
ax2 = create_radar_chart(ax2, angles, bal_data, team_colors['BAL'])plt.show()
从一个纯粹的传球者的角度来看,拉塞尔·威尔逊有更平衡的数据,但拉马尔·杰克逊显然有更“划算”的传球码数,正如他的传球得分和四分卫得分所显示的那样。此外,拉马尔·杰克逊创造了一个赛季的 QB 冲刺记录,这在这张图表中没有体现出来。
起草第一轮 QBs 的团队
四名四分卫在 2020 年 NFL 第一轮选秀中被选中:乔·伯罗(CIN),图阿·塔戈瓦洛(米娅),贾斯汀·赫伯特(拉加),以及(令人惊讶的)乔丹·洛夫(英国)。让我们想象一下这些选秀会取代的四分卫的数据。
# 1st Round Draft Picks
# Create figure
fig = plt.figure(figsize=(8, 8), facecolor='white')# Add subplots
ax1 = fig.add_subplot(221, projection='polar', facecolor='#ededed')
ax2 = fig.add_subplot(222, projection='polar', facecolor='#ededed')
ax3 = fig.add_subplot(223, projection='polar', facecolor='#ededed')
ax4 = fig.add_subplot(224, projection='polar', facecolor='#ededed')# Adjust space between subplots
plt.subplots_adjust(hspace=0.8, wspace=0.5)# Get QB data
cin_data = get_qb_data(data_radar_filtered, 'CIN')
mia_data = get_qb_data(data_radar_filtered, 'MIA')
lac_data = get_qb_data(data_radar_filtered, 'LAC')
gnb_data = get_qb_data(data_radar_filtered, 'GNB')# Plot QB data
ax1 = create_radar_chart(ax1, angles, cin_data, team_colors['CIN'])
ax2 = create_radar_chart(ax2, angles, mia_data, team_colors['MIA'])
ax3 = create_radar_chart(ax3, angles, lac_data, team_colors['LAC'])
ax4 = create_radar_chart(ax4, angles, gnb_data, team_colors['GNB'])plt.show()
- 无论从哪方面来看,安迪·道尔顿都经历了一个糟糕的赛季——显然是时候继续前进了
- Ryan Fitzpatrick 在所有六个统计指标上都低于平均水平,这个赛季表现平平。考虑到乔什-罗森在菲茨派翠克的比赛中坐冷板凳,热火需要为图阿的未来投资。
- 菲利普·里弗斯实际上在大多数类别中表现平平,但显然有一些糟糕的决策,从他拦截的次数来看。
- Aaron Rodgers 经历了一个似乎高于平均水平的赛季,但从我们对他的高度期望来看,仍然低于平均水平。乔丹的爱情选择对每个人来说都是一个震惊。
其他分部
NFC 北方
NFC 东
NFC South
亚足联西区
AFC 北部
亚足联东区
亚足联南区
结束语
正如您所看到的,可能性是无限的——这个框架可以很容易地适用于其他统计数据,如抢断和接收指标。本文使用的 Jupyter 笔记本可以在这个 Github 资源库中找到。
感谢您的阅读!我感谢任何反馈,你可以在 Twitter 上找到我,并在 LinkedIn 上与我联系以获得更多更新和文章。
从半结构化文档中抓取结构化数据
作为建立朋友角色网络项目的一部分,我需要从每一集收集信息
数据科学工具带来的最强大的功能之一是处理非结构化数据并将其转化为可结构化和可分析的数据的能力。任何称职的数据科学家都应该能够从文档中“抓取”数据,无论是来自网络、本地还是任何其他类型的基于文本的资产。
在这篇文章中,我将展示如何抓取电视节目 Friends 的在线脚本,目的是为每一集创建一个编号场景表以及这些场景中的角色名称。这是我建立互动朋友网络网站的第一步。这也是一个很好的抓取技术的测试案例,因为剧本都有一定的基本结构,但这种结构并不完全可以预测,编剧并不总是遵循每集相同的格式规则。
你可以在我的朋友项目的 Github repo 上找到这项工作的所有最终代码。
我要刮的脚本可以在这里找到。如果你点击每一个,你可以看到文件名有一个一致的格式,格式为[https://fangj.github.io/friends/season/[ss][ee].html](https://fangj.github.io/friends/season/0101.html)
,其中[ss]
是两位数的季号,[ee]
是两位数的集号。
为了有效地刮——你需要了解两件事:
- 您选择的语言的相关抓取包。在我将在这里使用的 R 中,这些包是
rvest
和xml2
。在 Python 里,大部分人用的都是美汤。如果你想要一个关于如何使用rvest
和xml2
抓取网页的深入教程,请看我之前的文章这里,我建议你在阅读这篇文章之前先阅读一下。 - 使用
regex
(正则表达式的缩写)的能力,这是文本搜索的语言。在本文中,我们将更多地关注regex
。regex
的语法在不同的平台上可能会有一些不同,但是大部分都非常相似。R 中regex
的一个很好的备忘单是这里的。regex
不是一个容易精通的话题——这个世界上很少有真正的regex
专家——但是大多数数据科学家知道足够多的知识来执行最常见的任务,并且经常在网上搜索他们需要的其余内容。
看着老友记的剧本,想出我们需要做什么
让我们提醒自己这里的任务。我们对两件事感兴趣:
- 我们想把一集分成几个场景
- 我们想列出每个场景中出现的所有角色
让我们来看看第一季第一集的网页代码。你可以通过在 Google Chrome 中打开脚本,然后按下 CMD+Option+C(或者 Windows 中的 Ctrl+Shift+C)来打开 Elements 控制台,在这里你可以与页面本身并排查看页面的 HTML。
我们可以立即看到的一件事是,冒号前面的大多数单词都是我们感兴趣的。其实大部分都是在一个场景里说了些什么的角色名。我们还看到包含字符串"Scene:"
的线条是场景边界的非常可靠的指示器。
我们可能要做的第一件事是在一个列表或节点向量中获取 HTML 代码,这些节点表示文档中不同的格式和文本。因为这将包含每个角色所说的单独的台词,这将对我们的工作非常有帮助。所以我们将使用我们漂亮的抓取包来下载 HTML 代码,将其分解成节点,这样我们就有了一个漂亮整洁的脚本内容向量。
library(rvest) # (also automatically loads xml2) url_string <- "https://fangj.github.io/friends/season/0101.html" nodes <- xml2::read_html(url_string) %>%
xml2::as_list() %>%
unlist()
现在,如果你看一下你的nodes
对象,你会发现它是一个字符向量,包含许多不同的分割部分,但最重要的是它包含脚本中的行,例如:
> nodes[16] html.body.font.p
"[Scene: Central Perk, Chandler, Joey, Phoebe, and Monica are there.]"
使用正则表达式提取我们需要的内容
为了对我们的任务有用,我们需要创建一个向量,如果该行代表一个场景的开始,则包含单词“New Scene ”,如果该行代表角色所说的话,则包含角色的名称。这将是我们想要做的最好的形式。
我们需要做的第一件事是将任何包含"Scene:"
的文本字符串交换为字符串"New Scene"
。我们可以非常简单地在nodes
向量上使用ifelse()
,其中我们使用grepl()
来识别nodes
中的哪些条目包含字符串"Scene:"
。
nodes <- ifelse(grepl("Scene:", nodes), "New Scene", nodes)
我们可以快速检查是否有包含"New Scene"
的条目。
> nodes[nodes == "New Scene"] [1] "New Scene" "New Scene" "New Scene" "New Scene" "New Scene" "New Scene" "New Scene" "New Scene"
[9] "New Scene" "New Scene" "New Scene" "New Scene" "New Scene" "New Scene" "New Scene"
效果很好。现在,你可能也注意到了冒号前面的字符名。因此,这可能是提取字符名称的好方法(虽然它可能会给我们一些脚本中冒号之前的其他内容,但我们可以稍后再处理)。
所以我们要做的是用regex
告诉 R 我们正在寻找冒号前面的任何东西。我们将如下使用一个前瞻字符串:".+?(?=:)"
。
让我们看看这个字符串,确保我们知道它的意思。.+?
组件的意思是“查找任意长度的任意文本字符串”。括号中的部分被称为 lookahead :它的意思是向前查找该字符串,并找到一个冒号作为下一个字符。因此,这指示 R 查找冒号前面的任何文本字符串并返回它。如果我们将包stringr
和它的函数str_extract()
与这个 regex 字符串一起使用,它将遍历nodes
向量的每个条目,并将其转换为冒号前面的第一个文本字符串。如果没有找到这样的字符串,它将返回一个NA
值。这对我们来说很好,因为我们知道口语字符名称总是在节点的开始,所以如果我们只取每行中的第一个实例,我们肯定不会错过任何一个。为了安全起见,我们还应该不要弄乱我们已经放入向量中的场景中断:
library(stringr) nodes <- ifelse(nodes != "New Scene",
stringr::str_extract(nodes, ".+?(?=:)"),
nodes)
现在让我们看看节点向量中有什么
> nodes[sample(100)]
[1] "Joey" NA "Chandler"
[4] NA "Phoebe" "All"
[7] "Chandler" NA NA
[10] NA NA NA
[13] NA "Joey" "Chandler"
[16] NA NA NA
[19] NA "Transcribed by" NA
[22] "Joey" NA NA
[25] NA NA NA
[28] NA NA NA
[31] "Phoebe" NA NA
[34] "Monica" NA NA
[37] NA NA NA
[40] NA "Additional transcribing by" NA
[43] "(Note" NA NA
[46] NA NA NA
[49] NA NA NA
[52] NA "Monica" NA
[55] NA NA "New Scene"
[58] "Monica" NA "Ross"
[61] NA NA NA
[64] NA NA "Chandler"
[67] "Joey" NA NA
[70] "Chandler" "Chandler" NA
[73] NA NA NA
[76] NA "Written by" "Monica"
[79] NA NA NA
[82] "Ross" "Joey" "Monica"
[85] NA NA NA
[88] NA NA "Chandler"
[91] "Phoebe" NA NA
[94] "Chandler" NA NA
[97] NA NA NA
[100] NA
所以这是可行的——但是我们有更多的清洁工作要做。例如,我们想要去掉NA
值。我们还注意到,有一些序言行通常包含单词“by ”,我们可以看到括号中的字符串(如“(注意))似乎已被提取。如果我们不想要它们,我们可以创建一堆特殊的清理命令来清除它们。例如:
nodes <- nodes[!is.na(nodes)] # remove NAs
# remove entries with "by" or "(" or "all" irrelevant of the case
nodes <- nodes[!grepl("by|\\(|all", tolower(nodes))]
让我们看一个例子:
> nodes[sample(10)]
[1] "Phoebe" "Monica" "New Scene" "Chandler" "Chandler" "Joey" "Chandler" "Monica"
[9] "Chandler" "Phoebe"
这看起来不错。当然,我上面做的清理步骤并不完整,我们放在!grepl("by|\\(|all", tolower(nodes))
中的那个字符串将会随着我们做越来越多的抓取而扩展,以解决解析中的常见故障。但是你明白了。请注意,当您在regex
字符串中使用字符时,这些字符也被用作“特殊字符”,您需要在它们前面加上一个\\
来对它们进行转义。我的意思是:R 需要知道你什么时候输入一个(
-你的字面意思是你正在寻找一个(
还是你正在启动一个前瞻命令或类似的东西。所以如果你想让一个特殊的字符按字面意思理解——是的,我真的想要一个字面的左括号——你应该在 regex 中把它写成\\(
。请参考上面的备忘单链接或在线查看 regex 中的特价商品列表。
组织我们的产出
让我们假设我们的清理工作已经完成,我们有一个很好的向量,它包含在剧集中说台词的角色的名字或者“新场景”来表示我们正在穿越一个场景边界。我们现在只需要将这个向量转换成一个简单的数据帧,它有三列:episode
、scene
和character
。
剧集编号是显而易见的,我们已经有了角色列表,所以我们真的只需要遍历我们的节点向量,对于每个条目,计算“新场景”之前出现的次数并加 1。我们可以通过以下方式做到这一点:
# number each scene
scene_count <- c()
for (i in 1:length(nodes)) {
scene_count[i] <- sum(grepl("New Scene", nodes[1:i])) + 1
}
然后,我们可以通过将三个向量放在一起并删除同一场景中任何重复的字符来最终确定我们的数据帧。我们还可以修正脚本以新场景开始的情况,我们可以一致地将角色名称格式化为标题大小写,以考虑不同的大小写类型:
library(tidyverse)
results <- data.frame(episode = 1, scene = scene_count, character = nodes) %>%
dplyr::filter(character != "New Scene") %>%
dplyr::distinct(episode, scene, character) %>%
dplyr::mutate(scene = scene - min(scene) + 1, # set first scene number to 1
character = character %>% tolower() %>% tools::toTitleCase()) # title case
让我们看看results
数据框架的一些输出示例:
归纳为每一季和每一集的函数
当然,我们这里的目的不是只刮第一集,而是刮每一季的每一集。一名优秀的数据科学家现在将寻求概括他们所做的工作,并将其放入一个函数中,该函数将接受一个季节编号和一集编号,然后抓取该集并返回我们刚刚构建的数据帧。
如果我们看看第一季第一集的格式,我们会发现它在大部分——尽管不是全部——剧集中重复出现。有一些例外:
- 第二季(第三集)的一些情节有不同的格式
- 整个第十季似乎使用了一些不同的 HTML 格式,格式出现在冒号之前,而不是之后。
因此,有必要用if else
语句扩展上面的代码,以考虑到我们知道格式会有些不同的情况。我们还需要尽可能地扩大我们的清理范围,以考虑到我们解析其他剧集时发生的我们没有预料到的事情。在抓取文本和清理意料之外的结果时,需要一点人力,这是很正常的。最终结果并非 100%无误也是很常见的。数据科学家必须确定什么时候结果足够接近他们需要的,什么时候清理剩余的 1-2%的错误不值得这么做。
这里是我的最后一集抓取功能,我很高兴做我需要的工作。你会看到代码锚围绕着我们上面所做的工作,但是把它扩展到所有的季节和剧集。
现在我们可以用这个简单的命令来抓取第九季第二集:
scrape_friends(9, 2)
这个功能将会变得非常有用,因为我们将会尝试根据出现在同一个场景中的角色来创建角色网络的边列表。在接下来的文章中,请留意这一点。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在 LinkedIn 或Twitter上找我。也可以看看我在drkeithmcnulty.com的博客。
《老友记》(NBC 提供)
从 PDF 表格中提取数据—使用 Python 中的一行代码
在本文中,您将学习使用 python 从 PDF 文件的表格中提取数据并导出到 CSV/Excel 的最佳方式。
戴维·克洛德在 Unsplash 上的照片
从 PDF 文件中获取表格不再是一项困难的任务,您可以使用 python 中的一行代码来完成。
你将学到什么
- 安装磁盘库。
- 正在导入库。
- 阅读 PDF 文件。
- 读取 PDF 文件特定页面上的表格。
- 读取 PDF 文件同一页面上的多个表格。
- 将 PDF 文件直接转换为 CSV 文件。
白板
Tabula 是一个有用的软件包,它不仅允许你从 PDF 文件中抓取表格,还可以将 PDF 文件直接转换成 CSV 文件。
所以让我们开始吧…
1.安装磁盘库
pip install tabula-py
2.导入 tabula 库
import tabula
3.阅读 PDF 文件
让我们把这个 PDF 剪切成熊猫数据帧。
图片由 Satya Ganesh 提供
file1 = "[https://nbviewer.jupyter.org/github/kuruvasatya/Scraping-Tables-from-PDF/blob/master/data1.pdf](https://nbviewer.jupyter.org/github/kuruvasatya/Scraping-Tables-from-PDF/blob/master/data1.pdf)"table = tabula.read_pdf(file1,pages=1)table[0]
看看上面在 Google Colabs 中执行的代码片段的输出
图片由 Satya Ganesh 提供
4.读取 PDF 文件特定页面上的表格。
假设我们需要废弃这个包含多页的 PDF 文件 。
图片由 Satya Ganesh 提供
图片由 Satya Ganesh 提供
file2 = "[https://nbviewer.jupyter.org/github/kuruvasatya/Reading-Table-Data-From-PDF/blob/master/data.pdf](https://nbviewer.jupyter.org/github/kuruvasatya/Reading-Table-Data-From-PDF/blob/master/data.pdf)"# To read table in first page of PDF file
table1 = tabula.read_pdf(file2 ,**pages=1**)# To read tables in secord page of PDF file
table2 = tabula.read_pdf(file2 ,**pages=2**)print(table1[0])
print(table2[0])
5.如果一个 PDF 文件的同一个页面上有多个表格怎么办?
假设我们需要抓取这两个表格,它们在一个 PDF 文件 的同一页上。
图片由 Satya Ganesh 提供
To read multiple tables we need to add extra parameter**multiple_tables = True** -> Read multiple tables as independent tables
**multiple_tables = False** -> Read multiple tables as single table
5.1.将多个表作为独立的表读取
file3 = "[https://nbviewer.jupyter.org/github/kuruvasatya/Reading-Table-Data-From-PDF/blob/master/data3.pdf](https://nbviewer.jupyter.org/github/kuruvasatya/Reading-Table-Data-From-PDF/blob/master/data3.pdf)"tables = tabula.read_pdf(file3 ,pages=1, **multiple_tables=True**)print(tables[0])
print(tables[1])
图片由 Satya Ganesh 提供
图片由 Satya Ganesh 提供
5.2 将多个表作为单个表读取
tables = tabula.read_pdf(file3 ,pages=1,**multiple_tables=False**)tables[0]
图片由 Satya Ganesh 提供
6.将 PDF 文件直接转换为 CSV 文件
我们可以使用 tabula 库中的 convert_into() 方法将包含表格数据的 PDF 文件直接转换为 CSV 文件。
1.将一页 PDF 文件中的表格转换为 CSV 格式
# output just the first page tables in the PDF to a CSVtabula.convert_into("pdf_file_name", "Name_of_csv_file.csv")
2.将 PDF 文件中的所有表格转换为 CSV 格式
tabula.convert_into("pdf_file_name","Name_of_csv_file.csv",**all****=****True**)
结论
我希望您学会了一种使用 python 中的一行代码来抓取 PDF 文件表格的好方法。
查看我的相关文章
[## 从网站抓取表格数据—使用 Python 中的一行代码
本文将教您如何使用 python 中的一行代码从网站中抓取表格数据。
towardsdatascience.com](/scraping-table-data-from-websites-using-a-single-line-in-python-ba898d54e2bc) [## 新冠肺炎的影响-使用 Python 进行数据可视化
使用 python 在印度地图上可视化冠状病毒爆发的初级方法。当你到达…的终点时
towardsdatascience.com](/impact-of-covid-19-data-visualization-using-python-6f8e3bdc860b)
参考
[## 表格:将 PDF 中的表格读入数据框架表格文档
是 tabula-java 的一个简单的 Python 包装器,可以读取 PDF 的表格。您可以从 PDF 读取表格并转换成…
tabula-py.readthedocs.io](https://tabula-py.readthedocs.io/en/latest/)
感谢阅读😃过得愉快
从网站抓取表格数据—使用 Python 中的一行代码
本文将教您使用 python 中的一行代码从网站中抓取表格数据的最佳方式。
米卡·鲍梅斯特在 Unsplash 上的照片
介绍
D 如今,ata 已经成为最有价值的货币和珍贵商品,你使用它的方式将使你与普通人有所不同。您需要足够聪明来获取这些数据,这些数据在您身边随处可见,在本文中,您将能够学习一种简单的方法,使用 python 中的一行代码从任何网站获取表格数据。
对于数据收集,“越快越好”永远是最好的答案——玛丽莎·梅耶尔(雅虎前首席执行官!)
每天都有大量公开生成的数据,最常见的数据类型之一是 HTML 表格,有许多方法可以将这些数据放入笔记本,如使用 urllib 和 BeautifulSoup 但这是一个漫长而耗时的过程,您可以在一行代码中使用 pandas 库来完成相同的任务。
我们开始吧
Pandas 库中的 read_html() 方法是一个 web 抓取工具,只需将所需的 URL 作为参数提供给该方法,就可以提取网站上的所有表格。
先决条件:导入熊猫库
import pandas as pd
1。获取网站上的所有表格
只需给出一个 URL 作为参数,就可以获得该特定网站上的所有表格。
pandas.**read_html**(URL)
例子:从维基百科中读取数据
#Data about Cricket World cupURL = ["https://en.wikipedia.org/wiki/Cricket_World_Cup](https://en.wikipedia.org/wiki/Cricket_World_Cup)"tables = pd.read_html(URL)print("There are : ",len(tables)," tables")
print("Take look at table 0")
print(tables[0])
2.从网站上读取特定的表格
如果一个网站上有很多表格,找到你要找的表格将会是一项繁琐的任务,你不能只手动搜索一个表格。为此,我们需要在 read_html() 方法中添加一个额外的参数( match) 来自动搜索所需的表。
match 属性可以将一个字符串或一个编译的正则表达式作为它的值
pandas.**read_html**(URL,match = ".+")
示例:使用 match 作为参数来抓取特定的表格
URL = "https://en.wikipedia.org/wiki/Cricket_World_Cup"tables = pd.read_html(URL,match="Performance details")print("There are : ",len(tables)," tables")print("Take look at table 0")tables[0]
由萨特雅·加内什创作
**注意:**搜索空间缩小,这可能有助于您快速搜索所需的表
3.格式化输出
Useful attributes
1\. header : The row to use to make as the column header.
2\. index_col : The column to use to create the index
3\. skiprows : Number of rows to skip after parsing column integer
4。您也可以通过另一种方式瞄准特定的表格
pandas.**read_html**(URL,attrs = {'html_tag' : 'value'})
我们可以通过检查表格来直接定位对应于所需表格的 HTML 标签。
如何检查表格?
左键点击工作台并选择检查选项
图片由 Satya Ganesh 提供
在检查了查找该表专用的相关标签后,我们可以看到class:'wikitable'
是一个标识该表的标签。
图片由 Satya Ganesh 提供
#By using this line of code we can hit the target table directly
pandas.**read_html**(URL,attrs = {'class' : 'wikitable'})
结论
读完这篇文章后,你将能够很快从任何网站上收集表格数据。
参考
[## 维基百科(一个基于 wiki 技术的多语言的百科全书协作计划ˌ也是一部用不同语言写成的网络百科全书ˌ 其目标及宗旨是为全人类提供自由的百科全书)ˌ开放性的百科全书
维基百科是一个免费的在线百科全书,由世界各地的志愿者创建和编辑,由维基媒体托管…
www.wikipedia.org](https://www.wikipedia.org/) [## pandas.read_html - pandas 0.23.4 文档
pandas.pydata.org](https://pandas.pydata.org/pandas-docs/version/0.23.4/generated/pandas.read_html.html)
你可以在这里找到它的应用…
使用 python 在印度地图上可视化冠状病毒爆发的初级方法。当你到达…的终点时
towardsdatascience.com](/impact-of-covid-19-data-visualization-using-python-6f8e3bdc860b)
感谢阅读😃过得愉快
抓取没有文本的表格
有时你需要的数据是用颜色显示的,而不是文本。下面是获取数据的方法
安德鲁·雷德利在 Unsplash 上的照片
过去两周,我讨论了如何从维基百科的某些表格中抓取文本。两周前,我在这里展示了如何从维基百科抓取部分表格,上周,我在这里展示了如何从多个页面抓取数据。这一周,我们将解决一个稍微不同的与桌子相关的问题:颜色。
如果你只是想从维基百科中抓取一个完整的表格,你可以很容易地使用这个站点来生成一个 csv 文件。当然,你也可以在 python 中使用 Requests 和 BeautifulSoup 来做到这一点,但是坦率地说,当你有一个完全可行的选择时,没有必要重新发明轮子。然而,本周我们将关注英国烘焙大赛第一季的淘汰赛。
首先,让我们展示一下为什么使用 csv 网站行不通。当您在网站中运行 url 并查看为淘汰图表生成的 csv 时,您会看到以下内容:
Baker,1,2,3,4,5,6
Edd,,,,,,WINNER
Ruth,,,,,,Runner-up
Miranda,,,,,,Runner-up
Jasminder,,,,,OUT,
David,,,,OUT,,
Jonathan,,,OUT,,,
Annetha,,OUT,,,,
Louise,,OUT,,,,
Lea,OUT,,,,,
Mark,OUT,,,,,
这是表格在实际页面上的样子:
https://en . Wikipedia . org/wiki/The _ Great _ British _ Bake _ Off _(series _ 1)
显然,csv 文件未能捕捉到此图表上的颜色,正如我们在颜色键中看到的,这些颜色传达了潜在的有价值的信息:
因此,我们必须进入代码以获取所有信息。首先,和往常一样,我们将通过 BeautifulSoup 运行 url,这样我们就可以浏览 html:
import requests
from bs4 import BeautifulSoup
import pandas as pd
import reurl = '[https://en.wikipedia.org/wiki/The_Great_British_Bake_Off_(series_1)'](https://en.wikipedia.org/wiki/The_Great_British_Bake_Off_(series_1)')page = requests.get(url)soup = BeautifulSoup(page.content)
现在,我们将分离出排除图:
这几行实际上是说,“看看所有的表格,如果标题文本中有‘消除图表’,这就是我们定义的‘elim _ Chart’”
接下来,我们将制作一个包含每列标题的列表。如果我们看一下图表,这个列表应该是这样的:
['Baker', '1', '2', '3', '4', '5', '6']
要生成该列表,代码如下:
这些行实际上是说,“对于清除图表中标记为 th 的项目,只要项目的文本不以‘Elim’开头(因为我们不想删除‘清除图表’的较大标题),就将每个项目的文本添加到标题列表中”。rstrip()被添加到末尾,因为在初始列表中,6 后面有一个换行符,这会不必要地使事情复杂化。
换一种方式,我们将查看颜色键并创建一个字典,以便稍后在创建表格时使用。
meaning_list = soup.find('b', text=re.compile('Colour key:')).find_all_next('dd', limit = 6)
这一行基本上抓取了颜色键中显示的所有颜色信息和分配给它们的文本。通过在搜索文本时使用 re.compile,您不需要有精确的匹配,当您考虑到潜在的不可见字符(如空格和换行符)时,这可以使您的生活容易得多。从这里开始,我们通过执行以下操作来研究该列表:
首先,我们创建两个列表,color_list,它是颜色键中所有颜色的列表,以及 meaning_text_list,它是分配给每种颜色的文本的列表。我们通过对含义列表中每个项目的样式部分进行重新分割来访问颜色,使短语正好在颜色之前,background-color:,一个非捕获组,然后直接捕获它后面的单词。从这里开始,我们将颜色列表中的所有单词全部小写,以避免由于大小写不一致而导致的任何潜在的不匹配。为了获得每个颜色含义的文本,我们分割项目的文本并捕捉破折号后的单词,然后获取结果列表中的最后一个项目。从那里我们将这两个列表压缩到一个字典中,full_text_meaning_dict:
{'lightblue': 'Baker got through to the next round',
'orangered': 'Baker was eliminated',
'plum': "Baker was one of the judges' least favourite bakers that week, but was not eliminated",
'cornflowerblue': "Baker was one of the judges' favourite bakers that week",
'limegreen': 'Baker was a series runner-up',
'yellow': 'Baker was the series winner'}
这样做的一个问题是在处理数据时这些值太长了。可能有更有效的方法,但我只是创建了以下列表:
shortened_text_list = ['next_round','eliminated','least_fav','fav','runner_up','winner',]
然后我压缩了列表和颜色列表,得到了这本字典:
{'lightblue': 'next_round',
'orangered': 'eliminated',
'plum': 'least_fav',
'cornflowerblue': 'fav',
'limegreen': 'runner_up',
'yellow': 'winner'}
然后我添加了以下条目,silver 定义为 not_in_comp(不在比赛中),因为它不包含在颜色键中,但在查看表格时是必需的:
{'silver':'not_in_comp'}
从这里我们可以回到我们的淘汰表。这里提醒一下我们正在看的东西:
我们将通过执行以下操作来清理该表:
太多了,让我们从头到尾看一遍。首先,我们从创建一个行列表开始。这个行列表将是一个字典列表,其中键将是标题,值将是每一行的信息。
然后在接下来的三行中,我们开始查找所有面包师的名字,这是每行的第一项。从这里我们开始一个列表,row,它包含了我们访问过的每个条目的文本,也就是名称。从那里,我们得到每个名字的下六个兄弟姐妹。这六个兄弟姐妹对应于代表每集选手表现的六个不同的列。
从这里开始,我们查看每一行中的每一项(第 7 行)。对于每个项目,我们访问该项目的颜色,并且我们有一个设置为 1 的 col 变量。我们有这个 col 变量,因为有时,如果一个参赛者在连续两集里得到相同的结果(例如,他们连续两周是评委最喜欢的),代码只会说“创建一个两列宽的评委最喜欢的颜色块。”然而,当我们试图重新创建一个表,并且每行需要六个条目时,事情就变得复杂了。因此,第 12 行和第 13 行表示,“如果代码指定颜色块的宽度大于一列,则使 col 变量等于颜色块跨越的列数。”然后,第 15–17 行将该颜色添加到行列表中,方法是对第 9 行创建的颜色变量进行切片,以便只访问颜色名称,然后在我们创建的定义每种颜色含义的字典中运行该颜色,然后将该颜色乘以 col 变量。这确保了我们创建的每个行列表长度相同,并且表示重复的结果。最后,我们得到一个类似这样的行列表:
[{'Baker': 'Edd',
'1': 'next_round',
'2': 'fav',
'3': 'fav',
'4': 'least_fav',
'5': 'fav',
'6': 'winner'},
{'Baker': 'Ruth',
'1': 'fav',
'2': 'next_round',
'3': 'fav',
'4': 'fav',
'5': 'fav',
'6': 'runner_up'},
{'Baker': 'Miranda',
'1': 'fav',
'2': 'fav',
'3': 'next_round',
'4': 'fav',
'5': 'least_fav',
'6': 'runner_up'},
{'Baker': 'Jasminder',
'1': 'next_round',
'2': 'next_round',
'3': 'least_fav',
'4': 'fav',
'5': 'eliminated',
'6': 'not_in_comp'},
{'Baker': 'David',
'1': 'least_fav',
'2': 'least_fav',
'3': 'least_fav',
'4': 'eliminated',
'5': 'not_in_comp',
'6': 'not_in_comp'},
{'Baker': 'Jonathan',
'1': 'next_round',
'2': 'fav',
'3': 'eliminated',
'4': 'not_in_comp',
'5': 'not_in_comp',
'6': 'not_in_comp'},
{'Baker': 'Annetha',
'1': 'fav',
'2': 'eliminated',
'3': 'not_in_comp',
'4': 'not_in_comp',
'5': 'not_in_comp',
'6': 'not_in_comp'},
{'Baker': 'Louise',
'1': 'least_fav',
'2': 'eliminated',
'3': 'not_in_comp',
'4': 'not_in_comp',
'5': 'not_in_comp',
'6': 'not_in_comp'},
{'Baker': 'Lea',
'1': 'eliminated',
'2': 'not_in_comp',
'3': 'not_in_comp',
'4': 'not_in_comp',
'5': 'not_in_comp',
'6': 'not_in_comp'},
{'Baker': 'Mark',
'1': 'eliminated',
'2': 'not_in_comp',
'3': 'not_in_comp',
'4': 'not_in_comp',
'5': 'not_in_comp',
'6': 'not_in_comp'}]
从这里开始,我们要做的就是把它变成一个真正的熊猫餐桌,如下所示:
df = pd.DataFrame.from_dict(row_list)df.set_index('Baker', *inplace*=True)
注意,我将 baker 列设置为索引,因为这对我更有意义。这段代码生成了下表:
现在,这实际上是直接从页面中抓取表格。然而,如果您想要对这些数据进行任何类型的分析,您将不得不创建虚拟变量。如果你不确定虚拟变量是什么,它本质上是一种表示分类数据的数字方式(在这种情况下,一个参赛者在一集里的表现)。在熊猫中,有一个函数叫做 get_dummies。如果我们这样做:
pd.get_dummies(df)
我们会得到这个:
注意:这只是生成的 25 列的一个示例
第一列中的 1 代表那个人是否在第一集被淘汰,第二列代表那个人是否是评委的最爱,等等。然而,假设你想把焦点放在某个选手何时是最受欢迎的或者刚刚进入下一轮。要做到这一点,可以翻转数据框,使索引变成列,然后执行我前面展示的 get_dummies 函数。在代码中,翻转看起来像这样:
t_df = df.transpose()
数据帧看起来像这样:
然后创建虚拟变量,如下所示:
pd.get_dummies(t_df)
这是结果:
从这里开始,您就有了可以处理和分析的数据(尽管六行数据可能并不多)。这就是我们如何获取丰富多彩的非文本表格,并将其处理成实际有价值的数据。
用熊猫抓取表格数据
编程;编排
使用 Python 和 Pandas 进行网页抓取
eb 抓取是一种从网站获取数据的技术。BeautifulSoup 和 Scrapy 是 Python 中两个广泛用于执行 Web 抓取的库。然而,使用这些库可能很麻烦,因为我们需要找到元素标签,从它们中提取文本,然后清理数据。
本文将向您展示一种使用 Pandas 提取表格数据的简单方法。是啊!熊猫!
从 HTML 页面提取表格
对于本教程,我们将从这个维基百科页面中摘录全球十大亿万富翁的详细信息。
我们将使用 Pandas 库的[read_html](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_html.html)
方法来读取 HTML 表。
import pandas as pd
url = 'https://en.wikipedia.org/wiki/The_World%27s_Billionaires'
df_list = pd.read_html(url)
这个脚本将 HTML 表格返回到 DataFrame 对象列表中。
让我们检查找到的表的总数:
len(df_list)
# Output:
# 32
要访问特定表,只需访问列表中的元素。例如,
将返回下表:
将特定列设置为索引
我们可以通过使用index_col
参数来选择表索引的特定列。
例如:
pd.read_html(url, index_col=1)[2]
返回下表:
返回包含字符串或正则表达式的表格
我们还可以使用match
参数指定返回包含特定字符串或正则表达式的表列表。
举例:
pd.read_html(url, match='Number and combined net worth of billionaires by year')[0].head()
指定要识别为不适用的字符串
我们可以通过使用na_values
参数来指定要识别为 NA/NaN 的字符串列表。
例如:
- 未指定
na_values
:
pd.read_html(url)[0].tail()
不指定 na_values
- 指定
na_values
后:
pd.read_html(
url,
na_values=["Forbes: The World's Billionaires website"]
)[0].tail()
指定 na_values 后
其他参数
skiprows
参数允许我们跳过开始的“n”行header
参数可以用来使指定的行作为列标题
例如:
pd.read_html(url, skiprows=3, header=0)[0].head()
结论
在本文中,我们学习了如何使用read_html
方法轻松地从页面中抓取 HTML 表格。此外,我们还学习了一些重要的参数,这些参数可以进一步帮助我们抓取所需的表。
参考
资源
本文中使用的代码片段可以在我的 GitHub 页面上找到。
让我们连接
领英:https://www.linkedin.com/in/jimit105/
GitHub:https://github.com/jimit105
推特:https://twitter.com/jimit105