【问题标题】:Why is my for loop only grabbing first element?为什么我的 for 循环只抓取第一个元素?
【发布时间】:2018-06-22 09:36:11
【问题描述】:

我正在使用 PHP 从网站中获取数据,并且正在尝试根据该数据创建模型。这是我当前的代码:

$dom = new DOMDocument();
$html = file_get_contents('https://www.baseball-reference.com/register/team.cgi?id=41270199');
libxml_use_internal_errors(true);
$dom->loadHTML($html);
$table = $dom->getElementByID('team_batting');
$rows = $table->getElementsByTagName("tr");

for($i = 0; $i < $rows->length; $i++) {

    $stats = $table->getElementsByTagName("td");

    $name = $stats->item($i)->getAttribute('player');
    $age = $stats->item($i)->getAttribute('age');
    $plateAppearances = $stats->item($i)->getAttribute('PA');
    $atBats = $stats->item($i)->getAttribute('AB');
    $hits = $stats->item($i)->getAttribute('H');
    $doubles = $stats->item($i)->getAttribute('2B');
    $triples = $stats->item($i)->getAttribute('3B');
    $homeruns = $stats->item($i)->getAttribute('HR');
    $walks = $stats->item($i)->getAttribute('BB');
    $strikeouts = $stats->item($i)->getAttribute('SO');

    $name = $stats->item(0)->textContent;
    $age = $stats->item(1)->textContent;
    $plateAppearances = $stats->item(3)->textContent;
    $atBats = $stats->item(4)->textContent;
    $hits = $stats->item(6)->textContent;
    $doubles = $stats->item(7)->textContent;
    $triples = $stats->item(8)->textContent;
    $homeruns = $stats->item(9)->textContent;
    $walks = $stats->item(13)->textContent;
    $strikeouts = $stats->item(14)->textContent;

    $player = new Player([
        'name' => $name, 
        'age' => $age, 
        'plateAppearances' => $plateAppearances,
        'atBats' => $atBats,
        'hits' => $hits,
        'doubles' => $doubles,
        'triples' => $triples,
        'homeruns' => $homeruns,
        'walks' => $walks,
        'strikeouts' => $strikeouts
    ]);

    echo $player;
    echo '<br>';

}

这会检索我想要的所有属性,但会导致只有第一个玩家的 19 个实例(总行数),如下所示:

{"name":"Miguel Amaya","age":"19","plateAppearances":"241","atBats":"212","hits":"61","doubles":"14","triples":"2","homeruns":"9","walks":"24","strikeouts":"53"}

为了检索表中的所有玩家而不仅仅是第一个玩家,我可以进行哪些更改,并为每个玩家创建一个player 模型?

编辑/更新:添加了一些我从中提取数据的表

<tr ><th scope="row" class="right " data-stat="ranker" >1</th><td class="left " data-append-csv="player.fcgi?id=amaya-000mig" data-stat="player" csk="Amaya,Miguel" ><a href="/register/player.fcgi?id=amaya-000mig">Miguel Amaya</a></td><td class="right " data-stat="age" >19</td><td class="right " data-stat="G" >59</td><td class="right " data-stat="PA" >241</td><td class="right " data-stat="AB" >212</td><td class="right " data-stat="R" >29</td><td class="right " data-stat="H" >61</td><td class="right " data-stat="2B" >14</td><td class="right " data-stat="3B" >2</td><td class="right " data-stat="HR" >9</td><td class="right " data-stat="RBI" >33</td><td class="right " data-stat="SB" >0</td><td class="right " data-stat="CS" >0</td><td class="right " data-stat="BB" >24</td><td class="right " data-stat="SO" >53</td><td class="right " data-stat="batting_avg" >.288</td><td class="right " data-stat="onbase_perc" >.365</td><td class="right " data-stat="slugging_perc" >.500</td><td class="right " data-stat="onbase_plus_slugging" >.865</td><td class="right " data-stat="TB" >106</td><td class="right " data-stat="GIDP" >3</td><td class="right " data-stat="HBP" >3</td><td class="right " data-stat="SH" >0</td><td class="right " data-stat="SF" >2</td><td class="right " data-stat="IBB" >2</td><td class="right " data-stat="notes" ></td></tr>

【问题讨论】:

  • @Devon 道歉,意思是我的 for 循环,会更正
  • 在正确的代码后面有一段重复的代码。从此代码$name = $stats-&gt;item(0)-&gt;textContent; 开始,您将使用相同的无效值覆盖所有正确值。
  • 您最初将 $i 用于 $stats,但我认为这与 $stats 无关。然后你用硬编码的数字覆盖所有这些......
  • @user2182349 但是如何在没有第二段代码的情况下检索textContent
  • 是的,我认为您需要使用调试器并运行它以查看值是什么。您绝对不想在不使用任何地方的情况下覆盖变量。你可能不想在你所在的地方使用 $i。

标签: php html laravel dom


【解决方案1】:

问题是$stats 没有从循环的当前行获取&lt;td&gt; 元素。您将其设置为 all 表中的 &lt;td&gt; 元素。改变

$stats = $table->getElementsByTagName("td");

到:

$stats = $rows[$i]->getElementsByTagName("td");

然后摆脱所有使用items($i) 的作业。 $i$rows中的索引,与$stats无关。

另外,您需要跳过表格中的标题行,它没有任何&lt;td&gt; 元素。使用它只获取&lt;tbody&gt; 中的行,跳过&lt;thead&gt;

$rows = $table->getElementsByTagName("tbody")->item(0)->getElementsByTagName("tr");

【讨论】:

  • 当我尝试这个时,我在$name = $stats-&gt;item(0)-&gt;textContent; 行得到ErrorException (E_ERROR) Trying to get property 'textContent' of non-object
  • 表格的每一行都有&lt;td&gt;元素吗?可能该表的标题行使用&lt;th&gt; 而不是&lt;td&gt;。您需要跳过该行。
  • 每一行都有&lt;td&gt;元素和&lt;th&gt;元素。在这种情况下我将如何完成?
  • dd($stats-&gt;item(0));null,当我改回$stats = $table-&gt;getElementsByTagName("td"); 时,我得到一个DOMElementnodeName: "td" 在同一个dd
  • 这意味着循环的当前行中没有 &lt;td&gt; 元素。你一定是错的,每一行都有&lt;td&gt; 元素。在问题中发布示例表。
【解决方案2】:

在每次遍历的循环内部都会选择回表的所有 TD 标记。你想要的是一次只扫描一行。我建议更改 foreach 上的循环类型,而不是将行作为上下文,然后在行上下文中查找唯一的“TD”。不是完整的代码,但应该如下所示:

$table = $dom->getElementByID('team_batting');
$rows = $table->getElementsByTagName("tr");
foreach($rows as $row){
  $cols=$row->getElementsByTagName("td");
  foreach($cols as $col){
    $type=$col->getAttribute('data-stat');
    if($type=='player') $name=$col->textContent;
    elseif(if($type=='age') $age=$col->textContent;
    ...
  }
  $player=new Player([
  ...
  ]);
}

这只是一个尝试遵循您的风格的代码概述,但列提取而不是在分离的变量上提取它们,可以通过将它们收集到关联数组中来更有效地完成。

【讨论】:

    猜你喜欢
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多