【问题标题】:Daily leaderboard or price tracking data每日排行榜或价格跟踪数据
【发布时间】:2021-02-06 21:06:50
【问题描述】:

我会从头开始,因为我觉得自己迷失了所有不同的可能性。我将谈论的是排行榜,但也可以应用于价格跟踪。

我的目标是从website(一直以来的排行榜/隐藏)中抓取数据,将其放入 .csv 文件并每天中午更新。

到目前为止我取得的成功:抓取数据。

  1. 尝试抓取with BS4,但由于数据被隐藏,我无法具体到仅获得所有时间点。我发现这是成功的,因为我能够获得一个包含我需要的所有数据和日期作为标题的表格。我对这个解决方案的问题是 1) 填充 csv 的无用数据 2) 表格是垂直的而不是水平的
  2. 抓取数据with CSS selector 但我放弃了这个想法,因为有时页面不会加载并且数据没有被抓取。马上发现有一个json文件包含了数据
  3. Json 抓取似乎是最佳选择,但无法创建可以用来制作图表的 csv 文件。

这就是让我陷入困境的原因:将数据存储在一个看起来像这样的表格中,其中灰色区域是点,而 DATE1 是数据被抓取的时刻:

我不想过多地操作 csv 文件中的数据。如果表格看起来像我上面的图片,那么之后制作图表会更容易,但我遇到了麻烦。我所做的最好的事情是创建一个看起来像这样并且是垂直而不是水平的表格。

name,points,date
Dennis,52570,10-23-2020
Dinh,40930,10-23-2020
name,points,date
Dennis,52570,10-23-2020
Dinh,40930,10-23-2020
name,points,date
Dennis,52570,10-23-2020
Dinh,40930,10-23-2020

感谢您的帮助。

这是代码

import pandas as pd
import time
timestr = time.strftime("%Y-%m-%d %H:%M")
url_all_time = 'https://community.koodomobile.com/widget/pointsLeaderboard?period=allTime&maxResults=20&excludeRoles='
data = pd.read_json(url_all_time)
table = pd.DataFrame.from_records(data, index=['name'], columns=['points','name'])
table['date'] = pd.Timestamp.today().strftime('%m-%d-%Y')
table.to_csv('products.csv', index=True, encoding='utf-8')

如果我想要的东西是不可能的,我可能只是为每个成员单独抓取,为每个成员制作一个 CSV 文件并制作一个引用这些不同文件的图表。

【问题讨论】:

  • 我强烈建议使用轻量级数据库(例如 SQLite),以便将来轻松更新、检索和查询。目标表应该像(日期、排名、用户、分数)一样简单。
  • 你能用这个进行网页抓取吗?
  • 网页抓取(数据检索)、数据预处理和数据存储是三个独立的步骤。如果我理解正确,您的问题是关于数据预处理和存储。我假设您将独立处理抓取部分(pd.read_json 部分已经工作)。

标签: python pandas dataframe web-scraping


【解决方案1】:

嘿,因为您将它加载到熊猫框架中,所以操作相当简单。我先运行了你的代码

 import pandas as pd
import time
timestr = time.strftime("%Y-%m-%d %H:%M")
url_all_time = 'https://community.koodomobile.com/widget/pointsLeaderboard?period=allTime&maxResults=20&excludeRoles='
data = pd.read_json(url_all_time)
table = pd.DataFrame.from_records(data, index=['name'], columns=['points','name'])
table['date'] = pd.Timestamp.today().strftime('%m-%d-%Y')

然后我添加了几行代码来根据您的需要修改熊猫框架表。

idxs = table['date'].index
for i,val in enumerate(idxs):
    table.at[ val , table['date'][i] ] = table['points'][i]
table = table.drop([ 'date', 'points' ], axis = 1)

在上面的 sn-p 中,我使用 pandas 框架通过索引分配值的能力。因此,首先我获取日期列的索引值,然后遍历它们中的每一个以添加所需日期的列(日期列中的值)并根据我们之前提取的索引获取相应的点

这给了我以下输出:

name    10-24-2020
Dennis  52570.0
Dinh    40930.0
Sophia  26053.0
Mayumi  25300.0
Goran   24689.0
Robert T    19843.0
Allan M 19768.0
Bernard Koodo   14404.0
nim4165 13629.0
Timo Tuokkola   11216.0
rikkster    7338.0
David AKU   5774.0
Ranjan Koodo    4506.0
BobTheElectrician   4170.0
Helen Koodo 3370.0
Mihaela Koodo   2764.0
Fred C  2542.0
Philosoraptor   2122.0
Paul Deschamps  1973.0
Emilia Koodo    1755.0

然后我可以使用您代码中的最后一行将其保存到 csv。类似地,您可以提取更多日期的数据并将其格式化以将其添加到同一个熊猫框架中

table.to_csv('products.csv', index=True, encoding='utf-8')

【讨论】:

  • 我不确定我是否了解如何添加更多数据。如果我运行脚本,上次运行的数据将被覆盖
  • 你将不得不做 pandaframename.append(newdataframe)。您可能每次都覆盖相同的变量,因此丢失了提取的旧数据。代码末尾flush数据到csv,先构建整个需要的panda框架。
  • 不确定我是否理解您的意思
