I'm newbie in C# and specially in Selenium. The code I'm providing works as intended, but I would like to add to it. Basically how to use Selenium to log into Linkedin, search for CURRENT EMPLOYEES of a company (Walmart for this example) and scrape the links for "send inmail" for every user.... clicking the "next" button on every page until there are no more. The following will open linkedin, login, enter walmart into searchbox, then click the next button.
I would like to write the links found within all the Send InMail buttons to a text file. Inspecting one of the Send InMail buttons gives the following:
<a class="primary-action-button label" href="/requestList?displayProposal=&destID=262919732&creationType=DC&authToken=BrmS&authType=name&trk=vsrp_people_res_pri_act&* amp;trkInfo=VSRPsearchId%3A5225861601486589992400%2CVSRPtargetId%3A262919732%2CVSRPcmpt%3Aprimary">Send InMail</a>
I would like to write all of these links into a text file as it cycles through all the "next" buttons. Also, I would like to know how to select a value from a drop down list that I haven't been able to "inspect" ... When you type "Walmart" or whatever into the search box, a drop-down gives you the option of selecting "people that currently work at walmart", etc. I haven't even been able to inspect that option in developer mode for some reason.
I've updated my code... This now SEEMS to WANT to do what i need...but there seems to be a timing issue where the "next" button might be loading before the "Send InMail" buttons...it will print a few results to the console and clicks next a few times, but then seems to melt down:
// Go to the home page
driver.Navigate().GoToUrl("https://www.linkedin.com");
// Get User Name field, Password field and Login Button
var userNameField = driver.FindElementById("login-email");
var userPasswordField = driver.FindElementById("login-password");
var loginButton = driver.FindElementByXPath("//input[#value='Sign in']");
// Type user name and password
userNameField.SendKeys("me#hotmail.com");
userPasswordField.SendKeys("Password123");
// and click the login button
loginButton.Click();
// perform search
var newSearch = driver.FindElementById("main-search-box");
var searchButton = driver.FindElementByName("search");
// search
newSearch.SendKeys("walmart");
searchButton.Click();
// Get all links from Send InMail buttons
List<IWebElement> elementList = new List<IWebElement>();
elementList.AddRange(driver.FindElements(By.LinkText("Next >")));
if (elementList.Count > 0)
{
foreach(IWebElement item in driver.FindElements(By.LinkText("Send InMail")))
{
Console.WriteLine(item.GetAttribute("href"));
var goForward = driver.FindElementByLinkText("Next >");
goForward.Click();
}
}
Console.ReadLine();
As far as I understand, after you perform search, there will be a search results which will be populated in the list like the ones in the figure attached.
screenshot of the Linkedin search result.
Then iterate through the results using similar method (code sample here is in Java, might be similar in C#)
List<WebElements> results_div = driver.findElemnts(By.xpath("//*[#id="results"]")) // where xpath of the <ul> element
int counter = 2 // because the data starts from id = 2. Refer image.
while(counter <= results_div)
{
WebElement element = driver.findElements(By.xpath("//*[#data-li-position=\"+counter+\"])) //xpath of the <li> element
String anchor_text = element.findElement(By.linkText("Send InMail")).getAtribute("href")
//Write a logic to save the data to a text file
}
Iterate the above until all the results are reached!
Hope it helps.
Edit : Try this, its not a working code! But try it on these grounds. It might help.
#Test(description = "Search the Site with some predefined words after Login and print the href attribute")
public void printUserID()
{
StartPage startPage = new StartPage(driver);
HomePage homePage = startPage.loginIntoAccount(LinkdinAccount.linkedEmail,LinkdinAccount.linkedPassword); // Logs in in to the account
driver.findElement(By.xpath("//*[#id=\"main-search-box\"]")).sendKeys("herbalife");
driver.findElement(By.xpath("//*[#id=\"global-search\"]/fieldset/button")).click();
WebDriverWait wait = new WebDriverWait(driver, 60);
wait.until(ExpectedConditions.visibilityOf(driver.findElement(By.xpath("//*[#id=\"results\"]"))));
List<WebElement> results_div = driver.findElements(By.xpath("//*[#id=\"results\"]/li")); // where xpath of the <ul> element
System.out.println(results_div.size()); //*[#id="results"]
#driver.findElement(By.xpath("//*[#id=\"results-pagination\"]/ul/li[11]/a")).click();
int count = 1;
wait.until(ExpectedConditions.visibilityOf(driver.findElement(By.xpath("//*[#id=\"results\"]"))));
while(driver.findElement(By.xpath("//*[#id=\"results-pagination\"]/ul/li[11]/a")).isDisplayed())
{
while(count <= results_div.size())
{
WebElement element = driver.findElement(By.xpath("//*[#data-li-position=\"" + count + "\"]"));
if(element.findElements(By.linkText("Send Inmail")).size() > 0)
{
String anchor_text = element.findElement(By.linkText("Send InMail")).getAttribute("href");
System.out.println(anchor_text);
}
count ++;
}
//for clicking the next button
driver.findElement(By.xpath("//*[#id=\"results-pagination\"]/ul/li[11]/a")).click()
}
Related
So, i am trying to use Selenium to scrape this webpage that updates every couple seconds. I am looking to click on the most recent link which, in this case, is the link by Edward Researcher. This list will update multiple times and get updated with new links stacking at the top.
My current code basically takes the top most link of the entire page when I want it to take links that are shown on the page starting from the top
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
This will get all the clickable links going top down using a tags to grab the links. My problem is trying to only get links from one place on the webpage and grab that link everytime it updates. Not really sure how to do that since the code and xPath changes with every added link. Any help would be greatly appreciated
The bottom most mark in red is the current link that I inspected that is topmost but when the page gets updated with new Links the two top red marks are where they will be updated in another DIV.
The Code that I have a t the moment works to grab the first link on the page but I would prefer to start at the Logged hits section as shown in the first image. Here is the code that makes it work:
public static void searchAllLinks()
{
//HitForker is labelled as '0'
//PandaCrazy tab is labellled as '1'
//Start index at 1
int listIndex = 1;
//Different numbers need for the first run down the list
bool firstRun = true;
//HashSet to store all IWebElements found on page at runtime
HashSet<IWebElement> allLinks;
//Get browser tabs open at current time
var browserTabs = driver.WindowHandles;
//Switch to HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Grab links from page starting at Logged Hits Most recent Div
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
//Loop through all links in hash
//Hashset contains every link on the page. Only need the middle link to access the content
foreach(IWebElement value in allLinks)
{
if(firstRun == true)
{
//Second link in the hash
if(listIndex == 2)
{
value.Click();
firstRun = false;
listIndex = 0;
whatToClick(value);
}
}
//When linkIndex is 5 then click the value and reset to 0
if(listIndex == 5)
{
value.Click();
whatToClick(value);
listIndex = 0;
}
listIndex++;
}
}
//Method to find which webpage opened on successfull click
public static void whatToClick(IWebElement currentLink)
{
//Grabs the browser handles open
var browserTabs = driver.WindowHandles;
//When the link is clicked on switch to that tab
driver.SwitchTo().Window(driver.WindowHandles[2]);
//2 options
//Hit is not available then add to PC
//Hit is available then accept
try
{
if (driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Click();
driver.Close();
}
else
{
driver.Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
//Switch Back to the HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Catch exception to catch if the hit cannot be accepted
} catch (OpenQA.Selenium.NoSuchElementException e )
{
if (driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Click();
driver.Close();
}
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
catch (OpenQA.Selenium.NoSuchWindowException w)
{
Console.WriteLine("Window Not open");
}
}
Please click the element using relative xpath as follows
driver.FindElement(By.Xpath("//div[#id='log_table']/div[0]/div/div[1]/span[1]//a")).Click()
The above xpath will focus on the first of the table and accesses the hyperlink of that.
I have a Responsive website where it contains 36 buttons .At first glance we can only able to see 12 buttons and I need to validate their text values which I know how to do that .But problem is I need to click next button until it is not visible. So how can I do it with selenium in c#.
Also while clicking next we need to store text for the button in a list or array so i can add all the lists in a new list and do comparison.
Look at below code which i tried
while (respRightNav.Displayed)
{
IList<IWebElement> sportsBtn = Driver.FindElements(By.CssSelector(".sports-buttons-responsive>ul li[class='sport-button']"));
foreach (var item in sportsBtn)
{
string btnText = item.Text.Replace(System.Environment.NewLine, "");
if (btnText == btnText.ToUpper())
{
Console.WriteLine("Sports Button : " + btnText + " is In Upper Case");
}
else
{
Assert.Fail("Sports Button Text is not Upper Case for : " + btnText);
}
}
respRightNav.Click();
if (respRightNav.Displayed.Equals(false))
{
break;
}
Now above is working but i'm stuck at when Next button is not available I still need to run above foreach loop for getting text ( if (respRightNav.Displayed.Equals(false))). means i need to run Foreach loop
Any help would be appreciated.
You could check the ".Displayed" value with a while loop:
IWebElement button;
while((button = driver.FindElementByXPath("//input[.='Next']")).Displayed) {
button.Click();
...
}
Or with FindElementsByXPath in a single call matching the visible element:
ReadOnlyCollection<IWebElement> buttons;
while((buttons = driver.FindElementsByXPath("//input[.='Next' and not(contains(#class,'none'))]")).Count > 0) {
buttons[0].Click();
...
}
I am working on a windows application where I embedded webbroswercontrol. I am trying to post sample message to a open facebook group. I am unable to change value of a textbox with c#. When ever I automate click it says textbox value is null. What would be the fix?
<input type="hidden" autocomplete="off" class="mentionsHidden"
name="xhpc_message" value="lklklkl">
HtmlElement textBox = this.FindControlByName("xhpc_message",
this.webBrowser.Document.All);
//Click Code
var elements = webBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement element in elements)
{
// If there's more than one button, you can check the
//element.InnerHTML to see if it's the one you want
if (element.InnerText.Contains("Post"))
{
if (textBox.InnerText.Trim() == "Write something...")
{
textBox.Focus();
textBox.GetAttribute("value").Equals("Test Message");
IHTMLElement nativeElement = element.DomElement as IHTMLElement;
nativeElement.click();
break;
}
}
}
1) I suggest you to ensure, that textbox is null, not the textBox.InnerText. Usually inner text for elements is null, so its better to check the "placeholder" attribute and update the code with:
// if (textBox.InnerText.Trim() == "Write something...")
if (textBox.GetAttribute("placeholder") == "Write something...")
2) This code doesn't set the value. It gets the value and compares to "Test message".
textBox.GetAttribute("value").Equals("Test Message");
Just use SetValue instead.
textBox.SetAttribute("value", "Test message");
3) Ensure, that all operations are made after page is loaded.
public SomeFormName()
{
...
webBrowser.DocumentCompleted += webBrowser_DocumentCompleted;
}
void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs args)
{
// put your code here
}
4) Not sure, how the FindControlByName is working, so check a simple LINQ query to ensure that textbox is found.
var textbox = webBrowser.Document.All.OfType<HtmlElement>()
.Where(item => item.Name == "xhpc_message")
.FirstOrDefault()
;
Use the code below after the Document complete event has been fired completely in a separate function, after commenting your code.The URL in the webbrowser should be holding the Group Page on which the post is to happen.
private void AfteDocumentLoads()
{
HtmlElementCollection textBox = webBrowser.Document.GetElementsByTagName("textarea").GetElementsByName("xhpc_message");
HtmlElementCollection button = webBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement element in textBox)
{
foreach (HtmlElement btnelement in button)
{
if (btnelement.InnerText == "Post")
{
element.Focus();
element.InnerText = txtPortalUserId.Text.ToString();
btnelement.InvokeMember("Click");
}
}
}
}
I was also stuck as it was not posting earlier because I was using WebBrowser class to get current WebBrowser. Result was that the text was inputted to the Group as a 'dim' Comment. Even if I clicked manually on FB page it would say "This status update appears to be blank. Please write something or attach a link or photo to update your status."
I used the page's webbrowser & it worked cos' it came in proper manner on the page. Also little bit of changes are there in the LOCs
I have a simple code that help me crawl to page 2 of Google result page:
var ie= new IE();
ie.Link(Find.ByText("2")).Click();
All I want is crawling to more next page with the inputed number of page, so that I make a loop like this:
string[] page = null;
for (int i = 0; i < NumOfPage; i++)
{
Array.Resize<string> (ref page, i+1);
page[i] = "\"" + i.ToString() + "\"";
}
int count=2;
while (count<NumOfPage)
{
ie.Link(Find.ByText(page[count])).Click();
count++;
}
But the result is it pause at the first page, no crawling to the next page. It's seem the loop doesn't work. Where is the problem???
I think you should not use Click() method to go to the next page, I recognized that Click() will be performed only when the Link is visible, so you have to scroll the vertical scrollbar to the bottom to show the Link first (scrolling manually or programmatically works as you want). However I think to go to the next page, you can call the method GoTo() instead with the Url got from the found Link. I've tested it OK but the delay between page switches is a little large (about 2 seconds or above). I don't know why you want to do this and would like to know it from you:
for (int i = 2; i < NumOfPage; i++)
{
ie.GoTo(ie.Link(WatiN.Core.Find.ByText(i.ToString())).Url);//Don't need quotes at all.
}
:)
// Setup browser object
var browser = new IE();
var url = "www.google.com";
browser.GoTo(url);
var searchBox = browser.TextField(Find.ByName("q"));
searchBox.Value="Rex";
//click on the search button
var btnSearch = browser.Button(Find.ByValue("Search"));
btnSearch.Click();
//wait for browser to load properly
browser.WaitForComplete();
// Find the navigation menu table
var navigationtable = browser.Table(Find.ById("nav"));
// To go to the second page
var secondpage = navigationtable.Link(Find.ByText("2"));
secondpage.Click();
//wait for browser to load properly
browser.WaitForComplete();
this simply goes to the second page
now if you want to loop through
then
for (int i = 2; i <= 10; i++)
{
var nextpage = navigationtable.Link(Find.ByText("i"));
//check if the link exists
/if yes then click on it
if(nextpage.Exists)
nextpage.click();
browser.waitforComplete
}
Ok, I hope I can explain this well enough.
I have one or more third party Up/Down Spinner+Textbox controls on my page that are black boxes that I can't change the source for.
I want the user to change the UpDownControl contents to choose a quantity and then click a calendar button which will:
Add the quantity of all Up/Down boxes.
Call a javascript popup to display a calendar with the count from step 1 in the url "...calendar.asp?qty=5".
My problem is getting the two steps to execute in the same click. As it stands I can click the button once and it counts
the items and adds them to the popup string and then I have to click it a second time to actually execute the JS popup window.
The code was originally written to "load up" the counts into a second button and then programmatically click it but that looks
like a popup to the browsers since the user didn't click that button.
Here is what I have so far that almost works --
On my page:
<asp:ImageButton ID="btnPrepCal" runat="server" Text="PrepCal" OnClick="btnPrepCal_Click" ImageUrl="~/images/Calendar.gif"/>
In code behind:
public void btnPrepCal_Click(object sender, EventArgs e)
{
StringBuilder sbParams = new StringBuilder();
int TotalQty = 0;
int basketItemCount = 0;
int rowIndex = 0;
string Sku = string.Empty;
foreach (GridViewRow varRow in VariantGrid.Rows)
{
int qnty = GetControlValue(varRow, "Quantity", 0);
if (qnty > 0)
{
basketItemCount++;
string optionList = (string)VariantGrid.DataKeys[rowIndex].Value;
ProductVariant variant = _VariantManager.GetVariantFromOptions(optionList);
if (variant != null)
{
BasketItem basketItem = GetBasketItem(optionList, varRow);
if (basketItem != null)
{
TotalQty += basketItem.Quantity;
Sku = variant.Sku;
}
}
}
rowIndex++;
}
if(Sku.Length > 4) Sku = Sku.Substring(0,4);
sbParams.Append(string.Format("?sku={0}&Qty={1}", Sku, TotalQty));
string popup = string.Empty;
popup = string.Format("window.open('http://trustedtours.org/store/egalaxycalendar.asp{0}','Reservation Calendar','width=265,height=465')",sbParams.ToString());
btnPrepCal.OnClientClick = popup;
}
I'm new to .NET and web programming so I'm probably going at it totally backwards so any help is appreciated. I apologize if it's not clear what I'm trying to do or how. If you need any more info please ask - the rest of the file is a lot of shopping cart mumbo jumbo so I left it out, I hope it's enough.
---- update ----
After looking at the referenced pages I get:
Type cstype = this.GetType();
ClientScriptManager cs = Page.ClientScript;
StringBuilder cstext1 = new StringBuilder();
cstext1.Append("<script type=text/javascript>" + popup + "<script>");
cs.RegisterStartupScript(cstype, "PopupCalendar", cstext1.ToString());
And I believe this is added after I set the value of popup near the bottom of my Click handler above, removing the OnClientClick part, right?
Should this popup the other window on a page reload after clicking the button? (I hate being a newb and asking what's probably obvious questions.)
You can accomplish what you're aiming for using the ClientScriptManager.RegisterStartupScript method. Instead of assigning the OnClientClick method of the button to your JS popup code, set that code to run when the page is reloaded using the RegisterStartupScript method.
This page has some good examples: http://dotnetslackers.com/articles/aspnet/JavaScript_with_ASP_NET_2_0_Pages_Part1.aspx
Ken is correct. To add to his answer and clarify why your code was not working - you were assigning the click-handler of your button to do a popup, but only after it was clicked. This is why you only saw the popup after the 2nd click - the handler was not there the first time you clicked it.