分析 PyPI 软件包下载量#
本部分介绍如何使用公共 PyPI 下载统计数据集来详细了解托管在 PyPI 上的软件包(或软件包)的下载量。例如,你可以使用它来发现用于下载软件包的 Python 版本分布。
背景#
出于多种原因,PyPI 不会显示下载统计数据:[1]
与内容分发网络 (CDN) 配合使用效率低下:下载统计数据会不断变化。将它们包含在大量缓存的项目页面中,将需要更频繁地使缓存失效,并降低缓存的整体有效性。
极不准确:许多因素会阻止下载计数准确,其中包括
pip
的下载缓存(降低下载计数)内部或非官方镜像(既可以提高也可以降低下载计数)
未托管在 PyPI 上的软件包(为了比较)
非官方脚本或尝试增加下载计数(提高下载计数)
已知的历史数据质量问题(降低下载计数)
并非特别有用:仅仅因为一个项目被下载了很多次并不意味着它很好;同样,仅仅因为一个项目没有被下载很多次并不意味着它很糟糕!
简而言之,由于其价值出于各种原因较低,并且使其发挥作用所需的权衡很高,因此它并不是有效利用有限资源的方式。
公共数据集#
作为替代方案,Linehaul 项目将 PyPI 的下载日志流式传输到 Google BigQuery [2],它们存储为公共数据集。
开始设置#
为了使用 Google BigQuery 查询 公共 PyPI 下载统计数据集,你需要一个 Google 帐户,并在 Google Cloud Platform 项目中启用 BigQuery API。你可以每月运行高达 1TB 的查询 使用 BigQuery 免费层,无需信用卡
导航到 BigQuery Web UI。
创建一个新项目。
启用 BigQuery API。
有关如何开始使用 BigQuery 的更详细说明,请查看 BigQuery 快速入门指南。
数据架构#
Linehaul 会为每次下载在 bigquery-public-data.pypi.file_downloads
表中写入一个条目。该表包含有关下载了哪些文件以及如何下载文件的信息。表架构 中一些有用的列包括
列 |
说明 |
示例 |
---|---|---|
timestamp |
日期和时间 |
|
file.project |
项目名称 |
|
file.version |
软件包版本 |
|
details.installer.name |
安装程序 |
pip, bandersnatch |
details.python |
Python 版本 |
|
有用的查询#
通过单击“撰写查询”按钮在 BigQuery Web UI 中运行查询。
请注意,这些行存储在分区表中,这有助于限制查询的成本。这些示例查询通过筛选 timestamp
列来分析近期历史记录中的下载情况。
统计软件包下载量#
以下查询统计项目“pytest”的总下载量。
#standardSQL
SELECT COUNT(*) AS num_downloads
FROM `bigquery-public-data.pypi.file_downloads`
WHERE file.project = 'pytest'
-- Only query the last 30 days of history
AND DATE(timestamp)
BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND CURRENT_DATE()
num_downloads |
---|
26190085 |
要仅统计来自 pip 的下载量,请筛选 details.installer.name
列。
#standardSQL
SELECT COUNT(*) AS num_downloads
FROM `bigquery-public-data.pypi.file_downloads`
WHERE file.project = 'pytest'
AND details.installer.name = 'pip'
-- Only query the last 30 days of history
AND DATE(timestamp)
BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND CURRENT_DATE()
num_downloads |
---|
24334215 |
按时间推移统计软件包下载量#
要按月度下载量分组,请使用 TIMESTAMP_TRUNC
函数。按此列筛选还可以降低相应的成本。
#standardSQL
SELECT
COUNT(*) AS num_downloads,
DATE_TRUNC(DATE(timestamp), MONTH) AS `month`
FROM `bigquery-public-data.pypi.file_downloads`
WHERE
file.project = 'pytest'
-- Only query the last 6 months of history
AND DATE(timestamp)
BETWEEN DATE_TRUNC(DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH), MONTH)
AND CURRENT_DATE()
GROUP BY `month`
ORDER BY `month` DESC
num_downloads |
month |
---|---|
1956741 |
2018-01-01 |
2344692 |
2017-12-01 |
1730398 |
2017-11-01 |
2047310 |
2017-10-01 |
1744443 |
2017-09-01 |
1916952 |
2017-08-01 |
按时间推移统计 Python 版本#
从 details.python
列中提取 Python 版本。警告:此查询处理超过 500 GB 的数据。
#standardSQL
SELECT
REGEXP_EXTRACT(details.python, r"[0-9]+\.[0-9]+") AS python_version,
COUNT(*) AS num_downloads,
FROM `bigquery-public-data.pypi.file_downloads`
WHERE
-- Only query the last 6 months of history
DATE(timestamp)
BETWEEN DATE_TRUNC(DATE_SUB(CURRENT_DATE(), INTERVAL 6 MONTH), MONTH)
AND CURRENT_DATE()
GROUP BY `python_version`
ORDER BY `num_downloads` DESC
python |
num_downloads |
---|---|
3.7 |
18051328726 |
3.6 |
9635067203 |
3.8 |
7781904681 |
2.7 |
6381252241 |
null |
2026630299 |
3.5 |
1894153540 |
获取工件的绝对链接#
有时,能够根据 PyPI 上的工件哈希获取绝对下载链接非常有用,例如,如果某个特定项目或版本已从 PyPI 中删除。元数据表包含 path
列,其中包括哈希和工件文件名。
注意
此处生成的 URL 并不保证稳定,但目前与托管 PyPI 工件的 URL 保持一致。
SELECT
CONCAT('https://files.pythonhosted.org/packages', path) as url
FROM
`bigquery-public-data.pypi.distribution_metadata`
WHERE
filename LIKE 'sampleproject%'
url |
---|
注意事项#
除了上面背景中列出的注意事项之外,Linehaul 还存在一个错误,导致其在 2018 年 7 月 26 日之前严重低估了下载统计信息。此日期之前的下载在比例上是准确的(例如,Python 2 与 Python 3 下载的百分比),但总数比实际值低一个数量级。
其他工具#
除了使用 BigQuery 控制台之外,在分析下载统计信息时还有一些其他工具可能很有用。
google-cloud-bigquery
#
您还可以通过 BigQuery API 和 google-cloud-bigquery 项目(BigQuery 的官方 Python 客户端库)以编程方式访问公共 PyPI 下载统计信息数据集。
from google.cloud import bigquery
# Note: depending on where this code is being run, you may require
# additional authentication. See:
# https://cloud.google.com/bigquery/docs/authentication/
client = bigquery.Client()
query_job = client.query("""
SELECT COUNT(*) AS num_downloads
FROM `bigquery-public-data.pypi.file_downloads`
WHERE file.project = 'pytest'
-- Only query the last 30 days of history
AND DATE(timestamp)
BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
AND CURRENT_DATE()""")
results = query_job.result() # Waits for job to complete.
for row in results:
print("{} downloads".format(row.num_downloads))
pypinfo
#
使用 pip 安装 pypinfo。 用法python3 -m pip install pypinfo
$ pypinfo requests
Served from cache: False
Data processed: 6.87 GiB
Data billed: 6.87 GiB
Estimated cost: $0.04
| download_count |
| -------------- |
| 9,316,415 |
pandas-gbq
#
pandas-gbq 项目允许通过 Pandas 访问查询结果。