【解决方案2】:

所以,我已经解决了你的问题,这就是我想出的。

基本上,数据存储的最佳选择是轻量级数据库,正如 cmets 中所建议的那样。然而,通过一些计划,跳几圈,以及一些 hacky 代码,你可以通过一个简单的(有点)JSON 最终得到一个看起来像这样的.csv 文件:

注意数值相同,因为我不想等待一两天才能真正更新排行榜

我所做的是重新排列从请求返回到 API 的数据,并构建了一个如下所示的结构:

    "BobTheElectrician": {
        "id": 7160010,
        "rank": 14,
        "score_data": {
            "2020-10-24 18:45": 4187,
            "2020-10-24 18:57": 4187,
            "2020-10-24 19:06": 4187,
            "2020-10-24 19:13": 4187
        }

每个玩家都是您的主键,其中包含scores_data 值。这又是一个dict,它为您运行脚本的每一天保存points 值。

现在,诀窍是让这个JSON 看起来像你想要的.csv。问题是 - 如何?

  • 好吧,既然您打算更新所有玩家的数据(我只是假设),他们都应该有相同数量的 score_data 条目。

  • score_data 的键是您的时间戳。抓住任何玩家的score_data 键,你就有了日期标题,对吧?

  • 话虽如此,您可以用相同的方式构建您的.csv 行:从score_data 获取玩家的姓名和他们的所有积分值。这应该会给你一个列表,对吧?对。

然后,当您拥有所有这些后,您只需将其转储到 .csv 文件中即可!

把它们放在一起:

import csv
import json
import os
import random
import time
from urllib.parse import urlencode

import requests


API_URL = "https://community.koodomobile.com/widget/pointsLeaderboard?"
LEADERBOARD_FILE = "leaderboard_data.json"


def get_leaderboard(period: str = "allTime", max_results: int = 20) -> list:
    payload = {"period": period, "maxResults": max_results}
    return requests.get(f"{API_URL}{urlencode(payload)}").json()


def dump_leaderboard_data(leaderboard_data: dict) -> None:
    with open("leaderboard_data.json", "w") as jf:
        json.dump(leaderboard_data, jf, indent=4, sort_keys=True)


def read_leaderboard_data(data_file: str) -> dict:
    with open(data_file) as f:
        return json.load(f)


def parse_leaderboard(leaderboard: list) -> dict:
    return {
        item["name"]: {
            "id": item["id"],
            "score_data": {
                time.strftime("%Y-%m-%d %H:%M"): item["points"],
            },
            "rank": item["leaderboardPosition"],
        } for item in leaderboard
    }


def update_leaderboard_data(target: dict, new_data: dict) -> dict:
    for player, stats in new_data.items():
        target[player]["rank"] = stats["rank"]
        target[player]["score_data"].update(stats["score_data"])
    return target


def leaderboard_to_csv(leaderboard: dict) -> None:
    data_rows = [
        [player] + list(stats["score_data"].values()) 
        for player, stats in leaderboard.items()
    ]
    random_player = random.choice(list(leaderboard.keys()))
    dates = list(leaderboard[random_player]["score_data"])
    with open("the_data.csv", "w") as output:
        w = csv.writer(output)
        w.writerow([""] + dates)
        w.writerows(data_rows)


def script_runner():
    if os.path.isfile(LEADERBOARD_FILE):
        fresh_data = update_leaderboard_data(
            target=read_leaderboard_data(LEADERBOARD_FILE),
            new_data=parse_leaderboard(get_leaderboard()),
        )
        leaderboard_to_csv(fresh_data)
        dump_leaderboard_data(fresh_data)
    else:
        dump_leaderboard_data(parse_leaderboard(get_leaderboard()))


if __name__ == "__main__":
    script_runner()

该脚本还会检查您是否有一个伪装成正确数据库的JSON 文件。如果没有,它将写入排行榜数据。下次运行脚本时,它会更新 JSON 并生成一个新的 .csv 文件。

希望这个答案能将你推向正确的方向。

【讨论】:

  • 正是我想要的。我认为不可能重新排列来自 json 的数据。绝对没有知识想出这个。我最终为每个用户名制作了一个 JSON 文件,然后手动将这些文件合并到另一个 excel 中。这个解决方案肯定更好。它会更新 id 说有人获得更多积分吗?或者数据将在同一时间写入?
  • 这只会更新分数和排名,但您可以轻松调整它以写入任何数据。如果您觉得我的回答有用,请投票和/或接受它。
  • 到目前为止,它能够成功地在 cronjob 中工作。每当脚本在 cron 中运行时,它会给出一个 KeyError 但在 PyCharm 中运行良好。我糊涂了。我没有改变任何东西。
  • 如果没有错误回溯,我无法为您提供帮助。将问题作为单独的问题发布。
猜你喜欢
  • 2010-12-30
  • 2015-03-04
  • 1970-01-01
  • 1970-01-01
  • 2015-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多