deivit24 / python-steam-api

MIT License
25 stars 11 forks source link

Key error #4

Closed crystallikelaw closed 1 year ago

crystallikelaw commented 1 year ago

I'm getting a KeyError while doing searches:

from steam import Steam
from apikeys import steamCreds
s = Steam(steamCreds)
a = s.apps.search_games('Isaac')

returns

KeyError                                  Traceback (most recent call last)
Cell In[59], line 1
----> 1 a = s.apps.search_games('Isaac')

File c:\Code\Miniconda3\envs\test\lib\site-packages\steam\apps.py:50, in Apps.search_games(self, term)
     48 for l in links:
     49     app = {}
---> 50     string_id = l["data-ds-appid"]
     51     href = l["href"].replace("\\", "").replace('"', "")
     52     app["id"] = int(string_id.replace("\\", "").replace('"', ""))

File c:\Code\Miniconda3\envs\test\lib\site-packages\bs4\element.py:1519, in Tag.__getitem__(self, key)
   1516 def __getitem__(self, key):
   1517     """tag[key] returns the value of the 'key' attribute for the Tag,
   1518     and throws an exception if it's not there."""
-> 1519     return self.attrs[key]

KeyError: 'data-ds-appid'

Here's the version I'm running

python-steam-api 1.0.7 Python 3.10.8 bs4 4.11.1

Works fine with certain searches, 'terr' as in the docs works. Throws this error often though.

Also throws a warning, if it's relevant (even when it works)

c:\Code\Miniconda3\envs\test\lib\site-packages\steam\apps.py:45: GuessedAtParserWarning: No parser was explicitly specified, so I'm using the best available HTML parser for this system ("lxml"). This usually isn't a problem, but if you run this code on another system, or in a different virtual environment, it may use a different parser and behave differently.

The code that caused this warning is on line 45 of the file c:\Code\Miniconda3\envs\test\lib\site-packages\steam\apps.py. To get rid of this warning, pass the additional argument 'features="lxml"' to the BeautifulSoup constructor.
deivit24 commented 1 year ago

@crystallikelaw will take a look tomorrow morning and try to replicate the issue. Will let you know when I patch it up!

deivit24 commented 1 year ago

@crystallikelaw Hey I was able to fix the issue and just patched python-steam-api 1.08 has the fix now. I added a retry decorator to have max tries of at least 3 times to make the HTTP request. But the main problem is that the Steam API does not have the capability to search for games/apps for some reason. So I ended up scrapping for that data and converting it to json basically.

So when searching for apps/games I was under the impression that each item that comes back has data-ds-appid attribute. But it does not some items have bundle IDs that I was not handling properly. For the time being, Only apps/games will be searched. Will think about handling bundles appids later!

Thank you for reaching out

crystallikelaw commented 1 year ago

@deivit24 Thanks so much for the quick fix! And yeah, I noticed about the API lacking search. I was looking for a fix and ended up using Steam's search suggestions API code here (Setris' example script, relevant function below though it's incomplete)

It handles bundles and packages too, if it's of any help. Thanks again!

# https://codereview.stackexchange.com/questions/248006/game-scraper-for-steam

SEARCH_SUGGEST_API = "https://store.steampowered.com/search/suggest"

def get_game_information(games: List[str]) -> Dict[str, Optional[SteamItem]]:
    game_to_info = {}

    with requests.Session() as session:
        for game in games:
            params = {"term": game, "f": "games", "cc": "US", "l": "english"}
            response = session.get(SEARCH_SUGGEST_API, params=params)
            response.raise_for_status()

            # get first search suggestion
            result = BeautifulSoup(response.text, "html.parser").find("a")

            if result:
                bundle_id = result.get("data-ds-bundleid")
                package_id = result.get("data-ds-packageid")
                app_id = result.get("data-ds-appid")

                if bundle_id:
                    name = result.find("div", class_="match_name").get_text()
                    bundle_data = deserialize_bundle_data(
                        result["data-ds-bundle-data"]
                    )
                    app_ids = extract_app_ids(bundle_data)
                    app_names = lookup_app_names(session, app_ids)
                    price = extract_bundle_price(bundle_data)

                    info: Optional[SteamItem] = SteamBundle(
                        bundle_id=int(bundle_id),
                        name=name,
                        price=price,
                        application_names=app_names,
                    )
                elif package_id:
                    info = get_package(session, package_id)
                elif app_id:
                    info = get_game(session, app_id)
                else:
                    info = None
            else:
                info = None

            game_to_info[game] = info

    return game_to_info
deivit24 commented 1 year ago

@crystallikelaw Ahhh nice might use something like this since this has all the known apis for this steam API. Thank you! Also if you ever feel like contributing then feel free to fork it! No pressure :p