繁体   English   中英

使用 BigQuery 和 MaxMind GeoIP 查找 ipv4 和 ipv6 的地理位置

[英]Finding Geo Locations for ipv4 and ipv6 using BigQuery and MaxMind GeoIP

我正在尝试使用用户的 IP 将用户与其地理位置相匹配。

IPs 是 ipv4、ipv6 和一些带有无效条目的行的混合。

我正在使用 GeoLite2-City-Blocks-IPv4(知道我将不得不为 ipv6 使用不同的文件,如果有人知道哪个是正确的,非常感谢)和 GeoLite2-City-Locations-en。 所以基本上,一个文件有 IP 块和它们的位置代码,另一个文件有这些代码的实际位置。

我使用 Felipe Hoffa https://towardsdatascience.com/geolocation-with-bigquery-de-identify-76-million-ip-addresses-in-20-seconds-e9e652480bd2这篇文章中的说明将我的 IP 与块文件。

问题是我在尝试使用 .NET.SAFE_IP_FROM_STRING(ip_address) &.NET.IP.NET_MASK(4,mask) 函数时遇到错误。 错误是:

BYTES 的按位二元运算符要求输入的长度相等。 左侧有 16 个字节,右侧有 4 个字节。

因此,在使用正则表达式将 IP 传递给 function 之前,我尝试确保 IP 有效。 这适用于 ipv4。 我现在也找到了匹配 ipv6 的正则表达式,从我的检查来看,它似乎是准确的。 但是我仍然得到错误。 我不知道为什么以及如何修复我的查询以获得正确的结果。

请参阅下面的整个查询:

(
    SELECT
      *,
      NET.SAFE_IP_FROM_STRING(ip_address) & NET.IP_NET_MASK(4,
        mask) network_bin
    FROM (
      SELECT
        * EXCEPT (is_valid)
      FROM (
        SELECT
          *,
          CASE
            WHEN (REGEXP_CONTAINS(ip_address, r'\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}\z') OR (NOT REGEXP_CONTAINS(ip_address, r'\A(.*?[a-f0-9](:|\z)){8}') AND REGEXP_CONTAINS(ip_address, r'\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?::([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?\z')) OR REGEXP_CONTAINS(ip_address, r'\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){5}:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z') OR (NOT REGEXP_CONTAINS(ip_address, r'\A(.*?[a-f0-9]:){6}') AND REGEXP_CONTAINS(ip_address, r'\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,4})?::([a-f0-9]{1,4}:){0,5}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z')) ) THEN TRUE
            WHEN REGEXP_CONTAINS(ip_address, r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$") THEN TRUE
          ELSE
          FALSE
        END
          AS is_valid
        FROM (
          SELECT
            user,
            ip_address,
            date
          FROM 
            `project.dataset.table`)
      WHERE
        is_valid IS TRUE),
      UNNEST(GENERATE_ARRAY(9,32)) mask)

问题在这里得到了回答: https://stackoverflow.com/a/65403033/12675934

总之:问题是 NET.SAFE_IP_FROM_STRING 返回 4 个字节的 IPv4 和 16 个字节的 IPv6。 所以 & NET.IP_NET_MASK(4, mask) 对于 IPv4 是可以的,但是对于 IPv6 你需要使用 & NET.IP_NET_MASK(16, mask)。

与这个问题有相同的答案How to improve performance of GeoIP query in BigQuery?

WITH test_data AS (
    SELECT '2a02:2f0c:570c:fe00:1db7:21c4:21fa:f89' AS ip UNION ALL 
    SELECT '79.114.150.111' AS ip
)
-- replace the input_data with your data
, ipv4 AS (
    SELECT DISTINCT ip, NET.SAFE_IP_FROM_STRING(ip) AS ip_bytes
    FROM test_data 
    WHERE BYTE_LENGTH(NET.SAFE_IP_FROM_STRING(ip)) = 4
), ipv4d AS (
    SELECT ip, city_name, country_name, latitude, longitude
    FROM (
        SELECT ip, ip_bytes & NET.IP_NET_MASK(4, mask) network_bin, mask
        FROM ipv4, UNNEST(GENERATE_ARRAY(8,32)) mask
    )
    JOIN `demo_bq_dataset.geoip_city_v4`
    USING (network_bin, mask)
), ipv6 AS (
    SELECT DISTINCT ip, NET.SAFE_IP_FROM_STRING(ip) AS ip_bytes
    FROM test_data 
    WHERE BYTE_LENGTH(NET.SAFE_IP_FROM_STRING(ip)) = 16
), ipv6d AS (
    SELECT ip, city_name, country_name, latitude, longitude
    FROM (
        SELECT  ip, ip_bytes & NET.IP_NET_MASK(16, mask) network_bin, mask
        FROM ipv6, UNNEST(GENERATE_ARRAY(19,64)) mask
    )
    JOIN `demo_bq_dataset.geoip_city_v6`  
    USING (network_bin, mask)
)
SELECT * FROM ipv4d
UNION ALL 
SELECT * FROM ipv6d

为了获得 geoip_city_v4 和 geoip_city_v6,您需要从https://maxmind.com/下载 geoip 数据库

您可以按照本教程更新和准备数据集https://hodo.dev/posts/post37-gcp-bigquery-geoip/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM