Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Saya telah mengemas kini pengambil Python untuk data BoardGameGeek

Saya telah mengemas kini pengambil Python untuk data BoardGameGeek

Patricia Arquette
Patricia Arquetteasal
2024-10-15 22:15:30335semak imbas

I

Skrip ini akan mengambil data item daripada BoardGameGeek API dan menyimpan data dalam fail CSV.

Saya mengemas kini skrip preivous. Memandangkan respons API adalah dalam format XML dan memandangkan tiada titik akhir untuk mengambil semua item sekali gus, skrip sebelumnya akan menggelungkan melalui julat ID yang disediakan, membuat panggilan satu demi satu untuk setiap item. Itu tidak optimum, ia mengambil masa yang lama untuk julat ID yang lebih besar (pada masa ini bilangan item (ID) tertinggi yang tersedia pada BGG mencapai setinggi 400k ) dan hasilnya mungkin tidak boleh dipercayai. Oleh itu, dengan beberapa pengubahsuaian dalam skrip ini, lebih banyak ID item akan ditambahkan sebagai nilai parameter pada url permintaan tunggal, dan dengan itu, respons tunggal akan mengembalikan berbilang item (~800 ialah jumlah tertinggi yang dikembalikan semula oleh satu respons. BGG akhirnya boleh mengubahnya kemudian; anda boleh mengubah saiz_kelompok dengan mudah untuk melaraskan jika perlu).

Selain itu, skrip ini akan mengambil semua item, dan bukan sahaja data yang berkaitan dengan permainan papan.

Maklumat yang diambil dan disimpan untuk setiap permainan papan adalah seperti berikut:

nama, id_permainan, jenis, rating, berat, tahun_terbitan, min_players, max_players, min_play_time, max_pay_time, min_age, owned_by, kategori, mekanik, pereka bentuk, artis dan penerbit.

Kemas kini dalam skrip ini adalah seperti berikut; Kami mulakan dengan mengimport perpustakaan yang diperlukan untuk skrip ini:

# Import libraries
from bs4 import BeautifulSoup
from csv import DictWriter
import pandas as pd
import requests
import time

Berikut ialah fungsi yang akan dipanggil apabila skrip selesai berdasarkan julat ID. Selain itu, jika terdapat ralat semasa membuat permintaan, fungsi ini akan dipanggil untuk menyimpan semua data yang dilampirkan pada senarai permainan sehingga tahap pengecualian berlaku.

# CSV file saving function
def save_to_csv(games):
    csv_header = [
        'name', 'game_id', 'type', 'rating', 'weight', 'year_published', 'min_players', 'max_players',
        'min_play_time', 'max_play_time', 'min_age', 'owned_by', 'categories',
        'mechanics', 'designers', 'artists', 'publishers'
    ]
    with open('bgg.csv', 'a', encoding='UTF8') as f:
        dictwriter_object = DictWriter(f, fieldnames=csv_header)
        if f.tell() == 0:
            dictwriter_object.writeheader()
        dictwriter_object.writerows(games)

Kami perlu menentukan tajuk untuk permintaan. Jeda antara permintaan boleh ditetapkan melalui SLEEP_BETWEEN_REQUESTS (Saya telah melihat beberapa maklumat bahawa had kadar ialah 2 permintaan sesaat, tetapi ia mungkin maklumat lapuk kerana saya tidak menghadapi masalah dengan jeda ditetapkan kepada 0). Di samping itu, di sini ditetapkan nilai untuk ID titik mula (start_id_range), julat maksimum (max_id_range) dan batch_size ialah berapa banyak permainan yang sepatutnya respons kembali. Url asas ditakrifkan dalam bahagian ini, tetapi ID ditambahkan di bahagian seterusnya skrip.

# Define request url headers
headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0",
    "Accept-Language": "en-GB, en-US, q=0.9, en"
}

# Define sleep timer value between requests
SLEEP_BETWEEN_REQUEST = 0

# Define max id range
start_id_range = 0
max_id_range = 403000
batch_size = 800
base_url = "https://boardgamegeek.com/xmlapi2/thing?id="

Bahagian berikut adalah logik utama skrip ini. Pada mulanya, berdasarkan saiz kelompok, ia akan membuat rentetan ID yang berada dalam julat ID yang ditentukan, tetapi tidak lebih ID daripada nombor yang ditentukan dalam batch_size dan yang akan ditambahkan pada parameter id url. Dengan itu, setiap respons akan mengembalikan data untuk bilangan item yang sama dengan saiz kelompok. Selepas itu ia akan memproses dan menambahkan data ke senarai permainan untuk setiap respons dan akhirnya menambah pada fail CSV.

# Main loop that will iterate between the starting and maximum range in intervals of the batch size
for batch_start in range(start_id_range, max_id_range, batch_size):
    # Make sure that the batch size will not exceed the maximum ids range
    batch_end = min(batch_start + batch_size - 1, max_id_range)
    # Join and append to the url the IDs within batch size
    ids = ",".join(map(str, range(batch_start, batch_end + 1)))
    url = f"{base_url}?id={ids}&stats=1"

    # If by any chance there is an error, this will throw the exception and continue on the next batch
    try:
        response = requests.get(url, headers=headers)
    except Exception as err:
        print(err)
        continue


    if response.status_code == 200:
        soup = BeautifulSoup(response.text, features="html.parser")
        items = soup.find_all("item")
        games = []
        for item in items:
            if item:
                try:
                    # Find values in the XML
                    name = item.find("name")['value'] if item.find("name") is not None else 0
                    year_published = item.find("yearpublished")['value'] if item.find("yearpublished") is not None else 0
                    min_players = item.find("minplayers")['value'] if item.find("minplayers") is not None else 0
                    max_players = item.find("maxplayers")['value'] if item.find("maxplayers") is not None else 0
                    min_play_time = item.find("minplaytime")['value'] if item.find("minplaytime") is not None else 0
                    max_play_time = item.find("maxplaytime")['value'] if item.find("maxplaytime") is not None else 0
                    min_age = item.find("minage")['value'] if item.find("minage") is not None else 0
                    rating = item.find("average")['value'] if item.find("average") is not None else 0
                    weight = item.find("averageweight")['value'] if item.find("averageweight") is not None else 0
                    owned = item.find("owned")['value'] if item.find("owned") is not None else 0


                    link_type = {'categories': [], 'mechanics': [], 'designers': [], 'artists': [], 'publishers': []}

                    links = item.find_all("link")

                    # Append value(s) for each link type
                    for link in links:                            
                        if link['type'] == "boardgamecategory":
                            link_type['categories'].append(link['value'])
                        if link['type'] == "boardgamemechanic":
                            link_type['mechanics'].append(link['value'])
                        if link['type'] == "boardgamedesigner":
                            link_type['designers'].append(link['value'])
                        if link['type'] == "boardgameartist":
                            link_type['artists'].append(link['value'])
                        if link['type'] == "boardgamepublisher":
                            link_type['publishers'].append(link['value'])

                    # Append 0 if there is no value for any link type
                    for key, ltype in link_type.items():
                        if not ltype:
                            ltype.append("0")

                    game = {
                        "name": name,
                        "game_id": item['id'],
                        "type": item['type'],
                        "rating": rating,
                        "weight": weight,
                        "year_published": year_published,
                        "min_players": min_players,
                        "max_players": max_players,
                        "min_play_time": min_play_time,
                        "max_play_time": max_play_time,
                        "min_age": min_age,
                        "owned_by": owned,
                        "categories": ', '.join(link_type['categories']),
                        "mechanics": ', '.join(link_type['mechanics']),
                        "designers": ', '.join(link_type['designers']),
                        "artists": ', '.join(link_type['artists']),
                        "publishers": ', '.join(link_type['publishers']),
                    }

                    # Append current item to games list
                    games.append(game)
                except TypeError:
                    print(">>> NoneType error. Continued on the next item.")
                    continue
        save_to_csv(games)

        print(f">>> Request successful for batch {batch_start}-{batch_end}")
    else:
        print(f">>> FAILED batch {batch_start}-{batch_end}")

    # Pause between requests
    time.sleep(SLEEP_BETWEEN_REQUEST)

Di bawah anda boleh pratonton beberapa baris pertama rekod dalam fail CSV sebagai DataFrame panda.

# Preview the CSV as pandas DataFrame
df = pd.read_csv('./bgg.csv')
print(df.head(5))

Atas ialah kandungan terperinci Saya telah mengemas kini pengambil Python untuk data BoardGameGeek. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn