[PYTHON] Wie man die schöne Suppeninstanziierung beschleunigt

Dies ist der Befund, als die Ausführungsgeschwindigkeit von Image Search Bot mit BeautifulSoup verbessert wurde. Ich hoffe, es ist hilfreich für diejenigen, die in Schwierigkeiten sind, da die Ausführungsgeschwindigkeit des Scrapings langsam ist.

Wie es geht

Umgebung

Skript

Sie können beschleunigen, indem Sie im Argument von BeautifulSoup einen geeigneten Zeichencode angeben: ** from_encoding **.

from urllib import request
import bs4

page = request.urlopen("https://news.yahoo.co.jp/")
html = page.read()
# from_Ersetzen Sie den Zeichencode der Site, die in die Codierung aufgenommen werden soll(Im Fall von Yahoo News diesmal utf-8)
soup = bs4.BeautifulSoup(html, "html.parser", from_encoding="utf-8")

So überprüfen Sie den Zeichencode

Grundsätzlich wird es nach charset = in das Meta-Tag geschrieben.

<!--Im Fall von Yahoo News-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

Vergleich der Ausführungszeit

Ich habe es mit dem folgenden Skript überprüft. Gemessen vor und nach dem Erstellen einer Instanz

verification_bs4.py


from urllib import request as req
from urllib import parse
import bs4
import time
import copy

url = "https://news.yahoo.co.jp/"
page = req.urlopen(url)
html = page.read()
page.close()

start = time.time()
soup = bs4.BeautifulSoup(html, "html.parser")
print('{:.5f}'.format(time.time() - start) + "[s] html.parser, None")

start = time.time()
soup = bs4.BeautifulSoup(html, "lxml")
print('{:.5f}'.format(time.time() - start) + "[s] lxml, None")

start = time.time()
hoge = copy.copy(soup)
print('{:.5f}'.format(time.time() - start) + "[s] copy(lxml, None)")

start = time.time()
soup = bs4.BeautifulSoup(html, "html.parser", from_encoding="utf-8")
print('{:.5f}'.format(time.time() - start) + "[s] html.parser, utf-8")

start = time.time()
soup = bs4.BeautifulSoup(html, "lxml", from_encoding="utf-8")
print('{:.5f}'.format(time.time() - start) + "[s] lxml, utf-8")

start = time.time()
hoge = copy.copy(soup)
print('{:.5f}'.format(time.time() - start) + "[s] copy(lxml, utf-8)")

start = time.time()
soup = bs4.BeautifulSoup(html, "lxml", from_encoding="utf-16")
#Der Rückgabewert ist leer, da der Zeichencode unterschiedlich ist
print('{:.5f}'.format(time.time() - start) + "[s] lxml, utf-16")

Das Ausgabeergebnis ist hier.

% python verification_bs4.py
2.10937[s] html.parser, None
2.00081[s] lxml, None
0.04704[s] copy(lxml, None)
0.03124[s] html.parser, utf-8
0.03115[s] lxml, utf-8
0.04188[s] copy(lxml, utf-8)
0.01651[s] lxml, utf-16

Zusammenfassung

Durch Angabe des Zeichencodes in ** from_encoding ** konnten wir die Instanziierung beschleunigen. Wenn ich mir den Code ansehe, der besagt, dass BeautifulSoup langsam ist, habe ich ihn nicht from_encoding zugewiesen, daher denke ich, dass dies die Ursache ist.

Für diejenigen, die Zeit haben

Ich habe mich gefragt, warum es solche Spezifikationen hat, also habe ich den Quellcode überprüft. Normalerweise berühre ich Python jedoch nicht so oft, sodass ich möglicherweise etwas schreibe, das nicht berücksichtigt wird. Der Quellcode ist hier

Grund für die Verspätung

Wahrscheinlich aufgrund der in ** bs4 / dammit.py ** definierten Klasse ** EncodingDetector **. Das Folgende ist ein Teilcode-Auszug.

class EncodingDetector:
    """Suggests a number of possible encodings for a bytestring.

    Order of precedence:

    1. Encodings you specifically tell EncodingDetector to try first
    (the override_encodings argument to the constructor).

    2. An encoding declared within the bytestring itself, either in an
    XML declaration (if the bytestring is to be interpreted as an XML
    document), or in a <meta> tag (if the bytestring is to be
    interpreted as an HTML document.)

    3. An encoding detected through textual analysis by chardet,
    cchardet, or a similar external library.

    4. UTF-8.

    5. Windows-1252.
    """
    @property
    def encodings(self):
        """Yield a number of encodings that might work for this markup.

        :yield: A sequence of strings.
        """
        tried = set()
        for e in self.override_encodings:
            if self._usable(e, tried):
                yield e

        # Did the document originally start with a byte-order mark
        # that indicated its encoding?
        if self._usable(self.sniffed_encoding, tried):
            yield self.sniffed_encoding

        # Look within the document for an XML or HTML encoding
        # declaration.
        if self.declared_encoding is None:
            self.declared_encoding = self.find_declared_encoding(
                self.markup, self.is_html)
        if self._usable(self.declared_encoding, tried):
            yield self.declared_encoding

        # Use third-party character set detection to guess at the
        # encoding.
        if self.chardet_encoding is None:
            self.chardet_encoding = chardet_dammit(self.markup)
        if self._usable(self.chardet_encoding, tried):
            yield self.chardet_encoding

        # As a last-ditch effort, try utf-8 and windows-1252.
        for e in ('utf-8', 'windows-1252'):
            if self._usable(e, tried):
                yield e

