大多数人都知道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存储库,你会发现自己比他们更了解他们的数据变更历史。

然而,一个巨大的挑战是,如何最有效地分析这些收集到的数据?面对成千上万个版本、大量的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"]'

在这个例子中,我们抓取的是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。

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

连接commits表即可查看每个版本的创建日期。也可以用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。

然而这个设计有个隐患,即如果某场火灾某一列的值更新为了null,我们该怎么办?我们如何分辨是更新还是没变化?

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

item_version_detail视图将多对多表中的列以JSON的方式呈现出来,我筛选了部分数据放在下图中,可以看出哪些火灾的哪些列在哪些版本中有更新:

通过下面这个SQL查询,我们能知道加州火灾哪些数据更新最频繁:

select columns.name, count(*)from incident_changedjoin incident_version on incident_changed.item_version = incident_version._idjoin columns on incident_changed.column = columns.idwhere incident_version._version > 1group by columns.nameorder by count(*) desc

查询结果如下:

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

直升机听起来很刺激!我们来筛选出第一个版本之后直升机数量有过至少一次更新的火灾。可以使用如下嵌套的SQL查询:

select * from incidentwhere _id in (select _item from incident_versionwhere _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 incidentwhere _id in (select _item from incident_versionwhere _id in (select item_version from incident_changed where column = 15)and _version > 1)

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

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

我编写了一段Python代码,把每一个网站快照转换为一个较简单的结构,再把这段代码传递到脚本的--convert选项:

#!/bin/bashgit-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..."}returnfor event in data["Events"]:event["id"] = event["extension"]["event-reference"]["event-identifier"]# Remove noisy updated timestampdel event["updated"]# Drop extension block entirelydel event["extension"]# "schedule" block is noisy but not interestingdel event["schedule"]# Flatten nested subtypesevent["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多个。那里有丰富的抓取数据等着你去探索!
稿件来源:https://simonwillison.net/2021/Dec/7/git-history/

git-history:一款用于分析保存在Git和SQLite中的网页抓取数据的工具相关推荐

  1. 系统检测到您疑似使用网页抓取工具访问本_12款最常使用的网络爬虫工具推荐...

    网络爬虫在当今的许多领域得到广泛应用.它的作用是从任何网站获取特定的或更新的数据并存储下来.网络爬虫工具越来越为人所熟知,因为网络爬虫简化并自动化了整个爬取过程,使每个人都可以轻松访问网站数据资源.使 ...

  2. 11.Scrapy框架基础-使用Scrapy抓取数据并保存到mongodb

    目录 一.Scrapy安装 1.mac系统 2.windows系统 二.使用scrapy爬取数据 1.新建一个scrapy工程 2.在spiders下新建一个爬虫文件 3.提取网页数据 三.保存数据到 ...

  3. Fiddler不能抓取数据原因分析Windows11

    Windows11下载Fiddler不能抓取数据除了常见的添加证书.设置系统代理外,还有个原因是Intrernet Explorer. Fiddler配置一切正常后使用Chrome.Edge.Fire ...

  4. 使用php简单网页抓取和内容分析,PHP抓取分析网页的方法

    这篇文章主要介绍了PHP抓取及分析网页的方法,结合实例形式详细分析了php网页抓取的原理及分析的技巧,需要的朋友可以参考下 本文实例讲述了PHP抓取及分析网页的方法.分享给大家供大家参考,具体如下: ...

  5. 安居客检测到网页抓取_原创内容不收录 解决网站抓取异常的有效分析方法

    有的网站明明内容优质原创,用户可以正常访问,但偏偏网络蜘蛛无法正常访问抓取导致无法被收录,搜索结果覆盖率偏低,对搜索引擎和网站都是一种损失,这种情况就是抓取异常.对于大量内容无法正常抓取的网站,搜索引 ...

  6. python爬虫---实现项目(二) 分析Ajax请求抓取数据

    这次我们来继续深入爬虫数据,有些网页通过请求的html代码不能直接拿到数据,我们所需的数据是通过ajax渲染到页面上去的,这次我们来看看如何分析ajax 我们这次所使用的网络库还是上一节的Reques ...

  7. wincurl:一款基于HTTP协议的轻量级web资源抓取和上传工具

    编写web程序经常要进行接口调测,通常我们会使用curl或者postman等工具,通过这些工具可以方便的发送GET或POST请求来验证接口的正确与否. 对于复杂的接口业务,我们可以通过这些工具构造po ...

  8. 安居客检测到网页抓取_安居客天津租房情况分析

    本篇文章通过对安居客官网天津市租房信息的进行抓取,然后对房价信息进行了简单的描述性统计分析. 工具和技术:VBA知识.Power Bi 一.采集数据过程抓取思路: 安居客租房信息网址为简单的静态网页, ...

  9. 2dpsk调制解调实验matlab_ila抓取数据,matlab分析,调试AD9361信号通路

    关注.星标公众号,直达精彩内容 公众号:ZYNQ 作者:watchman AD9361功能介绍 上次我们介绍了AD9361的配置,代码,采样,脚本转换,软件使用等等,今天我们来调试并使用ila抓取实测 ...

最新文章

  1. 在CentOS 7.7 x86_64上安装python3.7.7
  2. 《皇帝:中国的崛起》从入门到精通
  3. python之迭代锁与信号量
  4. python项目NoReverseMatch: Reverse for ‘topic‘ with arguments ‘(‘‘,)‘ not found解决方法
  5. 【Linux】一步一步学Linux——sudo命令(105)
  6. 实测哈啰电动车A80探索版:一辆带你行走的聪明“小神兽”
  7. ORACLE时间常用函数(字段取年、月、日、季度)
  8. Redis学习手册(List数据类型)(转)
  9. Java 原生 Base64 编解码、Md5、SHA-1、SHA-256 加密摘要算法
  10. 信号与线性系统分析(吴大正,郭宝龙)(3-单位脉冲/阶跃序列以及4-信号的运算)
  11. xsmax进入dfu模式_DFU模式是什么?苹果XR/XS Max的DFU模式进入与退出方法[多图]
  12. mysql 存储百分数_mysql中如何存储百分数
  13. 正弦波的生成及混频器
  14. 快速删除大文件利器 node 包 rimraf
  15. 为什么架构师工资比运维高?
  16. 如何学IO流IO流的含义
  17. python 可执行文件大_python – 如何使用pyinstaller创建最小大小的可执行文件?
  18. 你觉得程序员适合干一辈子吗?
  19. SJA1000的调试经历【转】
  20. C语言入门part4—大致梳理最终篇

热门文章

  1. C++基础之string类型
  2. [联想]vware打开虚拟机就一直蓝屏关机
  3. 手机技巧:微信发布 8.0.35 安卓内测版体验和下载
  4. php中tp_thinkphp中M()方法有什么用
  5. 九宫格摆法_合战忍者村九宫格布局摆法介绍
  6. springboot xss
  7. Web漏洞扫描-Appscan安装配置及扫描
  8. 华为OD机试真题-查找充电设备组合【2023Q1】【JAVA、Python、C++】
  9. java中main方法的作用是什么_java中main方法是什么
  10. 关于博客搬家及github的一些