【问题标题】:Extract top-level domain in PrestoDB在 PrestoDB 中提取顶级域
【发布时间】:2022-11-09 23:37:44
【问题描述】:

我有一个在 AWS Athena 中查询的大型 URL 数据集(使用 PrestoDB)。 PrestoDB 具有提取 URL 主机的内置函数。我现在想将主机进一步拆分为子域、域和顶级域 (TLD)。

据我的理解,这实际上不能用正则表达式来完成:“因为过去并且仍然没有找到可以为特定顶级域注册域的最高级别的算法方法(每个策略都不同)注册表),唯一的方法是创建一个列表。” (来自https://publicsuffix.org/learn/)。 Python 和 R 中的包(请参阅here)相应地使用来自 publicsuffix.org 的 TLD 列表。据我所知,PrestoDB 没有任何等价物。请注意,数据集太大,无法在 R 或 Python 中导出和处理。

从理论上讲,我可以遵循tldextract R 包的逻辑,它

  • 在有.的地方拆分主机(例如aws.amazon.com变成c('aws', 'amazon', 'com')的向量),
  • 然后以增量方式将这些组件(第一个除外)连接在一起(例如,第一个连接将是amazon.com,第二个和最后一个将是com
  • 并为每个连接检查 TLD 列表中是否存在匹配项。

从理论上讲,我可以将这个逻辑转换为 Presto,但没有循环就不容易。那么有没有任何现有的解决方案呢?

【问题讨论】:

    标签: r amazon-web-services presto


    【解决方案1】:

    我刚刚用正则表达式检查了选项。
    这与您的用例不匹配还是我误解了什么?
    试一试:

    SELECT regexp_extract('aws.amazon.com', '(w+.w+)$');
    SELECT regexp_extract('sub.domain.aws.amazon.com', '(w+.w+)$');
    

    【讨论】:

    • 不幸的是没有。正如我的问题中提到的,使用正则表达式显然不可能将主机解析为域。你可以通过这个例子看到:SELECT regexp_extract('bbc.co.uk', '(w+.w+)$')(这里,域应该是 bbc.co.uk 而不是 co.uk)
    • 啊,对。我没有想到那个边缘案例。对不起。
    【解决方案2】:

    我提出了自己的解决方案,主要基于 R 包 tldextract,它依赖于 Public Suffix List。我详细解释解决方案here。这假设您已将公共后缀列表作为 domains_tld 导入 Presto,其中包含一个名为 tld 的列包含 TLD。

    首先是一些玩具数据:

    CREATE TABLE dat AS
    SELECT url, url_extract_host(url) AS url_host FROM
    (WITH temp AS (
    SELECT * FROM (VALUES
    ('https://subsub.sub.example.co.uk/page?q=1234#abcd'),
    ('https://example.api.gov.uk/page?q=1234#abcd'),
    ('https://example.co.uk/page?q=1234#abcd'),
    ('https://www.example.com/page?q=1234#abcd'),
    ('https://example.com/page?q=1234#abcd')) AS tab (url))
    SELECT url from temp)
    

    接下来,连接生成的部分,从最后一个开始:

    CREATE TABLE dat3 AS
    SELECT url_host, part_1, part_2, part_3, part_4, part_5,
    CASE
    WHEN part_5 IS NOT NULL THEN part_5
    WHEN part_5 IS NULL AND part_4 IS NOT NULL THEN part_4
    WHEN part_4 IS NULL AND part_3 IS NOT NULL THEN part_3
    WHEN part_3 IS NULL AND part_2 IS NOT NULL THEN part_2
    END AS last,
    CASE
    WHEN part_5 IS NOT NULL THEN concat(part_4, '.', part_5)
    WHEN part_5 IS NULL AND part_4 IS NOT NULL THEN concat(part_3, '.', part_4)
    WHEN part_4 IS NULL AND part_3 IS NOT NULL THEN concat(part_2, '.', part_3)
    END AS two_last,
    CASE
    WHEN part_5 IS NOT NULL THEN concat(part_3, '.', part_4, '.', part_5)
    WHEN part_5 IS NULL AND part_4 IS NOT NULL THEN concat(part_2, '.', part_3, '.', part_4)
    END AS three_last,
    CASE
    WHEN part_5 IS NOT NULL THEN concat(part_2, '.', part_3, '.', part_4, '.', part_5)
    END AS four_last
    FROM dat2
    

    接下来,将此表转换为“长”格式:

    CREATE TABLE dat4 AS
    SELECT * FROM
    (WITH temp AS (
    SELECT url_host, array[last, two_last, three_last, four_last] last_combs
    FROM dat3)
    SELECT url_host, pattern FROM temp
    CROSS JOIN UNNEST(last_combs) as t(pattern))
    WHERE pattern IS NOT NULL
    

    现在,将组合与 TLD 列表匹配:

    CREATE TABLE dat5 AS
    SELECT url_host, max_by(tld, tld_length) tld FROM
    (SELECT url_host, pattern, d.tld, length(d.tld) tld_length
    FROM dat4 h
    LEFT JOIN domains_tld d ON h.pattern = d.tld
    WHERE d.tld IS NOT NULL)
    GROUP BY url_host
    

    最后,我们从主机中提取域!

    SELECT url_host, tld, 
    regexp_extract(url_host, concat(‘p{Alnum}+.’, tld)) AS sld 
    FROM dat5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      • 2018-01-04
      • 2014-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多