Rumah > Artikel > pembangunan bahagian belakang > Saya telah mengemas kini pengambil Python untuk data BoardGameGeek
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!