Loop through array of shuffled WebElements without them getting stale - python

My dilemma is that if I use
a=[]
a=driver.find_elements_by_class_name("card")
random.shuffle(a)
for card in a:
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The first round works but then I get a StaleElementException because it clicks links and goes to diff pages. So then I switched to this:
a=[]
a=driver.find_elements_by_class_name("card")
i=0
cardnum=len(a)
while i != cardnum:
i += 1 #needed because first element thats found doesnt work
a=driver.find_elements_by_class_name("card") #also needed to refresh the list
random.shuffle(a)
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The problem with this one is the i variable because the same card could be clicked due to the shuffle with each loop. Then I added a catch to check if card had already been clicked and continue if it has. Sounds like it'd work but sadly the i variable counts these and then eventually counts past the index. I thought about periodically setting i back to 1 but I do not know if it will work. Edit: would make an infinite loop since once all are clicked i will be zero and it will never exit.
I know the code works it's been tested extensively, however, bots get banned for not being humanlike and random. The basics of this script is goes thru a list of categories and then goes thru all the cards in a category. Tried randomizing the categories but similar dilemma because to refresh the list you have to remake array in each loop like the above block then comes the problem with categories already being completed would be clicked again... Any advice would be appreciated.

What is happening here is that, as you interact with the page, the DOM gets refreshed, eventually causing the elements you've been storing to go stale.
Rather than keeping a list of the elements, keep a reference to their individual element paths, and reacquire the elements as needed:
# The base css path for all the cards
base_card_css_path = ".card"
# Get all the target elements. You are only doing this to
# get a count of the number of elements on the page
card_elems = driver.find_elements_by_css_selector(base_card_css_path)
# Convert this to a list of indexes, starting with 1
card_indexes = list(range(1, len(card_elems)+1))
# Shuffle it
random.shuffle(card_indexes)
# Use `:nth-child(X)` syntax to get these elements on an as needed basis
for index in card_indexes:
card_css = base_card_css_path + ":nth-child({0})".format(index)
card = driver.find_element_by_css_selector(card_css)
nextup=(str(card.text) + '\n' + "_" * 15)
# do a bunch of stuff that takes about 10 min
(The above is untested for obvious reasons)

Related

Double ```for``` loop

I'm trying to make a nesting for loop to calculate points for my cribbage game. Right now I'm trying to figure out a method for counting all the '15's in the players hand. The way I currently have the system set up makes it so if 'dT' is in the hand, the card art for the ten of diamonds will print.
What I currently have (that is related to this question) is
hand = [] # this is filled by the rest of the program
handCombos = []
def count15s():
for _ in range(6):
possiblePairs = {'T5','J5','Q5','K5','78','96','5T','5J','5Q','5K','87','69'}
first = hand[0][1]
second = hand[2][1]
combo = ''.join(first+second)
if(combo in possiblePairs):
print('ham') # placeholder so the program can run without an error
print(combo)
This works for what it is supposed to--if the second part of the first term + the second part of the third term is in the list of possible pairs, then it prints 'ham' to the console. The problem with this, however, is that 1) It prints 'ham' 6 times, and 2) It actually does what it's supposed to.
I'm looking for a way that I can nest a for loop inside another one so it can scan every combination of '15's in the hand, then that specific combination to the handCombos array so that it can't be used again. I know that I need a for loop somewhere but the way I'm thinking of doing it would end up with a triple loop and I don't think that this is the best way to do it.

How to keep on updating a specific list within the for loop?

