在多行上使用正则表达式进行 Html 解析

Posted

技术标签:

【中文标题】在多行上使用正则表达式进行 Html 解析【英文标题】:Html Parsing with regex over multiple lines 【发布时间】:2020-03-05 00:10:15 【问题描述】:

目前我正在用 Java 编写一个程序,我必须在其中解析一个 html 文件并从表中获取所有名称。我必须用纯Java编写,所以我不能使用Jsoup或类似的东西。

html的sn-p在这里:

<table class="wikitable toptextcells" >
<tbody><tr>
<th >Comic
</th>
<th >Film
</th></tr>
<tr>
<td colspan="2" class="hintergrundfarbe2">
<h3><span id="0.E2.80.939"></span><span class="mw-headline" id="0–9"><span id="Real-0–9"></span> 0–9</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;veaction=edit&amp;section=2" class="mw-editsection-visualeditor" title="Abschnitt bearbeiten: 0–9">Bearbeiten</a><span class="mw-editsection-divider"> | </span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;action=edit&amp;section=2" title="Abschnitt bearbeiten: 0–9">Quelltext bearbeiten</a><span class="mw-editsection-bracket">]</span></span></h3>
</td></tr>
<tr>
<td>2 Guns
</td>
<td><a href="https://de.wikipedia.org/wiki/2_Guns" title="2 Guns">2 Guns</a> (2013)
</td></tr>
<tr>
<td>5 ist die perfekte Zahl (von <a href="https://de.wikipedia.org/wiki/Igort" title="Igort">Igort</a>)
</td>
<td>5 è il numero perfetto (2019)
</td></tr>
<tr>
<td>13 rue de l'Espoir (von <a href="https://de.wikipedia.org/wiki/Paul_Gillon" title="Paul Gillon">Paul Gillon</a>)
</td>
<td>Die tolle Masche (1961)
</td></tr>
<tr>
<td rowspan="2"><a href="https://de.wikipedia.org/wiki/XIII_(Comicserie)" title="XIII (Comicserie)">XIII</a>
</td>
<td><a href="https://de.wikipedia.org/wiki/XIII_%E2%80%93_Die_Verschw%C3%B6rung" title="XIII – Die Verschwörung">XIII – Die Verschwörung</a> (Fernseh-Zweiteiler, 2008)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/XIII_%E2%80%93_Die_Verschw%C3%B6rung_(Fernsehserie)" title="XIII – Die Verschwörung (Fernsehserie)">XIII – Die Verschwörung</a> (Fernsehserie, 2011–2012)
</td></tr>
<tr>
<td rowspan="3"><a href="https://de.wikipedia.org/wiki/20th_Century_Boys" title="20th Century Boys">20th Century Boys</a>
</td>
<td>20th Century Boys 1 (2008)
</td></tr>
<tr>
<td>20th Century Boys 2 (2009)
</td></tr>
<tr>
<td>20th Century Boys 3 (2009)
</td></tr>
<tr>
<td rowspan="2">30 Days of Night
</td>
<td><a href="https://de.wikipedia.org/wiki/30_Days_of_Night" title="30 Days of Night">30 Days of Night</a> (2007)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/30_Days_of_Night:_Dark_Days" title="30 Days of Night: Dark Days">30 Days of Night: Dark Days</a> (2010)
</td></tr>
<tr>
<td rowspan="2">47:an Löken (von Lennart Elworth)
</td>
<td>47:an Löken (1971)
</td></tr>
<tr>
<td>47:an Löken blåser på (1972)
</td></tr>
<tr>
<td rowspan="8">91:an Karlsson (von <a href="https://de.wikipedia.org/wiki/Rudolf_Petersson" title="Rudolf Petersson">Rudolf Petersson</a>)
</td>
<td>Aber warum, Herr Feldwebel? (1946)
</td></tr>
<tr>
<td>Zurück, marsch-marsch! (1947)
</td></tr>
<tr>
<td>Rekruten rechts 'raus (1951)
</td></tr>
<tr>
<td>Alla tiders 91:an Karlsson (1953)
</td></tr>
<tr>
<td>91 Karlsson rycker in (1955)
</td></tr>
<tr>
<td>91:an Karlsson slår knock out (1957)
</td></tr>
<tr>
<td>91:an Karlsson muckar (tror han) (1959)
</td></tr>
<tr>
<td>91:an och generalernas fnatt (1977)
</td></tr>
<tr>
<td rowspan="2"><a href="https://de.wikipedia.org/wiki/300_(Comic)" title="300 (Comic)">300</a>
</td>
<td><a href="https://de.wikipedia.org/wiki/300_(Film)" title="300 (Film)">300</a> (2007)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/300:_Rise_of_an_Empire" title="300: Rise of an Empire">300: Rise of an Empire</a> (2014)
</td></tr>
<tr>
<td colspan="2" class="hintergrundfarbe2">
<h3><span class="mw-headline" id="A"><span id="Real-A"></span> A</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;veaction=edit&amp;section=3" class="mw-editsection-visualeditor" title="Abschnitt bearbeiten: A">Bearbeiten</a><span class="mw-editsection-divider"> | </span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;action=edit&amp;section=3" title="Abschnitt bearbeiten: A">Quelltext bearbeiten</a><span class="mw-editsection-bracket">]</span></span></h3>
</td></tr>
<tr>
<td>Abattoir (Radical Studios)
</td>
<td>Abattoir – Er erwartet dich! (2016)
</td></tr>
<tr>
<td>Ace Drummond (von <a href="https://de.wikipedia.org/wiki/Edward_Vernon_Rickenbacker" title="Edward Vernon Rickenbacker">Eddie Rickenbacker</a> und Clayton Knight)
</td>
<td>Ace Drummond (Serial, 1936)
</td></tr>
<tr>
<td>Accident Man (von Pat Mills u.&nbsp;a.)
</td>
<td>Accident Man (2018)
</td></tr>
<tr>
<td>Ada im Dschungel (von <a href="https://de.wikipedia.org/wiki/Francesco_Tullio_Altan" title="Francesco Tullio Altan">Altan</a>)
</td>
<td>Ada dans la jungle (1988)
</td></tr>
<tr>
<td rowspan="6"><a href="https://de.wikipedia.org/wiki/The_Addams_Family" title="The Addams Family">The Addams Family</a> (Zeitungscartoons)
</td>
<td>Die Addams Family (Fernsehserie, 1964–1966)
</td></tr>
<tr>
<td>Halloween with the New Addams Family (1977)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Addams_Family_(1991)" title="Addams Family (1991)">Addams Family</a> (1991)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Die_Addams_Family_in_verr%C3%BCckter_Tradition" title="Die Addams Family in verrückter Tradition">Die Addams Family in verrückter Tradition</a> (1993)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Addams_Family_%E2%80%93_Und_die_lieben_Verwandten" title="Addams Family – Und die lieben Verwandten">Addams Family – Und die lieben Verwandten</a> (1998)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Die_neue_Addams_Familie" title="Die neue Addams Familie">Die neue Addams Familie</a> (Fernsehserie, 1998–1999)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Adeles_ungew%C3%B6hnliche_Abenteuer" title="Adeles ungewöhnliche Abenteuer">Adeles ungewöhnliche Abenteuer</a>
</td>
<td><a href="https://de.wikipedia.org/wiki/Ad%C3%A8le_und_das_Geheimnis_des_Pharaos" title="Adèle und das Geheimnis des Pharaos">Adèle und das Geheimnis des Pharaos</a> (2010)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Figuren_aus_dem_Marvel-Universum#Margaret_%E2%80%9EPeggy%E2%80%9C_Carter" title="Figuren aus dem Marvel-Universum">Agent Carter</a>
</td>
<td><a href="https://de.wikipedia.org/wiki/Marvel%E2%80%99s_Agent_Carter" title="Marvel’s Agent Carter">Marvel’s Agent Carter</a> (Fernsehserie, 2015–2016)
</td></tr>
<tr>
<td>Air Hawk (von John Dixon)
</td>
<td>Air Hawk (1981)
</td></tr>
<tr>
<td>Alena (von Kim W. Andersson)
</td>
<td>Alena (2015)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Alexander_Nikopol" title="Alexander Nikopol">Alexander Nikopol</a> (<i>Die Geschäfte der Unsterblichen</i>)
</td>
<td><a href="https://de.wikipedia.org/wiki/Immortal_%E2%80%93_New_York_2095:_Die_R%C3%BCckkehr_der_G%C3%B6tter" title="Immortal – New York 2095: Die Rückkehr der Götter">Immortal – New York 2095: Die Rückkehr der Götter</a> (2004)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Allein_(Comic)" title="Allein (Comic)">Allein</a>
</td>
<td>Seuls (2017)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Der_allt%C3%A4gliche_Kampf" title="Der alltägliche Kampf">Der alltägliche Kampf</a> (von <a href="https://de.wikipedia.org/wiki/Manu_Larcenet" title="Manu Larcenet">Manu Larcenet</a>)
</td>
<td>Le Combat ordinaire (2015)
</td></tr>
<tr>
<td rowspan="3">Ally Sloper (von Marie Duval und C. H. Ross)
</td>
<td>Ally Sloper (Kurzfilm, 1898)
</td></tr>
<tr>
<td>Sloper's Visit to Brighton (Kurzfilm, 1898)
</td></tr>
<tr>
<td>Ally Sloper (Kurzfilm-Serie, 1921)
</td></tr>
<tr>
<td rowspan="2"><a href="https://de.wikipedia.org/w/index.php?title=Along_with_the_Gods_(Webtoon)&amp;action=edit&amp;redlink=1" class="new" title="Along with the Gods (Webtoon) (Seite nicht vorhanden)">Along with the Gods</a>
</td>
<td><a href="https://de.wikipedia.org/wiki/Along_with_the_Gods:_The_Two_Worlds" title="Along with the Gods: The Two Worlds">Along with the Gods: The Two Worlds</a> (2017)
</td></tr>
<tr>
<td><a href="https://de.wikipedia.org/wiki/Along_with_the_Gods:_The_Last_49_Days" title="Along with the Gods: The Last 49 Days">Along with the Gods: The Last 49 Days</a> (2018)
</td></tr>
<tr>
<td>Alphonse and Gaston (von <a href="https://de.wikipedia.org/wiki/Frederick_Burr_Opper" title="Frederick Burr Opper">Frederick Burr Opper</a>)
</td>
<td>Alphonse and Gaston (Kurzfilm-Serie, 1902–1903)
</td></tr>
</tbody></table>
<h2><span class="mw-headline" id="Anmerkung_zu_Trickfilmen_und_Comiczeichnern">Anmerkung zu Trickfilmen und Comiczeichnern</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;veaction=edit&amp;section=57" class="mw-editsection-visualeditor" title="Abschnitt bearbeiten: Anmerkung zu Trickfilmen und Comiczeichnern">Bearbeiten</a><span class="mw-editsection-divider"> | </span><a href="https://de.wikipedia.org/w/index.php?title=Liste_von_Comicverfilmungen&amp;action=edit&amp;section=57" title="Abschnitt bearbeiten: Anmerkung zu Trickfilmen und Comiczeichnern">Quelltext bearbeiten</a><span class="mw-editsection-bracket">]</span></span></h2>

起初我只想匹配其中有两个&lt;td&gt;&lt;tr&gt;,但我不知道为什么它不起作用。 我在 www.regex101.com 上检查了我的正则表达式,似乎没问题。但在 Java 中,我必须用 .* 替换 \n?甚至得到一场比赛。 这是我的 Java 代码:

import utils.YearInterval;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Parser 
    private Pattern comicBegin = Pattern.compile("<h3><span id=\"0\\.E2\\.80\\.939\"></span>.*?</h3>", Pattern.MULTILINE | Pattern.DOTALL);
    private Pattern comicEnd = Pattern.compile("<h2><span class=\"mw-headline\" id=\"Anmerkung_zu_Trickfilmen_und_Comiczeichnern\">.*?</h2>", Pattern.MULTILINE | Pattern.DOTALL); 
    private Pattern comicEnum = Pattern.compile("<tr>.*?<td.*?>(?:<a.*?\">)?(.*?)(?:</a.*?>)?.*?</td>.*?<td>(?:<a.*?>)?(.*?)(?:</a>)?.*?</td></tr>", Pattern.MULTILINE | Pattern.DOTALL);  // Tried also this <tr>\\n<td.*?>(?:<a.*?">)?(.*?)(?:</a.*?>)?\\n</td>\\n<td>(?:<a.*?>)?(.*?)(?:</a>)?\\n</td></tr>
    private Scanner wikiComicFilmScanner = null;

    public static void main(String[] args) throws MalformedURLException, IOException 
        Path wikiComicFilmLocal = Paths.get("ressources/ListevonComicverfilmungen.html");
        Parser wp1 = new Parser("file:///" + wikiComicFilmLocal.toAbsolutePath());
        long start = System.currentTimeMillis();
        Map<String, Map<YearInterval, List<String>>> comicMap = wp1.contentToComicFilmsPerYear();
        System.out.println("Parsen ausgeführt in: " + (System.currentTimeMillis() - start) + "ms");
    

    public Parser(String uri) throws MalformedURLException, IOException 
        wikiComicFilmScanner = new Scanner(new URL(uri).openStream(), "UTF-8");
    


    public Map<String, Map<YearInterval, List<String>>> contentToComicFilmsPerYear() 
        Map<String, Map<YearInterval, List<String>>> comicMap = new HashMap<>();
        wikiComicFilmScanner.useDelimiter(comicBegin);
        if (wikiComicFilmScanner.hasNext()) 
            wikiComicFilmScanner.next();
        
        wikiComicFilmScanner.useDelimiter(comicEnd);
        if (wikiComicFilmScanner.hasNext()) 
            String filmsPerYearEnumeration = wikiComicFilmScanner.next();
            Matcher matcherEnum = comicEnum.matcher(filmsPerYearEnumeration);
            while (matcherEnum.find()) 
                String comicTitle = matcherEnum.group(0);
                String filmTitle = matcherEnum.group(2);
                System.out.println("---------------------------------");
                System.out.println(comicTitle);
                System.out.println(filmTitle);
            
        
        return comicMap;
    


