使用 Python 和 PySpark 提取 URL 参数

phi*_*thy 4 python url dataframe apache-spark pyspark

假设我有一个充满 URL 的列,如下所示:

+------------------------------------------+
|url                                       |
+------------------------------------------+
|https://www.example1.com?param1=1&param2=a|
|https://www.example2.com?param1=2&param2=b|
|https://www.example3.com?param1=3&param2=c|
+------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

从该列中提取 URL 参数并将它们作为列添加到数据框中以生成以下内容的最佳方法是什么?

+-------------------------------------------+---------------+
|                                        url| param1| param2|
+-------------------------------------------+---------------+
|https://www.example1.com?param1=1&param2=a |      1|      a|
|https://www.example2.com?param1=2&param2=b |      2|      b|
|https://www.example3.com?param1=3&param2=c |      3|      c|
|etc...                                     | etc...| etc...|
+-------------------------------------------+---------------+
Run Code Online (Sandbox Code Playgroud)

我的尝试

我可以想到两种可能的方法来做到这一点,使用functions.regexp_extractpyspark 库或使用标准库中的urllib.parse.parse_qsand 。urllib.parse.urlparse前一种解决方案使用正则表达式,这是一种从字符串中提取参数的挑剔方法,但后者需要包装在 UDF 中才能使用。

+------------------------------------------+
|url                                       |
+------------------------------------------+
|https://www.example1.com?param1=1&param2=a|
|https://www.example2.com?param1=2&param2=b|
|https://www.example3.com?param1=3&param2=c|
+------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

正则表达式解决方案:

+-------------------------------------------+---------------+
|                                        url| param1| param2|
+-------------------------------------------+---------------+
|https://www.example1.com?param1=1&param2=a |      1|      a|
|https://www.example2.com?param1=2&param2=b |      2|      b|
|https://www.example3.com?param1=3&param2=c |      3|      c|
|etc...                                     | etc...| etc...|
+-------------------------------------------+---------------+
Run Code Online (Sandbox Code Playgroud)

UDF解决方案:

from pyspark.sql import *
from pyspark.sql import functions as fn

df = spark.createDataFrame(
  [
    ("https://www.example.com?param1=1&param2=a",),
    ("https://www.example2.com?param1=2&param2=b",),
    ("https://www.example3.com?param1=3&param2=c",)
  ],
  ["url"]
)
Run Code Online (Sandbox Code Playgroud)

我想使用像这样的库的多功能性,urllib但也希望在 pyspark 函数中编写它的可优化性。有没有比我迄今为止尝试过的两种方法更好的方法?

bla*_*hop 5

您可以parse_url在 SQL 表达式内使用expr

提取特定查询参数

parse_url可以使用第三个参数来指定我们要从 URL 中提取的键 (param):

df.selectExpr("*", "parse_url(url,'QUERY', 'param1')").show()

+------------------------------------------+------+
|url                                       |param1|
+------------------------------------------+------+
|https://www.example2.com?param1=2&param2=b|2     |
|https://www.example.com?param1=1&param2=a |1     |
|https://www.example3.com?param1=3&param2=c|3     |
+------------------------------------------+------+
Run Code Online (Sandbox Code Playgroud)

将所有查询参数提取到列中

如果要将所有查询参数提取为新列,而无需指定其名称,则可以首先解析 URL,然后拆分和爆炸以获取参数及其值,最后通过透视将每个参数获取为一列。

import pyspark.sql.functions as F

df.withColumn("parsed_url", F.explode(F.split(F.expr("parse_url(url, 'QUERY')"), "&"))) \
    .withColumn("parsed_url", F.split("parsed_url", "=")) \
    .select("url",
            F.col("parsed_url").getItem(0).alias("param_name"),
            F.col("parsed_url").getItem(1).alias("value")
            ) \
    .groupBy("url") \
    .pivot("param_name") \
    .agg(F.first("value")) \
    .show()
Run Code Online (Sandbox Code Playgroud)

给出:

+------------------------------------------+------+------+
|url                                       |param1|param2|
+------------------------------------------+------+------+
|https://www.example2.com?param1=2&param2=b|2     |b     |
|https://www.example.com?param1=1&param2=a |1     |a     |
|https://www.example3.com?param1=3&param2=c|3     |c     |
+------------------------------------------+------+------+
Run Code Online (Sandbox Code Playgroud)

正如 @jxc 在评论中建议的,另一个解决方案是使用str_to_map函数:

df.selectExpr("*", "explode(str_to_map(split(url,'[?]')[1],'&','='))") \
    .groupBy('url') \
    .pivot('key') \
    .agg(F.first('value'))
Run Code Online (Sandbox Code Playgroud)

  • 如果是这种情况,我只需使用 split 来获取 PARAM,然后将其转换为 MapType 列,然后执行常规操作: `df.selectExpr('*', 'explode(str_to_map(split(url,"[? ]")[1],"&","="))').groupby('url').pivot('key').agg(first('value'))` (2认同)