I'm very new to the world of programming, I've been trying to solve a specific python academic exercise but I ran into an obstacle.
The problem is that I need to generate a lucky numbers sequence, as in the user inputs a sequence [1,...,n] and these steps happen:
Every second element is removed
Every third element is removed
Every fourth element is removed
.
.
.
When it becomes impossible to remove more numbers, the numbers left in the list are "lucky".
This is my code:
def lucky(l):
index = 2
new_list = []
while(index<len(l)):
for i in range(len(l)):
if(i%index==0):
new_list.append(l[i])
index=index+1
return new_list
The while loop is to have the final condition when " it is impossible to remove more numbers". However with every iteration, the list gets shorter more and more, but I don't know how to do it.
My code works for the first condition when index=2(remove every 2nd element), then in the following loops it doesn't work because:
It is still limited by length of the original list.
new_list.append(l[i]) will just add more elements to the new_list, rather than updating it in its place.
I don't know how to update the list without creating multiple amounts of lists and with each iteration adding the new elements to a new list.
Any help is appreciated.
You could use del with appropriate list slicing (see the manual for more details) to update the list in-place:
def lucky(l):
interval = 2
while interval <= len(l):
del l[interval-1::interval]
interval += 1
I am not sure if I understand your question correctly, but you can remove items from your original list via del l[index], where index is the index of the element to be removed.
For more details on lists look here:
https://docs.python.org/3/tutorial/datastructures.html
import math
def lucky(l, index):
for i in range(math.floor(len(l)/index)):
del l[(i+1)*(index-1)]
Not sure if the code will work, as I cannot test it right now. But I think it should work somehow like that.
EDIT:
Tested it and the code works. If you want to run all three steps, just do:
l = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
lucky(l,2)
lucky(l,3)
lucky(l,4)
print(l)
>>>[1,3,7,13,15]

How to check element as you append element in python list?

New to python with limited prior programming experience, I'm trying to create a card game as a learning vehicle. The first step is to deal cards. I know there are many ways to do this including creating card objects and deal methods. That will come later.
For now, I want to randomly generate suites and numbers, concatenate the values into a string and append the element in a list. As the cards are dealt, the newly generated element will be compared against what's already in the list to see if it's a repeat. If it is, then regenerate the element. I'm having trouble checking the element as I'm adding the element to the list. Everything I found on this topic has been checking the list against a static known value. I want to dynamically check the element against what's already in the list as new elements are added to the list. Below is what I have so far. Any help is much appreciated!
import random
player_count =int(input('Welcome! How many players? '))
for i in range (player_count):
x = random.randint(1,13)
y = random.choice(['-Spade','-Heart','-Diamond','-Club'])
card_set =[str(x) + y]
if card_set not in card_set:
card_set.append
print(card_set)
You're setting card_set fresh on every pass through the loop.
saved = []
for i in range(10):
draw = new_draw() # returns a string
if draw not in saved:
saved.append(draw)
Instead of setting card_set before the if condition, you should create another variable and assign the new result. If that new result is not in card_set then proceed.
player_count = int(input('Welcome! How many players? '))
for i in range (player_count):
x = random.randint(1,13)
y = random.choice(['-Spade','-Heart','-Diamond','-Club'])
card = str(x) + y
if card not in card_set:
#do some stuff

How to constantly update a list of fixed length in python?

Sorry for the noob question. I'm just starting to code and I need to keep track of the 1hr price history.
I wanted pull values every second into a list of size 3600 until the list is filled, then shift the list to the left every second from there on out so the prices would stay constant.
while True:
polo = exchange.returnTicker()
ethBtcRatio = polo["BTC_ETH"]['last']
priceHistory = []
## What do I do here? Append?
time.sleep(1)
Any ideas?
You could append each new entry, then check the length of the list and conditionally priceHistory.pop(0).
Looks like you're initializing the priceHistory list in the loop though. You want to do that before the loop, so you don't end up assigning empty lists to priceHistory every time.
Alternatively, and this depends on the use case, is that you can create an initial list with 3600 None entries, then just append and pop every time without the conditional length check.

Random list with rules

