IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> git-history:一款用于分析保存在Git和SQLite中的网页抓取数据的工具 -> 正文阅读

[开发工具]git-history:一款用于分析保存在Git和SQLite中的网页抓取数据的工具

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

如何分析这些收集到的数据是个公认的难题。git-history正是我为解决这个难题而设计的工具。

Git scraping技术回顾

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

这里有一个具体的例子:加州林业与消防局(Cal Fire)在fire.ca.gov/incidents网站上维护了一张火灾地图,地图展示了该州近期的大型火灾情况。

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

curl https://www.fire.ca.gov/umbraco/Api/IncidentApi/GetIncidents

然后我搭建了一个简单的抓取工具,每20分钟抓取一份网站数据并提交至Git。到现在,这个工具已经运行了14个月,收集了1559个提交版本。

Git scraping最让我兴奋的是,它可以创建真正独一无二的数据集。很多组织都不会对数据变更内容和位置做详细归档,所以通过抓取他们的网站数据并保存到Git存储库,你会发现自己比他们更了解他们的数据变更历史。

然而,一个巨大的挑战是,如何最有效地分析这些收集到的数据?面对成千上万个版本、大量的JSONCSV文档,如果只靠肉眼观察不同之处,想必很难挖掘出数据背后的价值。

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"]'

在这个例子中,我们抓取的是incidents.json这个文档的历史版本。

我们通过UniqueId列来识别随时间变化的记录和新增记录。

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

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

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

incident表包含每场火灾的最新记录。通过这张表,我们可以得到一张关于所有火灾的地图:

这里用到了datasette-cluster-map插件,它把表中给出有效经度和纬度值的所有行都标注在了地图上。

真正有趣的是incident_version这张表。这张表记录了每场火灾历次抓取版本间的数据更新情况。

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

  • Dixie Fire:268
  • Caldor Fire:153
  • Monument Fire 65
  • August Complex(包括Doe Fire):64
  • Creek Fire:56
  • French Fire:53
  • Silverado Fire:52
  • Fawn Fire:45
  • Blue Ridge Fire:39
  • McFarland Fire:34

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

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

git-history在这张表中只写入了与前一版本相比发生变化的值。因此一眼扫过去你就能看到哪些信息随时间变化了:

经常变化的是ConditionStatement列,这一列是文字描述,另外两个有意思的列是AcresBurned和PercentContained。

_commitcommits表的外键,该表记录了工具已提交的版本,所以当你再次运行工具的时候,工具能定位上次提交到哪个版本。

连接commits表即可查看每个版本的创建日期。也可以用incident_version_detail视图执行连接操作。

通过该视图,我们可以筛选_item值为174AcresBurned值不为空的所有行,借助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。

然而这个设计有个隐患,即如果某场火灾某一列的值更新为了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

查询结果如下:

  • 更新: 1785
  • 已扑灭的火灾占比:740
  • 状况说明:734
  • 火灾面积: 616
  • 开始时间: 327
  • 受灾人员:286
  • 灭火泵:274
  • 消防人员:256
  • 消防车:225
  • 无人机: 211
  • 消防飞机:181
  • 建筑物损毁:125
  • 直升机: 122

直升机听起来很刺激!我们来筛选出第一个版本之后直升机数量有过至少一次更新的火灾。可以使用如下嵌套的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抓取工具持续从511.org网站抓取数据并保存到dbreunig/511-events-history存储库。该网站记录旧金山湾区的交通事故。我将他的数据加载到了sf-bay-511数据库。

以sf-bay-511这个数据库作为示例有助于我们深入了解git-history与--convert选项叠加的用法。

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

)

但抓取的数据通常不是这种理想格式。

我找到了511.org网站的JSON Feed。这里面有非常复杂的嵌套对象,数据庞杂,其中一些对整体分析没有帮助,比如即使没有数据更新也会随版本变化的updated时间戳,还有包含大量重复数据的深度嵌套对象“extension”。

我编写了一段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文件夹中。

GitHubgit-scraping话题下面,很多人创建了存储库,目前已有200多个。那里有丰富的抓取数据等着你去探索!
稿件来源:https://simonwillison.net/2021/Dec/7/git-history/

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-12-23 15:56:13  更:2021-12-23 15:57:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 14:56:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码