如何修复不兼容的HTML,以便Expat将解析它(htmltidy不工作)

时间:2009-11-29 05:21:13

标签: html xml htmltidy expat-parser

我正试图从http://www.nfl.com/scores抓取信息(特别是,当游戏结束时找出,以便我的电脑可以停止录制)。我可以轻松地下载HTML,并且它声称符合标准:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

但是

  1. 尝试使用Expat解析它会产生错误not well-formed (invalid token)

  2. W3C's online validation service报告399次错误和121次警告。

  3. 我尝试使用tidy选项在我的Linux系统上运行HTML整洁(仅称为-xml),但整齐报告56个警告和117个错误,无法恢复良好的XML文件。错误看起来像这样:

    line 409 column 122 - Warning: unescaped & or unknown entity "&role"
    ...
    line 409 column 172 - Warning: unescaped & or unknown entity "&tabSeq"
    ...
    line 1208 column 65 - Error: unexpected </td> in <br>
    line 1209 column 57 - Error: unexpected </tr> in <br>
    line 1210 column 49 - Error: unexpected </table> in <br>
    

    但是当我检查输入时,“未知实体”似乎是正确引用的URL的一部分,所以我不知道某个地方或某个地方是否缺少双引号。

  4. 我知道那里有某些可以解析这些东西,因为Firefox和w3m都显示出一些合理的东西。 什么工具可以修复不合规的HTML,以便我可以使用Expat解析它?

3 个答案:

答案 0 :(得分:4)

他们在得分盒上使用某种Javascript,所以你将不得不玩更聪明的技巧(换线):

/* box of awesome */
// iscurrentweek ? true;
(new nfl.scores.Game('2009112905','54635',{state:'pre',container:'scorebox-2009112905',
wrapper:'sb-wrapper-2009112905',template:($('scorebox-2009112905').innerHTML),homeabbr:'NYJ',
awayabbr:'CAR'}));

然而,为了回答你的问题,BeautifulSoup解析它(貌似)很好:

fp = urlopen("http://www.nfl.com/scores")
data = ""
while 1:
    r = fp.read()
    if not r:
        break
    data += r
fp.close()

soup = BeautifulSoup(data)
print soup.contents[2].contents[1].contents[1]

输出:

<title>NFL Scores: 2009 - Week 12</title>

在我看来,可能更容易刮掉Yahoo's NFL scoreboard ......事实上,试试吧。


编辑:以您的问题为借口,开始学习BeautifulSoup。 Alex Martelli一直在赞美它,所以我觉得值得一试 - 男人,我印象深刻。

无论如何,我能够从雅虎做出一个基本的分数刮刀!记分牌,如下:

def main():
    soup = BeautifulSoup(YAHOO_SCOREBOARD)
    on_first_team = True
    scores = []
    hold = None

    # Iterate the tr that contains a team's box score
    for item in soup(name="tr", attrs={"align": "center", "class": "ysptblclbg5"}):
        # Easy
        team = item.b.a.string

        # Get the box scores since we're industrious
        boxscore = []
        for quarter in item(name="td", attrs={"class": "yspscores"}):
            boxscore.append(int(quarter.string))

        # Final score
        sub = item(name="span", attrs={"class": "yspscores"})[0]
        if sub.b:
            # Winning score
            final = int(sub.b.string)
        else:
            data = sub.string.replace("&nbsp;", "")
            if ":" in data:
                # Catch TV: XXX and 0:00pm ET
                final = None
            else:
                try: final = int(data)
                except: final = None

        if on_first_team:
            hold = { team : (boxscore, final) }
            on_first_team = False
        else:
            hold[team] = (boxscore, final)
            scores.append(hold)
            on_first_team = True

    for game in scores:
        print "--- Game ---"
        for team in game:
            print team, game[team]

我会在星期天调整一下,看看它是如何运作的,因为它非常粗糙。这是它现在输出的内容:

--- Game ---
Green Bay ([0, 13, 14, 7], 34)
Detroit ([7, 0, 0, 5], 12)
--- Game ---
Oakland ([0, 0, 7, 0], 7)
Dallas ([3, 14, 0, 7], 24)

看看那个,我也抓住了得分......对于一个尚未发生的游戏,我们得到:

--- Game ---
Washington ([], None)
Philadelphia ([], None)

无论如何,一个挂钩让你跳起来。祝你好运。

答案 1 :(得分:3)

在nfl.com的顶部有一个基于Flash的自动更新记分板。对网络流量的一些监控发现:

http://www.nfl.com/liveupdate/scorestrip/ss.xml

这可能比HTML记分板更容易解析。

答案 2 :(得分:2)

查看tagsoup。如果你想在Java中使用DOM树或SAX流,那就是票证。如果你只是想提取具体的信息,美丽的汤是一件美丽的事情。