网页数据抓取怎么写(Gitscraping技术回顾抓取数据到Git存储库的一大优势)

优采云 发布时间: 2021-12-26 10:02

  网页数据抓取怎么写(Gitscraping技术回顾抓取数据到Git存储库的一大优势)

  大多数人都知道 Git 抓取,这是一种用于网页抓取工具的编程技术。您可以定期将数据源的快照抓取到 Git 存储库,以跟踪数据源随时间的变化。

  如何分析采集

到的数据是一个公认的问题。git-history 是我设计用来解决这个问题的工具。

  Git 抓取技术回顾

  将数据抓取到 Git 存储库的一大优势在于抓取工具本身非常简单。

  下面是一个具体的例子:加州林业和消防局 (Cal Fire) 在 /incidents 网站上维护了一张火灾地图,该地图显示了最近*敏*感*词*火灾的状态。

  我找到了网站的底层数据:

  卷曲

  然后我搭建了一个简单的爬虫工具,每20分钟爬一次一个网站的数据,提交给Git。到目前为止,该工具已经运行了14个月,已经采集

了1559个提交版本。

  Git 抓取最让我兴奋的是它可以创建真正独特的数据集。许多组织没有详细归档数据更改的内容和位置,因此通过抓取他们的网站数据并将其保存到 Git 存储库,您会发现您比他们更了解他们的数据更改历史。

  然而,一个巨大的挑战是如何最有效地分析采集

到的数据?面对上千个版本和海量的JSON和CSV文档,如果仅靠肉眼观察差异,很难挖掘出数据背后的价值。

  git-history

  git-history 是我的新解决方案,它是一个命令行工具。它可以读取文件的所有历史版本并生成 SQLite 数据库来记录文件随时间的变化。然后你就可以使用Datasette来分析和挖掘这些数据。

  下面是通过使用 ca-fires-history 存储库运行 git-history 生成的数据库示例。我通过在存储库目录中运行以下命令创建了一个 SQLite 数据库:

  git-history file ca-fires.db incidents.json \

--namespace incident \

--id UniqueId \

--convert 'json.loads(content)["Incidents"]'

  

  在这个例子中,我们获取了文件 events.json 的历史版本。

  我们使用 UniqueId 列来标识随时间变化的记录和新记录。

  新创建的数据库表的默认名称是item和item_version,我们通过--namespace event将表名指定为incident和incident_version。

  工具中还嵌入了一段Python代码,可以将提交历史中存储的每个版本转换为与工具兼容的对象列表。

  让数据库帮助我们回答一些关于过去 14 个月加州火灾的问题。

  事件表收录

每次火灾的最新记录。从这张表中,我们可以得到一张所有火灾的地图:

  

  这里使用了 datasette-cluster-map 插件,它在地图上标记了表中具有有效经纬度值的所有行。

  真正有趣的是 event_version 表。该表记录了每次火灾的先前捕获版本之间的数据更新。

  250 场火灾有 2,060 个记录版本。如果根据_item进行分面,我们可以看到哪些火灾记录的版本最多。前十名依次为:

  版本越多,火持续的时间越长。维基百科上甚至还有 Dixie Fire 的条目!

  点击Dixie Fire,在弹出的页面中可以看到所有抓到的“版本”按版本号排列。

  git-history 只在此表中写入与之前版本相比发生变化的值。因此,一目了然,您可以看到哪些信息随时间发生了变化:

  

  经常变化的是 ConditionStatement 列。此栏是文字说明。另外两个有趣的列是 AcresBurned 和 PercentContained。

  _commit 是提交表的外键。该表记录了该工具已提交的版本,当您再次运行该工具时,该工具可以定位到上次提交的是哪个版本。

  连接到提交表以查看每个版本的创建日期。您也可以使用incident_version_detail 视图来执行连接操作。

  通过这个视图,我们可以过滤所有_item值为174、AcresBurned值不为空的行,借助datasette-vega插件,_commit_at列(日期类型)和AcresBurned列(数字类型) ) 比较形成一张图表,直观地显示了 Dixie Fire 火灾随时间的蔓延情况。

  

  总结一下:让我们首先使用 GitHub Actions 创建一个定时工作流,并每 20 分钟获取一次 JSON API 端点的最新副本。现在,在 git-history、Datasette 和 datasette-vega 的帮助下,我们已经成功地用图表展示了过去 14 个月加州最长的森林火灾的蔓延情况。

  关于表结构设计

  在git-history的设计过程中,最难的就是设计一个合适的表结构来存储之前的版本变更信息。

  我的最终设计如下(为清晰起见进行了适当编辑):

  CREATE TABLE [commits] (

[id] INTEGER PRIMARY KEY,

[hash] TEXT,

[commit_at] TEXT

);

CREATE TABLE [item] (

[_id] INTEGER PRIMARY KEY,

[_item_id] TEXT,

[IncidentID] TEXT,

[Location] TEXT,

[Type] TEXT,

[_commit] INTEGER

);

