datasette-scraper 作者 cldellow

星标

README 源代码

datasette-scraper

PyPI Changelog Tests License

datasette-scraper 是一个 Datasette 插件,用于管理小型(约 10 万页)抓取和提取作业。

  • 有主见但可扩展
    • 一些有用的任务开箱即用,或者编写自己的 pluggy hook 来实现更多功能
  • 深度依赖 SQLite
    • 通过 Datasette 中公开的操作表来内省你的抓取
  • 基于强大的库构建

不适用于对抗性抓取。想抓取屏蔽机器人的网站?你需要自己搞定。

安装

在与 Datasette 相同的环境中安装此插件。

datasette install datasette-scraper

使用方法

通过 metadata.json 配置 datasette-scraper。你需要在每个数据库级别启用该插件。

要在 my-database 数据库中启用它,可以这样写

{
  "databases": {
    "my-database": {
      "plugins": {
        "datasette-scraper": {
        }
      }
    }
  }
}

下次启动 datasette 时,该插件将在指定的数据库中创建几个表。前往 dss_crawl 表来定义抓取。

提供了一个 10 分钟的端到端操作视频

使用注意事项

datasette-scraper 需要一个数据库来跟踪其操作数据,还需要一个数据库来存储抓取的数据。它们可以是同一个数据库。

两个数据库都将设置为 WAL 模式。

操作数据库的 user_version pragma 将用于跟踪模式版本。

架构

datasette-scraper 处理抓取的核心簿记工作——跟踪待抓取的 URL、限制对来源的请求速率、将数据持久化到数据库中。它依赖插件来完成几乎所有有趣的工作。例如,获取实际页面、跟踪重定向、导航站点地图、提取数据。

该工具附带了用于常见用例的插件。一些用户可能希望编写自己的 after_fetch_urlextract_from_response 实现来进行自定义处理。

概览

flowchart LR
direction TB

subgraph init
  A(user starts crawl) --> B[get_seed_urls]
end

subgraph crawl [for each URL to crawl]
  before_fetch_url --> fetch_cached_url --> fetch_url --> after_fetch_url
  fetch_cached_url --> after_fetch_url
end

subgraph discover [for each URL crawled]
  discover_urls --> canonicalize_url --> canonicalize_url
  canonicalize_url --> x[queue URL to crawl]
  extract_from_response
end

init --> crawl --> discover
加载

插件钩子

大多数插件只会实现其中几个钩子。

  • conn 是一个读/写数据库的 sqlite3.Connection 对象
  • config 是抓取的配置

get_seed_urls(config)

返回一个字符串列表,表示要抓取的种子 URL。

它们将被视为深度为 0,即种子。

before_fetch_url(conn, config, job_id, url, depth, request_headers)

request_headers 是一个字典,你可以修改它来控制请求中发送的内容。

返回

  • truthy 值表示不应抓取此 URL(例如,达到最大抓取页数限制)
  • falsy 值表示没有意见

注意 before_fetch_urlcanonicalize_url

你还可以使用 canonicalize_url 钩子在 URL 进入抓取队列之前拒绝它们。

canonicalize_url 拒绝的 URL 将不会在 dss_crawl_queuedss_crawl_queue_history 表中生成条目。

使用哪个取决于你的偏好,通常情况下,如果你永远都不想要某个 URL,就在规范化时拒绝它。

fetch_cached_url(conn, config, url, depth, request_headers)

获取之前缓存的 HTTP 响应。系统在调用此方法之前不会检查速率限制是否可用。

返回

  • None,表示未处理
  • 一个响应对象,它是一个包含以下内容的字典
    • fetched_at - 一个 ISO 8601 时间格式,如 2022-12-26 01:23:45.00
    • headers - 响应头,例如 [['content-type', 'text/html']]
    • status_code - 响应码,例如 200
    • text - 响应体

一旦任何插件返回了 truthy 值,其他插件的 fetch_url 钩子将不会被调用。

fetch_url(conn, config, url, request_headers)

从活动服务器获取 HTTP 响应。系统在调用此方法之前会检查速率限制是否可用。

fetch_cached_url 具有相同的返回类型和行为。

after_fetch_url(conn, config, url, request_headers, response, fresh, fetch_duration)

对已抓取的 URL 进行处理。

discover_urls(config, url, response)

返回一个待抓取的 URL 列表。

URL 可以是字符串,在这种情况下它们将被加入队列,深度为 depth + 1;也可以是包含 URL 和深度的元组。这对于分页索引页非常有用,你可以设定最大抓取深度为 2,但将所有索引页都视为深度 1。

canonicalize_url(config, from_url, to_url, to_url_depth)

返回

  • False 用于过滤 URL
  • 用于替代的待抓取 URL
  • NoneTrue 表示不做任何操作

待抓取的 URL 可以是字符串,也可以是包含字符串和深度的元组。

此钩子适用于

  • 阻止我们永远不想要的 URL
  • 规范化 URL,例如,省略查询参数
  • 将抓取限制在同一来源
  • 重置分页深度

extract_from_response(config, url, response)

返回一个表示待插入或待更新插入行的对象

{
  "dbname": {  // can be omitted, in which case, current DB will be used
    "users": [
      {
        "id!": "cldellow@gmail.com",  // ! indicates pkey, compound OK
        "name": "Colin",
      },
      {
        "id!": "santa@northpole.com",
        "name": "Santa Claus",
      }
    ],
    "places": [
      {
        "id@": "santa@northpole.com",
        "__delete": true
      },
      {
        "id@": "cldellow@gmail.com",
        "city": "Kitchener",
      },
      {
        "id@": "cldellow@gmail.com",
        "city": "Dawson Creek"
      }
    ]
  }
}

列名末尾可以带符号

  • ! 表示该列是主键的一部分;该值最多只能对应一行数据
  • @ 表示该列应该被索引;该值可以对应多行数据

带符号的列必须在创建表时已知。虽然你可以有多个带符号的列,但在同一个表中不能混用 !@ 符号。

任何缺失的表或列都将创建。列将具有 ANY 数据类型。除非列带有 ! 符号,否则将允许空值。

你可以通过在你的对象中发出 __delete 键来指示应该删除某一行。

为了减少写事务并提高吞吐量,datasette-scraper 可能会批量提交你的更改到数据库。如果它确定数据库状态不会改变,它甚至可能完全省略 DELETE/INSERT 语句。

如果你想更精细地控制模式,请手动创建表。

元数据钩子

这些钩子不影响抓取操作。它们提供元数据,帮助验证用户的配置并显示用于配置抓取的 UI。

config_schema()

返回一个 ConfigSchema 选项,该选项定义了如何配置此插件。

配置通过 JSON schema 完成。UI 通过 JSON Forms 完成。

请查看现有插件,了解如何使用此钩子。

模式是可选的;如果省略,你需要以带外方式配置插件。

config_default_value()

返回 None 表示新抓取默认不应使用此插件。

否则,返回一个符合 config_schema() 中模式的合理默认值

开发

要在本地设置此插件,首先检出代码。然后创建一个新的虚拟环境

cd datasette-scraper
python3 -m venv venv
source venv/bin/activate

现在安装依赖和测试依赖

pip install -e '.[test]'

运行测试

pytest