【问题标题】:Why HTML code is different when parsing site using Jsoup than using browser为什么使用 Jsoup 解析站点时 HTML 代码与使用浏览器不同
【发布时间】:2017-09-05 02:06:06
【问题描述】:

我在网站http://www.flashscore.com/nhl/ 上,我正在尝试提取“今日比赛”表的链接。

我正在用下面的代码试一下,但是还是不行你能指出错误在哪里吗?

  final Document page = Jsoup
    .connect("http://d.flashscore.com/x/feed/t_4_200_G2Op923t_1_en_1")
    .cookie("_ga","GA1.2.47011772.1485726144")
    .referrer("http://d.flashscore.com/x/feed/proxy-local")
    .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36")
    .header("X-Fsign", "SW9D1eZo")
    .header("X-GeoIP", "1")
    .header("X-Requested-With", "XMLHttpRequest")
    .header("Accept" , "*/*")
    .get();

for (Element game : page.select("table.hockey tr")) {
Elements links = game.getElementsByClass("tr-first stage-finished");
for (Element link : links) {
    String linkHref = link.attr("href");
    String linkText = link.text();
}
 }

为了尝试修复它,我开始调试它。它表明我们得到了页面(尽管我们得到了一种奇怪的 HTML)。之后调试显示 for 循环甚至没有启动。我试图将 page.select("") 部分更改为不同的部分(如 getElementByAttribute 等),但我刚刚开始学习网络抓取,所以我需要熟悉这些方法来浏览文档。我应该如何提取这些数据?

【问题讨论】:

    标签: java web-scraping css-selectors jsoup data-extraction


    【解决方案1】:

    正如 cmets 中所说,该网站需要执行一些 Javascript 才能构建可链接元素。 Jsoup 只解析 HTML,它不运行任何 JS,如果您从浏览器获取或从 Jsoup 获取,您将不会看到相同的 HTML 源代码。

    您需要像在真正的浏览器上运行网站一样获取网站。您可以使用WebDriverFirefox 以编程方式执行此操作。

    我已尝试使用您的示例网站并且可以正常工作:

    pom.xml

    <project>
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
      <plugins>
        <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    <packaging>jar</packaging>
    
    <name>test</name>
    <url>http://maven.apache.org</url>
    
    <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
      <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-firefox-driver</artifactId>
        <version>2.43.0</version>
      </dependency>
    </dependencies>
    
    </project>
    

    App.java

    package com.test;
    
    import org.openqa.selenium.By;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.firefox.FirefoxDriver;
    import java.util.Collections;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class App {
    
    public static void main( String[] args ) {
        App app = new App();
        List<String> links = app.parseLinks();
        links.forEach(System.out::println);
    }
    
    public List<String> parseLinks() {
        try {
            WebDriver driver ;
            // should download geckodriver https://github.com/mozilla/geckodriver/releases and set according your local file
            System.setProperty("webdriver.firefox.marionette","C:\\apps\\geckodriver.exe");
            driver = new FirefoxDriver();
            String baseUrl = "http://www.flashscore.com/nhl/";
    
            driver.get(baseUrl);
    
            return driver.findElement(By.className("hockey"))
                    .findElements(By.tagName("tr"))
                    .stream()
                    .distinct()
                    .filter(we -> !we.getAttribute("id").isEmpty())
                    .map(we -> createLink(we.getAttribute("id")))
                    .collect(Collectors.toList());
    
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.EMPTY_LIST;
        }
    }
    
    private String createLink(String id) {
        return String.format("http://www.flashscore.com/match/%s/#match-summary", extractId(id));
    }
    
    private String extractId(String id) {
        if (id.contains("x_4_")) {
            id = id.replace("x_4_","");
        } else if (id.contains("g_4_")) {
            id = id.replace("g_4_","");
        }
    
        return id;
    }
    }
    

    输出:

    http://www.flashscore.com/match/f9MJJI69/#match-summary
    http://www.flashscore.com/match/zZCyd0dC/#match-summary
    http://www.flashscore.com/match/drEXdts6/#match-summary
    http://www.flashscore.com/match/EJOScMRa/#match-summary
    http://www.flashscore.com/match/0GKOb2Cg/#match-summary
    http://www.flashscore.com/match/6gLKarcm/#match-summary
    ...
    ...
    

    PS:使用 Firefox 32.0 和 Selenium 2.43.0 工作。在 Selenium 和 Firefox 之间使用不受支持的版本是一个常见错误。

    【讨论】:

    • 嗨@exoddues,非常感谢您的解决方案,它就像魅力一样。你能告诉我如何只过滤掉那些有今天日期的人吗?所以让我们说今天的日期是变量“字符串日期”。我想我应该以某种方式使用'.filter()'。
    • 乍一看似乎“今天的比赛”被放置在一个 id="fscountry" 的 div 中。例如,一种方法是做一个过滤器,获取 id="fscountry" 的 div 内的 tr 元素。尝试使用而不是前两个 .findElement 调用使用类似 .findElement(By.id("fscountry")).findElements(By.tagName("tr")
    • 嗨@exoddus,这是一个很好的提示。使用“fscountry”它不起作用,但使用“fs”。如果您检查 te 元素,您就会明白为什么。在“今天的比赛”表中,总是有两个元素具有相同的 id(两行,上面的主队,下面的客队),我改变了这样的事情:... .collections(toSet())" 所以我得到了同一个 id 只有一次。我不知道这是否是最好的解决方案,但它有效。
    【解决方案2】:

    .connect("http://d.flashscore.com/x/feed/t_4_200_G2Op923t_1_en_1") 中的地址有误 - 您需要在其中使用 .connect("http://www.flashscore.com/nhl/")

    然后,该站点使用 JS,并且在您获得正确的页面后 - 它的呈现方式将与在浏览器中不同,例如不会有一张“曲棍球”类的桌子。您将在您将获得的页面中看到它。 因此,您需要更改定位器。 或者考虑为此使用WebDriver

    【讨论】:

    • 你是对的,我更改了 .connect 中的字符串,现在我得到了正确的 HTML。谢谢。 :) 你知道我应该如何编写 for 循环来提取游戏的链接吗?
    • 结果被包装到不包含任何链接的div id="tournament-page-data-summary-results" 中。我认为JSoup不可能。尝试使用任何支持 JS 的 WebDriver 实现,例如 ChromeDriver 或 FirefoxDriver
    猜你喜欢
    • 1970-01-01
    • 2020-04-13
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 2013-11-25
    • 1970-01-01
    相关资源
    最近更新 更多