I'm trying to create a list of tasks that I've read from some text files and put them into lists. I want to create a master list of what I'm going to do through the day however I've got a few rules for this.
One list has separate daily tasks that don't depend on the order they are completed. I call this list 'daily'. I've got another list of tasks for my projects, but these do depend on the order completed. This list is called 'projects'. I have a third list of things that must be done at the end of the day. I call it 'endofday'.
So here are the basic rules.
A list of randomized tasks where daily tasks can be performed in any order, where project tasks may be randomly inserted into the main list at any position but must stay in their original order relative to each other, and end of day tasks appended to the main list.
I understand how to get a random number from random.randint(), appending to lists, reading files and all that......but the logic is giving me a case of 'hurty brain'. Anyone want to take a crack at this?
EDIT:
Ok I solved it on my own, but at least asking the question got me to picture it in my head. Here's what I did.
random.shuffle(daily)
while projects:
daily.insert(random.randint(0,len(daily)), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
Thanks for the answers, I'll give ya guys some kudos anyways!
EDIT AGAIN:
Crap I just realized that's not the right answer lol
LAST EDIT I SWEAR:
position = []
random.shuffle(daily)
for x in range(len(projects)):
position.append(random.randint(0,len(daily)+x))
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
I LIED:
I just thought about what happens when position has duplicate values and lo and behold my first test returned 1,3,2,4 for my projects. I'm going to suck it up and use the answerer's solution lol
OR NOT:
position = []
random.shuffle(daily)
for x in range(len(projects)):
while 1:
pos = random.randint(0,len(daily)+x)
if pos not in position: break
position.append(pos)
position.sort()
while projects:
daily.insert(position.pop(0), projects.pop(0))
random.shuffle(endofday)
daily.extend(endofday)
for x in daily: print x
First, copy and shuffle daily to initialize master:
master = list(daily)
random.shuffle(master)
then (the interesting part!-) the alteration of master (to insert projects randomly but without order changes), and finally random.shuffle(endofday); master.extend(endofday).
As I said the alteration part is the interesting one -- what about:
def random_mix(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
while True:
it = random.choice(iters)
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
Now, the mixing step becomes just master = list(random_mix(master, projects))
Performance is not ideal (lots of random numbers generated here, we could do with fewer, for example), but fine if we're talking about a few dozens or hundreds of items for example.
This insertion randomness is not ideal -- for that, the choice between the two sequences should not be equiprobable, but rather with probability proportional to their lengths. If that's important to you, let me know with a comment and I'll edit to fix the issue, but I wanted first to offer a simpler and more understandable version!-)
Edit: thanks for the accept, let me complete the answer anyway with a different way of "random mixing preserving order" which does use the right probabilities -- it's only slightly more complicated because it cannot just call random.choice;-).
def random_mix_rp(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while True:
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
try: yield it.next()
except StopIteration:
iters.remove(it)
it = iters[0]
for x in it: yield x
Of course other optimization opportunities arise here -- since we're tracking the lengths anyway, we could rely on a length having gone down to zero rather than on try/except to detect that one sequence is finished and we should just exhaust the other one, etc etc. But, I wanted to show the version closest to my original one. Here's one exploiting this idea to optimize and simplify:
def random_mix_rp1(seq_a, seq_b):
iters = [iter(seq_a), iter(seq_b)]
lens = [len(seq_a), len(seq_b)]
while all(lens):
r = random.randrange(sum(lens))
itindex = r < lens[0]
it = iters[itindex]
lens[itindex] -= 1
yield it.next()
for it in iters:
for x in it: yield x
Use random.shuffle to shuffle a list
random.shuffle(["x", "y", "z"])
How to fetch a random element in a list using python:
>>> import random
>>> li = ["a", "b", "c"]
>>> len = (len(li))-1
>>> ran = random.randint(0, len)
>>> ran = li[ran]
>>> ran
'b'
But it seems you're more curious about how to design this. If so, the python tag should probably not be there. If not, the question is probably to broad to get you any good answers code-wise.
Combine all 3 lists into a DAG
Perform all possible topological sorts, store each sort in a list.
Choose one from the list at random
In order for the elements of the "project" list to stay in order, you could do the following:
Say you have 4 project tasks: "a,b,c,d". Then you know there are five spots where other, randomly chosen elements can be inserted (before and after each element, including the beginning and the end), while the ordering naturally stays the same.
Next, you can add five times a special element (e.g. "-:-") to the daily list. When you now shuffle the daily list, these special items, corresponding to "a,b,c,d" from above, are randomly placed. Now you simply have to insert the elements of the "projects" list sequentially for each special element "-:-". And you keep the ordering, yet have a completely random list regarding the tasks from the daily list.

Categories