简体   繁体   中英

Linq query selects values from first row only in table

I am trying to parse website table to List using Linq.

Here is the source code:

<div id="users_table" class="security_slick_container slickgrid_300610 ui-widget" style="overflow: hidden; outline: 0px; position: relative;">
    <div tabindex="0" hidefocus="" style="position:fixed;width:0;height:0;top:0;left:0;outline:0;"></div>
    <div class="slick-header ui-state-default" style="overflow:hidden;position:relative;">
        <div class="slick-header-columns" style="left: -1000px; width: 2132px;" unselectable="on">
            <div class="ui-state-default slick-header-column slick-header-sortable slick-header-column-sorted" id="slickgrid_300610userName" title="" style="width: 94px;"><span class="slick-column-name"><strong>Username:</strong></span><span class="slick-sort-indicator slick-sort-indicator-asc"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610firstName" title="" style="width: 89px;"><span class="slick-column-name"><strong>Firstname:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610lastName" title="" style="width: 109px;"><span class="slick-column-name"><strong>Lastname:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610type" title="" style="width: 124px;"><span class="slick-column-name"><strong>Type:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610crew" title="" style="width: 109px;"><span class="slick-column-name"><strong>Crew:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610jobTitle" title="" style="width: 109px;"><span class="slick-column-name"><strong>Job title:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610defaultPriceClass" title="" style="width: 124px;"><span class="slick-column-name"><strong>Defaultprice class:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column" id="slickgrid_300610description" title="" style="width: 129px;"><span class="slick-column-name"><strong>Description:</strong></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column" id="slickgrid_300610language" title="" style="width: 39px;"><span class="slick-column-name"><strong>Language:</strong></span>
                <div class="slick-resizable-handle"></div>
            </div>
        </div>
    </div>
    <div class="slick-headerrow ui-state-default" style="overflow: hidden; position: relative; display: none;">
        <div class="slick-headerrow-columns" style="width: 1115px;"></div>
        <div style="display: block; height: 1px; position: absolute; top: 0px; left: 0px; width: 1132px;"></div>
    </div>
    <div class="slick-top-panel-scroller ui-state-default" style="overflow: hidden; position: relative; display: none;">
        <div class="slick-top-panel" style="width:10000px"></div>
    </div>
    <div class="slick-viewport" style="width: 100%; overflow: auto; outline: 0px; position: relative; height: 567px;">
        <div class="grid-canvas" style="height: 16550px; width: 1115px;">
            <div class="ui-widget-content slick-row even" style="top:0px">
                <div class="slick-cell l0 r0">john.smith</div>
                <div class="slick-cell l1 r1">John</div>
                <div class="slick-cell l2 r2">Smith</div>
                <div class="slick-cell l3 r3">Contractor</div>
                <div class="slick-cell l4 r4">Microsoft</div>
                <div class="slick-cell l5 r5">Sales manager</div>
                <div class="slick-cell l6 r6">A</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">en</div>
            </div>
            <div class="ui-widget-content slick-row odd" style="top:25px">
                <div class="slick-cell l0 r0">robert.geits</div>
                <div class="slick-cell l1 r1">Robert</div>
                <div class="slick-cell l2 r2">Geits</div>
                <div class="slick-cell l3 r3">Staff</div>
                <div class="slick-cell l4 r4">Google</div>
                <div class="slick-cell l5 r5">Project manager</div>
                <div class="slick-cell l6 r6">B</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">de</div>
            </div>
            <div class="ui-widget-content slick-row even" style="top:50px">
                <div class="slick-cell l0 r0">amir.rooney</div>
                <div class="slick-cell l1 r1">Amir</div>
                <div class="slick-cell l2 r2">Rooney</div>
                <div class="slick-cell l3 r3">Staff</div>
                <div class="slick-cell l4 r4">Microsoft</div>
                <div class="slick-cell l5 r5">Sales manager</div>
                <div class="slick-cell l6 r6">A</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">en</div>
            </div>
        </div>
    </div>
    <div tabindex="0" hidefocus="" style="position:fixed;width:0;height:0;top:0;left:0;outline:0;"></div>
</div>

What is the right Linq query to perform such an operation? My current Linq query selects only first row from table for some reason?

Here is my current code:

var DataHere = from cells in driver.FindElements(By.XPath("//*[@id=\"users_table\"]")) //.FindElements(By.TagName("div"))
               select new
               {
                   Username = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l0 r0')]")).Text,
                   Firstname = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l1 r1')]")).Text,
                   Lastname = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l2 r2')]")).Text,
                   Type = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l3 r3')]")).Text,
                   Crew = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l4 r4')]")).Text,
                   JobTitle = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l5 r5')]")).Text,
                   DefaultPrice = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l6 r6')]")).Text,
                   Future = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l7 r7')]")).Text,
                   Language = cells.FindElement(By.XPath("//div[contains(@class, 'slick-cell l8 r8')]")).Text,
               };

I am checking values in List using this line:

DataHere.ToList().ForEach(i => Console.Write("{0}\t", i));

Looks to me like your call driver.FindElements(By.XPath("//*[@id=\"users_table\"]")) will get element(s) with id="users_table". Looks like there is only one of those, your top-level "table" <div> . So, your "cells" is not actually a collection of cells, it's just your single top-level "table" <div> .

It looks like you expect each element returned by driver.FindElements(...) to be a <div class="ui-widget-content slick-row..."> element. A simple CSS selector should do the trick:

var rows = driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"));

The LINQ statement would look like:

var DataHere = from row in driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"))
               select new
               {
                   ...
               };

Within each row you need to find "cells" which seem to be <div class="slick-cell"> . Your xpaths all begin with // to find each cell. Those xpaths will be evaluated globally — it will search the entire web page. You want to scope the xpaths to each row. Instead of:

rows.FindElement(By.XPath("//div[contains(@class, 'slick-cell l0 r0')]"))

You want to prefix each xpath with a . character:

      row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l0 r0')]"))
                                ^
                                |
This becomes relative xpath ----+

The . character means Selenium will search within the current row object, which is what you want in this case. If you don't, Selenium returns the very first element in the entire web page that matches the XPath expression.

I would also recommend creating a proper page object model representing each row:

public class UserTableRow
{
    private readonly IWebElement row;

    public string Username => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l0 r0')]")).Text,
    public string Firstname => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l1 r1')]")).Text,
    public string Lastname => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l2 r2')]")).Text,
    public string Type => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l3 r3')]")).Text,
    public string Crew => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l4 r4')]")).Text,
    public string JobTitle => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l5 r5')]")).Text,
    public string DefaultPrice => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l6 r6')]")).Text,
    public string Future => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l7 r7')]")).Text,
    public string Language => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l8 r8')]")).Text,

    public UserTableRow(IWebElement row)
    {
        this.row = row;
    }
}

This will really clean up your LINQ statement:

var DataHere = from row in driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"))
               select new UserTableRow(row);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM