Schreiben Sie relative Links in HTML in absolute Links mit Python (lxml) um

Schreiben Sie relative Links in HTML in absolute Links mit Python (lxml) um

lxml

Dies ist eine praktische Bibliothek für den Umgang mit HTML (XML).

lxml - Processing XML and HTML with Python(http://lxml.de/)

lxml is the most feature-rich and easy-to-use library for processing XML and HTML in the Python language.

Mit lxml können Sie ganz einfach schreiben, wie alle relativen Links in HTML in absolute Links umgeschrieben werden.

Make_links_absolute () ist gut, wenn alle Links zu absoluten Links umgeschrieben werden

Verwenden Sie lxml.html.make_links_absolute (). Angenommen, Sie haben das folgende HTML (a.html).

a.html

<html>
  <head>
    <style type="text/css">
      .download {background-image:url(./images/download.png);}
    </style>
    <script src="./js/lib.js" type="text/javascript"/>
    <script src="./js/app.js" type="text/javascript"/>
  </head>
  <body>
    <img src="images/icon.png " alt="image"/>
    <a class="download "href="./download">download</a>
  </body>
</html>

Führen Sie den folgenden Code aus. Bitte geben Sie die Basis-URL an base_url an.

from lxml import html

with open("./a.html", "r") as rf:
    doc = html.parse(rf).getroot()
    html.make_links_absolute(doc, base_url="http://example.net/foo/bar")
    print html.tostring(doc, pretty_print=True)

Es kann wie folgt als absoluter Link umgeschrieben werden.

<html>
<head>
<style type="text/css">
      .download {background-image:url(http://example.net/foo/images/download.png);}
    </style>
<script src="http://example.net/foo/js/lib.js" type="text/javascript"></script><script src="http://example.net/foo/js/app.js" type="text/javascript"></script>
</head>
<body>
    <img src="http://example.net/foo/images/icon.png " alt="image"><a class="download " href="http://example.net/foo/download">download</a>
  </body>
</html>

Es ist wunderbar, dass die folgenden drei auch als Links interpretiert werden.

Wenn Sie etwas komplizierteres tun möchten, verwenden Sie rewrite_links ()

Beispielsweise möchten Sie möglicherweise alle Links in absolute Links umschreiben, aber nur die js-Dateien als relative Links belassen.

Werfen wir einen Blick auf die Implementierung von make_links_absolute ().

## lxml-3.2.1-py2.7-linux-x86_64.egg/lxml/html/__init__.py

class HtmlMixin(object):
#...
    def make_links_absolute(self, base_url=None, resolve_base_href=True):
        """
        Make all links in the document absolute, given the
        ``base_url`` for the document (the full URL where the document
        came from), or if no ``base_url`` is given, then the ``.base_url`` of the document.

        If ``resolve_base_href`` is true, then any ``<base href>``
        tags in the document are used *and* removed from the document.
        If it is false then any such tag is ignored.
        """
        if base_url is None:
            base_url = self.base_url
            if base_url is None:
                raise TypeError(
                    "No base_url given, and the document has no base_url")
        if resolve_base_href:
            self.resolve_base_href()
        def link_repl(href):
            return urljoin(base_url, href)
        self.rewrite_links(link_repl)

rewrite_links () wird verwendet. Mit anderen Worten, Sie sollten dies verwenden.

Angenommen, Sie möchten einen anderen Link als den Pfad, der auf js im vorherigen HTML-Code verweist, in einen absoluten Link ändern.

from lxml import html
from urlparse import urljoin
import functools

def repl(base_url, href):
    if href.endswith(".js"):
       return href
    else:
        return urljoin(base_url, href)

with open("./a.html", "r") as rf:
    doc = html.parse(rf).getroot()   
    base_url="http://example.net/foo/bar"
    doc.rewrite_links(functools.partial(repl, base_url))
    print html.tostring(doc, pretty_print=True)

Der Link zu js (src-Attribut des Skript-Tags) bleibt ein relativer Link.

<html>
<head>
<style type="text/css">
      .download {background-image:url(http://example.net/foo/images/download.png);}
    </style>
<script src="./js/lib.js" type="text/javascript"></script><script src="./js/app.js" type="text/javascript"></script>
</head>
<body>
    <img src="http://example.net/foo/images/icon.png " alt="image"><a class="download " href="http://example.net/foo/download">download</a>
  </body>
</html>

Vorsichtsmaßnahmen beim Umschreiben von HTML mit Mehrbyte-Zeichen

Wie oben erwähnt, ist die Verwendung von lxml praktisch, da Sie leicht von einem relativen Link zu einem absoluten Link umschreiben können. Sie müssen ein wenig vorsichtig sein, wenn Sie versuchen, HTML zu konvertieren, das Multi-Byte-Zeichen wie Japanisch enthält.

Angenommen, Sie haben das folgende HTML (b.html).

b.html

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
Japanische Zeichenkette
    <p>AIUEO</p>
  </body>
</html>

Lassen Sie uns dies mit lxml konvertieren.

# -*- coding:utf-8 -*-
from lxml import html

with open("./b.html", "r") as rf:
    doc = html.parse(rf).getroot()
    print html.tostring(doc, pretty_print=True)

Das Ausgabeergebnis ist wie folgt.

<html>
<head></head>
<body>
    &#26085;&#26412;&#35486;&#12398;&#25991;&#23383;&#21015;
    <p>&#12354;&#12356;&#12358;&#12360;&#12362;</p>
  </body>
</html>

Wenn das generierte HTML mit einem Browser angezeigt wird, wird es als beabsichtigte Zeichenfolge angezeigt. Wenn ich dieses HTML jedoch mit einem Editor usw. öffne, wird es in eine Liste mit mehreren Zahlen und Symbolen konvertiert. In HTML gibt es zwei Arten der Zeichendarstellung: "Zeichenentitätsreferenz" und "numerische Zeichenreferenz". Dies liegt daran, dass die in der ersteren übergebene Zeichenfolge in die letztere konvertiert wurde, wenn sie an lxml übergeben wird. (Einzelheiten finden Sie unter http://www.asahi-net.or.jp/~sd5a-ucd/rec-html401j/charset.html#h-5.3.1)

Dies ist genau das gleiche Format wie beim Konvertieren einer Unicode-Zeichenfolge in einen Wert vom Typ str in Python wie folgt.

print(u"AIUEO".encode("ascii", "xmlcharrefreplace"))
## &#12354;&#12356;&#12358;&#12360;&#12362;

In der Hilfe für encode () wird dies ebenfalls erwähnt.

$ python
Python 2.7.3 (default, Sep 26 2012, 21:51:14) 
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> help("".encode)
Help on built-in function encode:

encode(...)
    S.encode([encoding[,errors]]) -> object
    
    Encodes S using the codec registered for encoding. encoding defaults
    to the default encoding. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
    'xmlcharrefreplace' as well as any other name registered with
    codecs.register_error that is able to handle UnicodeEncodeErrors.

Die Geschichte geht zurück. Wenn Sie HTML mit Multibyte-Zeichen an lxml übergeben möchten, Geben Sie die String-Codierung ein.

# -*- coding:utf-8 -*-
from lxml import html

with open("./b.html", "r") as rf:
    doc = html.parse(rf).getroot()
    print html.tostring(doc, pretty_print=True, encoding="utf-8")

Es wurde in einem Format ausgegeben, das von Menschen gelesen werden kann.

<html>
<head></head>
<body>
Japanische Zeichenkette
    <p>AIUEO</p>
  </body>
</html>

Es wird noch etwas länger dauern.

Fortsetzung, Vorsichtsmaßnahmen beim Umschreiben von HTML mit Mehrbyte-Zeichen

Soweit ich mich im Internet umgesehen habe, haben viele von ihnen die Codierung an lxml.html.tostring übergeben. Es gibt noch ein paar Fallen.

Im vorherigen Beispiel wurde die Codierungsspezifikation zu HTML hinzugefügt. Lxml gibt jedoch eine seltsame Ausgabe zurück, wenn HTML ohne diese übergeben wird. (Obwohl gesagt werden kann, dass die Existenz von HTML ohne Kodierung an sich schlecht ist. Der Hauptanspruch ist vor der Realität machtlos)

HTML (d.html) ohne angegebene Codierung

<html>
  <head>
  </head>
  <body>
Japanische Zeichenkette
    <p>AIUEO</p>
  </body>
</html>

Das Ergebnis ist wie folgt.

<html>
<head></head>
<body>
    日本語の文字列
    <p>あいうえお</p>
  </body>
</html>

Lassen Sie uns die Ursache untersuchen. Werfen wir einen Blick auf die Implementierung von lxml.html.parse und lxml.html.tostring. Da ich die Codierung früher bestanden habe, kann ich mir vorstellen, dass der String bereits unterstützt wird, daher werde ich mir die Analyse ansehen.

## lxml-3.2.1-py2.7-linux-x86_64.egg/lxml/html/__init__.py

def parse(filename_or_url, parser=None, base_url=None, **kw):
    """
    Parse a filename, URL, or file-like object into an HTML document
    tree.  Note: this returns a tree, not an element.  Use
    ``parse(...).getroot()`` to get the document root.

    You can override the base URL with the ``base_url`` keyword.  This
    is most useful when parsing from a file-like object.
    """
    if parser is None:
        parser = html_parser
    return etree.parse(filename_or_url, parser, base_url=base_url, **kw)

Es scheint, dass es ein Argument namens Parser empfängt und standardmäßig ein Parser namens html_parser übergeben wird.

## lxml-3.2.1-py2.7-linux-x86_64.egg/lxml/html/__init__.py

from lxml import etree

# ..snip..

class HTMLParser(etree.HTMLParser):
    """An HTML parser that is configured to return lxml.html Element
    objects.
    """
    def __init__(self, **kwargs):
        super(HTMLParser, self).__init__(**kwargs)
        self.set_element_class_lookup(HtmlElementClassLookup())

# ..snip..

html_parser = HTMLParser()
xhtml_parser = XHTMLParser()

Eine Klasse, die von lxml.etree.HTMLParser erbt, ist als HTMLParser definiert. Der Standardparser scheint diese Instanz zu sein. Etree.HTMLParser scheint übrigens eine in C geschriebene Klasse zu sein.

## lxml-3.2.1-py2.7-linux-x86_64.egg/lxml/etree/__init__.py

def __bootstrap__():
   global __bootstrap__, __loader__, __file__
   import sys, pkg_resources, imp
   __file__ = pkg_resources.resource_filename(__name__,'etree.so')
   __loader__ = None; del __bootstrap__, __loader__
   imp.load_dynamic(__name__,__file__)
__bootstrap__()

Werfen wir einen Blick auf die Dokumentation (lxml.etree.HTMLParse: http://lxml.de/3.1/api/lxml.etree.HTMLParser-class.html).

Method Details 	[hide private]
__init__(self, encoding=None, remove_blank_text=False, remove_comments=False, remove_pis=False, strip_cdata=True, no_network=True, target=None, XMLSchema schema=None, recover=True, compact=True)
(Constructor)
	 
x.__init__(...) initializes x; see help(type(x)) for signature

Overrides: object.__init__ 

Es scheint, dass HTML Parser auch die Codierung angeben kann. Wahrscheinlich ist der Standardwert None, und das Verhalten zu diesem Zeitpunkt ähnelt dem Festlegen der Codierung durch Betrachten des Meta-Tags von HTML. Wenn dieses Meta-Tag nicht vorhanden ist, wird wahrscheinlich die Standardcodierung für Ihre Systemumgebung festgelegt.

Ich werde html_parser imitieren und eine Instanz erstellen.

# -*- coding:utf-8 -*-
from lxml import html

html_parser = html.HTMLParser(encoding="utf-8")

with open("./d.html", "r") as rf:
    doc = html.parse(rf, parser=html_parser).getroot()
    print html.tostring(doc, pretty_print=True, encoding="utf-8")

Es scheint, dass die Ausgabe erfolgreich war.

<html>
<head></head>
<body>
Japanische Zeichenkette
    <p>AIUEO</p>
  </body>
</html>

Chardet, wenn Sie die richtige Kodierung nicht kennen

Wenn Sie die richtige Codierung nicht kennen, können Sie chardet (cchardet) verwenden.

# -*- coding:utf-8 -*-
import chardet #if not installed. pip install chardet

print chardet.detect(u"AIUEO".encode("utf-8"))
# {'confidence': 0.9690625, 'encoding': 'utf-8'}

## warning
print chardet.detect(u"abcdefg".encode("utf-8")[:3])
# {'confidence': 1.0, 'encoding': 'ascii'}

def detect(fname, size = 4096 << 2):
    with open(fname, "rb") as rf:
        return chardet.detect(rf.read(size)).get("encoding")

print detect("b.html") # utf-8
print detect("a.html") # ascii

Das ist es.

Recommended Posts

Schreiben Sie relative Links in HTML in absolute Links mit Python (lxml) um
Konvertieren Sie die absolute URL in eine relative URL in Python
In Python von Markdown in HTML konvertieren
Schreiben Sie Python2-Code in Python3 um (2to3)
Python> Liste> Relative Pfade in absolute Pfade konvertieren> all_filepaths = [Datenbankpfad + fp für fp in train_filepaths]
So löschen Sie stdout in Python
Melden Sie sich auf der Website in Python an
Sprechen mit Python [Text zu Sprache]
Relative URL-Verarbeitung in Python
Post an Slack in Python
Eine Geschichte darüber, wie man einen relativen Pfad in Python angibt.
[Python] Wie man PCA mit Python macht
Zeigen Sie Fotos in Python und HTML an
Konvertieren Sie Markdown in Python in PDF
So sammeln Sie Bilder in Python
Verwendung von SQLite in Python
Geben Sie den Inhalt von ~ .xlsx im Ordner mit Python in HTML aus
Versuchen Sie, Trace in Python zu berechnen
Wie man MySQL mit Python benutzt
[Einführung in die Udemy Python3 + -Anwendung] 69. Import des absoluten Pfads und des relativen Pfads
So verpacken Sie C in Python
Verwendung von ChemSpider in Python
6 Möglichkeiten zum Stringen von Objekten in Python
Verwendung von PubChem mit Python
Umgang mit Japanisch mit Python
Eine Alternative zu "Pause" in Python
Ich habe eine Webanwendung in Python erstellt, die Markdown in HTML konvertiert
[Einführung in Python] Wie verwende ich eine Klasse in Python?
Installieren Sie Pyaudio, um Wellen in Python zu spielen
HTML-Mail mit Bild zum Senden mit Python
Ich habe versucht, Permutation in Python zu implementieren
Methode zum Erstellen einer Python-Umgebung in Xcode 6
Dynamisches Definieren von Variablen in Python
So machen Sie R chartr () in Python
Pin aktuelles Verzeichnis an Skriptverzeichnis in Python
[Itertools.permutations] So löschen Sie eine Sequenz in Python
PUT gzip direkt in S3 in Python
Senden Sie mit Python (Python3) E-Mails an mehrere Empfänger.
Konvertieren Sie die psd-Datei in Python in png
Beispielskript zum Überfüllen von Signalen in Python
Ich habe versucht, PLSA in Python 2 zu implementieren
So setzen Sie die Standardcodierung in Python auf utf-8
So arbeiten Sie mit BigQuery in Python
Melden Sie sich mit Anforderungen in Python bei Slack an
Wie bekomme ich Stacktrace in Python?
So zeigen Sie die neunundneunzig Tabelle in Python an
Einfache Möglichkeit, Wikipedia mit Python zu verwenden
Schreiben Sie den Filterknoten von SPSS Modeler mit Python neu
Ich habe versucht, ADALINE in Python zu implementieren
[Python] Pandas in 10 Minuten vollständig zu verstehen
Werfen Sie Incoming Webhook in Python auf Mattermost
Modul zum Generieren des Wortes N-Gramm in Python
Verweisen auf Umgebungsvariablen in Python in Blender
Ich wollte ABC159 mit Python lösen
So wechseln Sie die Python-Version in Cloud9
So passen Sie den Bildkontrast in Python an
Verwendung von __slots__ in der Python-Klasse
So füllen Sie mit Python dynamisch Nullen aus
Verwalten Sie Python-Pakete, um sie in Containern zu installieren
So betreiben Sie die Zeitstempelstation in Python