CREATE TABLE [item_version] (

[_id] INTEGER PRIMARY KEY,

[_item] INTEGER REFERENCES [item]([_id]),

[_version] INTEGER,

[_commit] INTEGER REFERENCES [commits]([id]),

[IncidentID] TEXT,

[Location] TEXT,

[Type] TEXT

);

CREATE TABLE [columns] (

[id] INTEGER PRIMARY KEY,

[namespace] INTEGER REFERENCES [namespaces]([id]),

[name] TEXT

);

CREATE TABLE [item_changed] (

[item_version] INTEGER REFERENCES [item_version]([_id]),

[column] INTEGER REFERENCES [columns]([id]),

PRIMARY KEY ([item_version], [column])

);

  前面提到,item_version表记录了不同时间点的网站快照,但为了节省数据库空间,提供简洁的版本浏览界面,这里只记录了与之前版本相比发生变化的列。所有未更改的列都写为空。

  但是这种设计有一个隐患,那就是如果某列的值在某次火灾中被更新为null,我们该怎么办?我们如何判断它是否已更新或未更改?

  为了解决这个问题,我添加了一个多对多表item_changed,它使用整数对来记录item_version表中哪些列更新了内容。使用整数对的目的是尽可能少占用空间。

  item_version_detail 视图将多对多表中的列显示为 JSON。我过滤了一些数据,放在下图中,看看哪些列更新了哪些版本:

  

  通过下面的SQL查询,我们可以知道加州火灾中哪些数据更新最频繁:

  select columns.name, count(*)

from incident_changed

join incident_version on incident_changed.item_version = incident_version._id

join columns on incident_changed.column = columns.id

where incident_version._version > 1

group by columns.name

order by count(*) desc

  查询结果如下:

  直升机听起来令人兴奋!让我们过滤掉第一个版本后直升机数量至少更新一次的火灾。您可以使用以下嵌套 SQL 查询:

  select * from incident

where _id in (

select _item from incident_version

where _id in (

select item_version from incident_changed where column = 15

)

and _version > 1

)

  查询结果显示,直升机出动火灾19次,我们在下图标注:

  

  --convert 选项的高级用法

  在过去的 8 个月里,Drew Breunig 使用 Git 爬虫不断从网站上爬取数据并将其保存到 dbreunig/511-events-history 存储库中。本网站记录旧金山湾区的交通事故。我将他的数据加载到 sf-bay-511 数据库中。

  以sf-bay-511数据库为例,帮助我们理解git-history和--convert选项overlay的用法。

  git-history 要求捕获的数据为以下特定格式:由 JSON 对象组成的 JSON 列表,每个对象都有一个列,可以作为唯一标识列来跟踪数据随时间的变化。

  理想的 JSON 文件如下所示:

  select * from incident

where _id in (

select _item from incident_version

where _id in (

select item_version from incident_changed where column = 15

)

and _version > 1

)

  但是,捕获的数据通常不是这种理想格式。

  我找到了网站的 JSON 提要。有非常复杂的嵌套对象,数据也很复杂,有些对整体分析没有帮助,比如更新的时间戳即使没有数据更新也会随着版本的变化而变化,深度嵌套的对象“扩展”收录

大量重复数据。.

  我写了一段 Python 代码将每个网站快照转换成更简单的结构,然后将这段代码传递给脚本的 --convert 选项:

  #!/bin/bash

git-history file sf-bay-511.db 511-events-history/events.json \

--repo 511-events-history \

--id id \

--convert '

data = json.loads(content)

if data.get("error"):

# {"code": 500, "error": "Error accessing remote data..."}

return

for event in data["Events"]:

event["id"] = event["extension"]["event-reference"]["event-identifier"]

# Remove noisy updated timestamp

del event["updated"]

# Drop extension block entirely

del event["extension"]

# "schedule" block is noisy but not interesting

del event["schedule"]

# Flatten nested subtypes

event["event_subtypes"] = event["event_subtypes"]["event_subtype"]

if not isinstance(event["event_subtypes"], list):

event["event_subtypes"] = [event["event_subtypes"]]

yield event

'

  传递给 --convert 的单引号字符串被编译成 Python 函数,并在每个 Git 版本上依次运行。代码在 Events 嵌套列表中循环运行,修改每条记录,然后使用 yield 以可迭代序列输出。

  一些历史记录显示服务器 500 错误,代码还可以识别和跳过这些记录。

  在使用 git-history 时,我发现我大部分时间都花在了迭代转换脚本上。将 Python 代码字符串传递给 git-history 等工具是一个非常有趣的模型。今年早些时候,我还尝试在 sqlite-utils 工具中覆盖转换。

  试一试

  如果你想尝试 git-history 工具,扩展文档 README 中有更多选项。示例中使用的脚本都保存在 demos 文件夹中。

  在 GitHub 上的 git-scraping 话题下,已经有很多人创建了仓库,目前有 200 多个仓库。海量爬取数据等你探索!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线