Wenn Sie den zu Beginn des Kurses geschriebenen Kommentar übersetzen, sieht er folgendermaßen aus (DeepL-Übersetzung)

    """"Wir empfehlen einige mögliche Codierungen für Byte-Strings.

Die Reihenfolge der Priorität ist wie folgt.

    1.Die Codierung, die Sie EncodingDetector angewiesen haben, zuerst zu versuchen
Konstruktorargument überschreiben_Codierungen verwenden).

    2.Die im Bystestring selbst deklarierte Codierung.
XML-Deklaration(Wenn die Byte-Zeichenfolge als XML interpretiert wird)
Dokument), Oder<meta>Im Tag(Byte-Zeichenfolge
Als HTML-Dokument interpretiert)。

    3.Die durch die Textanalyse von Charde erkannte Kodierung.
Verwenden Sie cchardet oder eine ähnliche externe Bibliothek.

    4. 4.UTF-8。

    5. Windows-1252。
    """

Aus den Kommentaren und der Verarbeitung schließe ich, dass es langsam ist, weil es verarbeitet wird, bis die obigen Listen 1 bis 5 in der richtigen Reihenfolge erfolgreich sind. Wenn Sie sich 2 ansehen, wird das Erraten des Zeichencodes aus dem zuvor erwähnten Meta-Tag ebenfalls automatisch durchgeführt. Ich denke, dass dies eine Überlegung ist, damit Sie es verwenden können, ohne den Zeichencode anzugeben, indem Sie sich die Quelle der Website ansehen. Beim Scraping denke ich jedoch, dass ich normalerweise den Quellcode überprüfe, daher denke ich nicht, dass es so spät sein sollte. (Wir haben nicht überprüft, welcher Prozess der Engpass ist. Bitte geben Sie mir jemanden.)

Warum das Kopieren schnell ist

Im früheren Skript zur Messung der Ausführungszeit wird die Methode copy.copy () verwendet, um die Instanz zu duplizieren. Der Grund dafür, dass dies so schnell geschieht, liegt in \ _ \ _ copy \ _ \ _ von bs4 / __ init__.py. Das Folgende ist ein Teilcode-Auszug.

__init__.py


class BeautifulSoup(Tag):

    def __copy__(self):
        """Copy a BeautifulSoup object by converting the document to a string and parsing it again."""
        copy = type(self)(
            self.encode('utf-8'), builder=self.builder, from_encoding='utf-8'
        )

        # Although we encoded the tree to UTF-8, that may not have
        # been the encoding of the original markup. Set the copy's
        # .original_encoding to reflect the original object's
        # .original_encoding.
        copy.original_encoding = self.original_encoding
        return copy

Es ist schneller, weil ich mich hier für utf-8 entschieden habe. Im Gegenteil, wenn der Zeichencode der Scraping-Site nicht utf-8 ist, ist er langsamer. Im folgenden Messskript wird der Zeichencode mit price com von shift-jis gemessen.

verification_bs4_2.py


from urllib import request as req
from urllib import parse
import bs4
import time
import copy

url = "https://kakaku.com/"
page = req.urlopen(url)
html = page.read()
page.close()

start = time.time()
soup = bs4.BeautifulSoup(html, "html.parser")
print('{:.5f}'.format(time.time() - start) + "[s] html.parser, None")

start = time.time()
soup = bs4.BeautifulSoup(html, "lxml")
print('{:.5f}'.format(time.time() - start) + "[s] lxml, None")

start = time.time()
soup = bs4.BeautifulSoup(html, "lxml", from_encoding="shift_jis")
print('{:.5f}'.format(time.time() - start) + "[s] lxml, shift_jis")

start = time.time()
hoge = copy.copy(soup)
print('{:.5f}'.format(time.time() - start) + "[s] copy(lxml, shift_jis)")

Das Ausgabeergebnis ist hier.

% python verification_bs4_2.py
0.11084[s] html.parser, None
0.08563[s] lxml, None
0.08643[s] lxml, shift_jis
0.13631[s] copy(lxml, shift_jis)

Wie oben erwähnt, ist das Kopieren langsamer als utf-8. Im Fall von Shift-Jis hat sich die Ausführungsgeschwindigkeit jedoch kaum geändert, selbst wenn in ** from_encoding ** nichts angegeben ist. ~~ Ich weiß das nicht mehr </ font> ~~