非常感谢!

【问题讨论】:

请问为什么要求纯Java?重新发明***与 DRY 完全矛盾。 这是为了大学运动。所以我们不允许使用其他东西。 ;) 正则表达式详细信息因每种语言而异。您应该使用专门针对 Java 的正则表达式测试器,如下所示:freeformatter.com/java-regex-tester.html 【参考方案1】:

根据 HTML 的来源,新行可能不仅是 \n(Unix、新 Mac),还可能是 \r(古代 Mac)或 \r\n(Windows)。请注意,这取决于 HTML 的创建者,不一定是您自己的系统。

根据Regular Expression to match cross platform newline characters,用于独立于平台的换行符的建议表达式是"\r\n?|\n"

这将匹配以下任何一项:

\r\n \r \n

由于在 HTML 中您可能不关心出现的次数、顺序或可能的混合,您也可以使用 [\r\n]*,这样可以避免为每个事件创建一个捕获组。

编辑:

正如@Toto 所指出的,您甚至可以更简单地使用\R。 所以\R* 应该为你解决问题。

【讨论】:

这就是\R 的用途。它匹配所有类型的换行符regular-expressions.info/nonprint.html 没错,出于某种原因,我忽略了这一点,尽管一直使用它。谢谢。

以上是关于在多行上使用正则表达式进行 Html 解析的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 在多行字符串上使用正则表达式

如何在多行上使用 JavaScript 正则表达式?

Python: 正则表达式匹配多行,实现多行匹配模式

Python 使用正则表达式解析 HTML

使用正则表达式解析日志文件

Python从零开始写爬虫-2 使用正则表达式解析HTML