Skip to content

Commit 48b79a2

Browse files
committed
v1.0.5
1 parent 01b1940 commit 48b79a2

File tree

3 files changed

+47
-19
lines changed

3 files changed

+47
-19
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ With the command line version of the script you can use the following arguments:
9797
| `-c` *OR* `--articles-comment` |If specified the script will retrieve and add sellers comments to the result page. | No |
9898

9999
## Version
100-
Current version is `1.0.4`, you can download this latest release on the Releases category (on the sidebar), from [this page](https://github.com/BenSouchet/cw-wizard/releases) or `git clone` the `main` branch of the repository.
100+
Current version is `1.0.5`, you can download this latest release on the Releases category (on the sidebar), from [this page](https://github.com/BenSouchet/cw-wizard/releases) or `git clone` the `main` branch of the repository.
101101

102102
## Changelog
103+
104+
### 1.0.5
105+
- Fixing issues with non english wantlist urls (for example urls with ".../fr/Pokémon/Wants/...").
106+
- Adding a maximum of 6 resquests per card in wantlist (7 with the initial one) to avoid reaching Cardmarket user request limit to quickly due to one card settings.
107+
- Fixing bugs with None object when loading more offers for cards.
108+
103109
### 1.0.4
104110
- Update Cloudflare bypass, now require to specify your browser User-Agent to properly bypass retrictions.
105111
- Fix price retrieve due to recent modification in the Cardmarket interface.

core.py

+39-17
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,24 @@
1818
# Global variables
1919
SCRIPT_NAME = 'CW Wizard'
2020

21-
VERSION = '1.0.4'
21+
VERSION = '1.0.5'
2222

2323
EXIT_ERROR_MSG = 'The Wizard encountered issue(s) please check previous logs.\n'
2424
EXIT_SUCCESS_MSG = 'The Wizard has finish is work, have a great day!\n'
2525

2626
# These two variables are extracted and set from the first wantlist url given to the script
27-
CURR_LANG = 'en'
2827
CURR_GAME = 'Magic'
2928

3029
CARDMARKET_TOP_DOMAIN = '.cardmarket.com'
3130
CARDMARKET_BASE_URL = 'https://www' + CARDMARKET_TOP_DOMAIN
3231
CARDMARKET_BASE_URL_REGEX = r'^https:\/\/www\.cardmarket\.com'
3332

34-
# This value can be overwriten via script arguments or via GUI
33+
# This value can be override via script arguments or via GUI
3534
MAXIMUM_SELLERS = 20
3635

36+
# This value cannot be currently override (this represent a max of 300 offers per card)
37+
MAXIMUM_NB_REQUESTS_PER_CARD = 6
38+
3739
CARD_LANGUAGES = { 'English': 1, 'French': 2, 'German': 3, 'Spanish': 4,
3840
'Italian': 5, 'S-Chinese': 6, 'Japanese': 7,
3941
'Portuguese': 8, 'Russian': 9, 'Korean': 10,
@@ -229,11 +231,11 @@ def cardmarket_log_in(session, credentials, silently=False):
229231

230232
# Step 3: Prepare payload
231233
token = regex_match.group('token')
232-
referal_page_path = '/{}/{}'.format(CURR_LANG, CURR_GAME)
234+
referal_page_path = '/en/{}'.format(CURR_GAME)
233235
payload = {'__cmtkn': token, 'referalPage': referal_page_path, 'username': credentials['login'], 'userPassword': credentials['password']}
234236

235237
# Step 4: Do the log-in POST request to Cardmarket with the payload
236-
response_post_login = session.post('{}/{}/{}/PostGetAction/User_Login'.format(CARDMARKET_BASE_URL, CURR_LANG, CURR_GAME), data=payload)
238+
response_post_login = session.post('{}/en/{}/PostGetAction/User_Login'.format(CARDMARKET_BASE_URL, CURR_GAME), data=payload)
237239
if response_post_login.status_code != 200:
238240
# Issue with the request
239241
funct_result.addDetailedRequestError('log-in to Cardmarket', response_post_login)
@@ -258,7 +260,7 @@ def cardmarket_log_out(session, silently=False):
258260
if not silently:
259261
LOG.debug('------- The Wizard log out of the temporary session on Cardmarket...\n')
260262

261-
response_get_logout = session.get('{}/{}/{}/PostGetAction/User_Logout'.format(CARDMARKET_BASE_URL, CURR_LANG, CURR_GAME))
263+
response_get_logout = session.get('{}/en/{}/PostGetAction/User_Logout'.format(CARDMARKET_BASE_URL, CURR_GAME))
262264
if response_get_logout.status_code != 200:
263265
# Issue with the request
264266
funct_result.addDetailedRequestError('logout of Cardmarket', response_get_logout)
@@ -318,6 +320,15 @@ def retrieve_wantlist(session, wantlist_url, continue_on_warning=False):
318320

319321
wantlist = None
320322

323+
# Get Information from url to recreate it (changing language to english)
324+
wantlist_url_regex = CARDMARKET_BASE_URL_REGEX + r"\/[a-z]{2}\/(?P<game>\w+)\/Wants/(?P<wantlist_id>\d+)$"
325+
match = re.match(wantlist_url_regex, wantlist_url)
326+
if not match:
327+
funct_result.addError('The wantlist url ("{}") seems invalid'.format(wantlist_url))
328+
return funct_result
329+
330+
wantlist_url = '{}/en/{}/Wants/{}'.format(CARDMARKET_BASE_URL, match.group('game'), match.group('wantlist_id'))
331+
321332
# Step 1: Get the desired wantlist page
322333
response_get_wantlist = session.get(wantlist_url)
323334
if response_get_wantlist.status_code != 200:
@@ -409,6 +420,8 @@ def _get_load_more_request_token(load_more_btn):
409420

410421

411422
def load_more_articles(session, funct_result, soup, card, articles_table):
423+
nb_of_requests = 0
424+
412425
# Step 1: Check if there isn't a load more articles button, in this case we stop
413426
load_more_btn = soup.find(id='loadMoreButton')
414427
if not load_more_btn:
@@ -422,7 +435,7 @@ def load_more_articles(session, funct_result, soup, card, articles_table):
422435
request_token = _get_load_more_request_token(load_more_btn)
423436

424437
# Step 3: Retrieve more article until card['maxPrice'] is reached or there is no more article to load
425-
while active:
438+
while active and nb_of_requests < MAXIMUM_NB_REQUESTS_PER_CARD:
426439
# Step 3.A: Get the price of the last card currently displayed
427440
last_article = articles_table.contents[-1]
428441
last_article_price_str = last_article.find('div', class_='price-container').find('span', class_='text-nowrap').contents[0]
@@ -433,7 +446,7 @@ def load_more_articles(session, funct_result, soup, card, articles_table):
433446
# Step 3.B.I: Initialize a payload and do a POST request
434447
args_base64 = base64.b64encode(bytes(json.dumps(load_more_args, separators=(',', ':')), 'utf-8'))
435448
payload = {'args': request_token + args_base64.decode("utf-8")}
436-
response_post_load_article = session.post('{}/{}/{}/AjaxAction'.format(CARDMARKET_BASE_URL, CURR_LANG, card_curr_game), data=payload)
449+
response_post_load_article = session.post('{}/en/{}/AjaxAction'.format(CARDMARKET_BASE_URL, card_curr_game), data=payload)
437450
if response_post_load_article.status_code != 200:
438451
# Issue with the request
439452
funct_result.addWarning('Failed to load more articles for card page ("{}")'.format(card['title']))
@@ -442,16 +455,27 @@ def load_more_articles(session, funct_result, soup, card, articles_table):
442455

443456
# Step 3.B.II: Handle the request result containing the new articles and the new page_index value
444457
more_article_soup = BeautifulSoup(response_post_load_article.text, 'html.parser')
445-
load_more_args['page'] = int(more_article_soup.find('newpage').contents[0])
458+
new_page_index_soup = more_article_soup.find('newpage')
459+
if new_page_index_soup:
460+
load_more_args['page'] = int(new_page_index_soup.contents[0])
461+
new_articles_rows_soup = more_article_soup.find('rows')
462+
if new_articles_rows_soup:
463+
articles_rows_html_str = base64.b64decode(new_articles_rows_soup.contents[0]).decode("utf-8")
464+
articles_table.append(BeautifulSoup(articles_rows_html_str, 'html.parser'))
465+
else:
466+
active = False
446467

447-
articles_rows_html_str = base64.b64decode(more_article_soup.find('rows').contents[0]).decode("utf-8")
448-
articles_table.append(BeautifulSoup(articles_rows_html_str, 'html.parser'))
449-
if load_more_args['page'] < 0:
450-
# There is no more article available, stop the process
468+
# TODO: All these if else active = False should be refactored
469+
if load_more_args['page'] < 0:
470+
# There is no more article available, stop the process
471+
active = False
472+
else:
451473
active = False
452474
else:
453475
active = False
454476

477+
nb_of_requests += 1
478+
455479

456480
def populate_sellers_dict(session, sellers, wantlist, articles_comment=False, continue_on_warning=False):
457481
funct_result = FunctResult()
@@ -460,7 +484,7 @@ def populate_sellers_dict(session, sellers, wantlist, articles_comment=False, co
460484
LOG.debug(' |____ The Wizard is aggregating sellers and articles data for the wantlist ("{}")...'.format(wantlist_url))
461485

462486
for card in wantlist:
463-
# If multiple languages selected with do one request per language
487+
# If multiple languages selected, do one request per language
464488
for card_language in card['languages']:
465489
# Save a sellers list for the current card,
466490
# to avoid adding multiple time the same article for a seller
@@ -494,7 +518,7 @@ def populate_sellers_dict(session, sellers, wantlist, articles_comment=False, co
494518
if isinstance(card['maxPrice'], Decimal):
495519
load_more_articles(session, funct_result, soup, card, articles_table)
496520

497-
# Step 4: Iterate over articles
521+
# Step 4: Iterate over articles (a.k.a offers)
498522
for article_row in articles_table.children:
499523
# Step 4.A: Check if this is a proper article
500524
if 'article-row' not in article_row.attrs['class']:
@@ -857,9 +881,7 @@ def check_wantlists_and_max_sellers(wantlist_urls, max_sellers, silently=False):
857881
# Since new games can be added to Cardmarket, checking "global_game" is not worth it
858882

859883
# Step 4: Assign info to global variables
860-
global CURR_LANG
861884
global CURR_GAME
862-
CURR_LANG = global_language
863885
CURR_GAME = global_game
864886

865887
return funct_result

cw-wizard-gui.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def next_step(window, param1, param2):
218218
window.browser_name = result.getResult()
219219

220220
# Step 1.B: Handling User-Agent
221-
user_agent = param2.get()
221+
user_agent = param2.get().strip()
222222
if not user_agent:
223223
set_window_description(window, 'Error: User-Agent cannot be empty.', is_error=True)
224224
return True

0 commit comments

Comments
 (0)