Schließlich

Danke, dass Sie so weit gelesen haben! Am Ende tut es mir leid, dass es chaotisch wurde. Ich frage mich, warum mehr als 90% der Websites der Welt utf-8 sind, aber es ist langsam. Ich habe einen Artikel erstellt, weil ich der Meinung war, dass es ein Problem war, dass die Websites, die mit BeautifulSoup gesucht und ganz oben angekommen sind, dies nicht erwähnt haben. Wenn Sie es nützlich finden, wäre es ermutigend, wenn Sie "LGTM" könnten.

Referenz https://stackoverrun.com/ja/q/12619706

Recommended Posts

Wie man die schöne Suppeninstanziierung beschleunigt
So beschleunigen Sie Python-Berechnungen
Wie man Scicit-Learn wie Conda Numpy beschleunigt
So erhöhen Sie die Verarbeitungsgeschwindigkeit der Erfassung der Scheitelpunktposition
Numba als Python zu beschleunigen
Zusammenfassung der Verwendung von pandas.DataFrame.loc
Zusammenfassung der Verwendung von pyenv-virtualenv
Project Euler 4 Versuch zu beschleunigen
Zusammenfassung der Verwendung von csvkit
[DRF] Snippet zur Beschleunigung von PrimaryKeyRelatedField
[Python] Wie man den Bruchteil einer natürlichen Zahl mit hoher Geschwindigkeit erhält
Unverzichtbar, wenn Sie Python verwenden! Wie man Numpy benutzt, um Berechnungen zu beschleunigen!
So richten Sie die Entwicklungsumgebung von ev3dev ein [Windows-Version]
[Python] Zusammenfassung der Verwendung von Pandas
[Memo] Verwendung von BeautifulSoup4 (1) HTML anzeigen
Wie man lange Einschlüsse loswird
So überprüfen Sie die Version von Django
So richten Sie SVM mit Optuna ein
So installieren Sie CatBoost [Stand Januar 2020]
Berechnen Verwenden Sie% des Befehls df
[Python2.7] Zusammenfassung der Verwendung von unittest
Zusammenfassung der Verfahren bis zur PyPI-Registrierung
Jupyter Notebook Grundlagen der Verwendung
Grundlagen von PyTorch (1) - Verwendung von Tensor-
Zusammenfassung der Verwendung der Python-Liste
[Python2.7] Zusammenfassung der Verwendung des Unterprozesses
Zusammenfassung des Schreibens von AWS Lambda
[Frage] Wie verwende ich plot_surface von Python?
Zusammenfassung zum Festlegen der Hauptfussel (pep8, pylint, flake8) von Python
So berechnen Sie die Volatilität einer Marke
Verwendung von Folium (Visualisierung von Standortinformationen)
So finden Sie den Bereich des Boronoi-Diagramms
[Python] Verwendung von zwei Arten von type ()
So verfolgen Sie die Arbeit mit Powershell
Zusammenfassung zum Importieren von Dateien in Python 3
So richten Sie eine zufällige Gesamtstruktur mit Optuna ein
Nicht viel erwähnt, wie man Pickle benutzt
Zusammenfassung der Verwendung von MNIST mit Python
So kratzen Sie Pferderenndaten mit Beautiful Soup
So legen Sie Attribute mit Mock of Python fest
So implementieren Sie "named_scope" von RubyOnRails mit Django
So messen Sie die Leitungsgeschwindigkeit vom Terminal aus
So richten Sie eine zufällige Gesamtstruktur mit Optuna ein
Organisieren Sie Python-Tools, um die anfängliche Bewegung von Datenanalyse-Wettbewerben zu beschleunigen
So erhalten Sie Elemente vom Typ Wörterbuch von Python 2.7
So richten Sie einen lokalen Entwicklungsserver ein
Geschwindigkeit: Element am Ende des Python-Arrays hinzufügen
[Python] Geben Sie Ihr Bestes, um SQL Alchemy zu beschleunigen
[Java] So wechseln Sie zwischen mehreren Java-Versionen
So beschleunigen Sie die Anwendungsmethode von Pandas mit nur einem Satz (mit Verifizierungsberechnung)
So zeigen Sie das Änderungsdatum einer Datei in C-Sprache bis zu Nanosekunden an
Wie man die Portnummer des xinetd-Dienstes kennt
Versuch und Irrtum, um Android-Screenshots zu beschleunigen
So ermitteln Sie die Anzahl der Stellen in Python
Hinweise zur Verwendung von AIST Spacon ABCI
Ich habe versucht zusammenzufassen, wie man Matplotlib von Python verwendet
So richten Sie eine Python-Umgebung mit pyenv ein
Hinweise zur Verwendung beim Kombinieren von pandas.DataFrame
Die Entscheidung von scikit-learn Wie man ein Holzmodell visualisiert
[Blender] Zusammenfassung der Installation / Aktualisierung / Deinstallation von Add-Ons